#!/usr/bin/ruby

#
## A very basic Rational() class, implementing a few symbolic relations.
#

class Rational(num, den) {

    method +(Number o) {
        self + Rational(o, 1)
    }

    method +(Rational o) {
        Rational(
            num*o.den + o.num*den,
            den*o.den
        )
    }

    method -(Number o) {
        self + -o
    }

    method -(Rational o) {
        self + -o
    }

    method *(Number o) {
        Rational(num*o, den)
    }

    method *(Rational o) {
        Rational(num*o.num, den*o.den)
    }

    method /(Number o) {
        Rational(
            num,
            den * o
        )
    }

    method /(Rational o) {
        Rational(
            num * o.den,
            den * o.num,
        )
    }

    method **(Number o) {
        if (o < 0) {
            var a = o.abs
            Rational(den**a, num**a)
        }
        else {
            Rational(num**o, den**o)
        }
    }

    method neg {
        Rational(-num, den)
    }

    method to_s {
        "Rational(#{num}, #{den})"
    }
}

class Number {
    method +(Rational o) {
        o + self
    }

    method -(Rational o) {
        -o + self
    }

    method *(Rational o) {
        o * self
    }

    method /(Rational o) {
        o**(-1) * self
    }
}

var r = 42+Rational(3,4)

assert_eq(r.num, 171)
assert_eq(r.den, 4)
assert_eq(42 + 3/4, r.num/r.den)

r = 42*Rational(3, 4)
assert_eq(r.num, 42*3)
assert_eq(r.den, 4)

r = 1/Rational(3,4)
assert_eq(r.num, 4)
assert_eq(r.den, 3)

r = 12-Rational(3, 4)
assert_eq(r.num, 45)
assert_eq(r.den, 4)

#
## sum(f(n)) = e, as n->oo.
#
func f((0)) { Rational(1, 1) }
func f(n)   { f(n-1) / n     }

assert_eq(f(10).den, 10!)

func num(n) {      (-1)**n }
func den(n) { (2*n + 1)**2 }

#
## sum(num(n)/den(n)) = Catalan's constant, as n->oo.
#
var sum = Rational(0, 1)

for i in (0 .. 5) {
    sum += Rational(num(i), den(i))
    say sum
}

assert_eq(sum.num, 98607816)
assert_eq(sum.den, 108056025)
