#!/usr/bin/ruby

#
## https://rosettacode.org/wiki/Elliptic_curve_arithmetic
#

module EC {

    var A = 0
    var B = 7

    class Horizon {
        method to_s {
            "EC Point at horizon"
        }

        method *(_) {
            self
        }

        method -(_) {
            self
        }
    }

    class Point(Number x, Number y) {
        method to_s {
            "EC Point at x=#{x}, y=#{y}"
        }

        method neg {
            Point(x, -y)
        }

        method -(Point p) {
            self + -p
        }

        method +(Point p) {

            if (x == p.x) {
                return (y == p.y ? self*2 : Horizon())
            }
            else {
                var slope = (p.y - y)/(p.x - x)
                var x2 = (slope**2 - x - p.x)
                var y2 = (slope * (x - x2) - y)
                Point(x2, y2)
            }
        }

        method +(Horizon _) {
            self
        }

        method *((0)) {
            Horizon()
        }

        method *((1)) {
            self
        }

        method *((2)) {
            var l = (3 * x**2 + A)/(2 * y)
            var x2 = (l**2 - 2*x)
            var y2 = (l * (x - x2) - y)
            Point(x2, y2)
        }

        method *(Number n) {
            2*(self * (n>>1)) + self*(n % 2)
        }
    }

    class Horizon {
        method +(Point p) {
            p
        }
    }

    class Number {
        method +(Point p) {
            p + self
        }
        method *(Point p) {
            p * self
        }
        method *(Horizon h) {
            h
        }
        method -(Point p) {
            -p + self
        }
    }
}

say var p = with(1) {|v| EC::Point(v, sqrt(abs(1 - v**3 - EC::A*v - EC::B))) }
say var q = with(2) {|v| EC::Point(v, sqrt(abs(1 - v**3 - EC::A*v - EC::B))) }
say var s = (p + q)

var bool = (abs((p.x - q.x)*(-s.y - q.y) - (p.y - q.y)*(s.x - q.x)) < 1e-20)

say "checking alignment: #{bool}"

assert_eq(bool, true)
