⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

{{collection|Rational Arithmetic}} ;The core of the Rational class

// the constructor
function Rational(numerator, denominator) {
    if (denominator === undefined)
        denominator = 1;
    else if (denominator == 0)
        throw "divide by zero";

    this.numer = numerator;
    if (this.numer == 0)
        this.denom = 1;
    else
        this.denom = denominator;

    this.normalize();
}

// getter methods
Rational.prototype.numerator   = function() {return this.numer};
Rational.prototype.denominator = function() {return this.denom};

// clone a rational
Rational.prototype.dup = function() {
    return new Rational(this.numerator(), this.denominator()); 
};

// conversion methods
Rational.prototype.toString = function() {
    if (this.denominator() == 1) {
        return this.numerator().toString();
    } else {
        // implicit conversion of numbers to strings
        return this.numerator() + '/' + this.denominator()
    }
};
Rational.prototype.toFloat  = function() {return eval(this.toString())}
Rational.prototype.toInt    = function() {return Math.floor(this.toFloat())};

// reduce 
Rational.prototype.normalize = function() {
    // greatest common divisor
    var a=Math.abs(this.numerator()), b=Math.abs(this.denominator())
    while (b != 0) {
        var tmp = a;
        a = b;
        b = tmp % b;
    }
    // a is the gcd

    this.numer /= a;
    this.denom /= a;
    if (this.denom < 0) {
        this.numer *= -1;
        this.denom *= -1;
    }
    return this;
}

// absolute value
// returns a new rational
Rational.prototype.abs = function() {
    return new Rational(Math.abs(this.numerator()), this.denominator());
};

// inverse
// returns a new rational
Rational.prototype.inv = function() {
    return new Rational(this.denominator(), this.numerator());
};

//
// arithmetic methods

// variadic, modifies receiver
Rational.prototype.add = function() {
    for (var i = 0; i < arguments.length; i++) {
        this.numer = this.numer * arguments[i].denominator() + this.denom * arguments[i].numerator();
        this.denom = this.denom * arguments[i].denominator();
    }
    return this.normalize();
};

// variadic, modifies receiver
Rational.prototype.subtract = function() {
    for (var i = 0; i < arguments.length; i++) {
        this.numer = this.numer * arguments[i].denominator() - this.denom * arguments[i].numerator();
        this.denom = this.denom * arguments[i].denominator();
    }
    return this.normalize();
};

// unary "-" operator
// returns a new rational
Rational.prototype.neg = function() {
    return (new Rational(0)).subtract(this);
};

// variadic, modifies receiver
Rational.prototype.multiply = function() {
    for (var i = 0; i < arguments.length; i++) {
        this.numer *= arguments[i].numerator();
        this.denom *= arguments[i].denominator();
    }
    return this.normalize();
};

// modifies receiver
Rational.prototype.divide = function(rat) {
    return this.multiply(rat.inv());
}


// increment
// modifies receiver
Rational.prototype.inc = function() {
    this.numer += this.denominator();
    return this.normalize();
}

// decrement
// modifies receiver
Rational.prototype.dec = function() {
    this.numer -= this.denominator();
    return this.normalize();
}

//
// comparison methods

Rational.prototype.isZero = function() {
    return (this.numerator() == 0);
}
Rational.prototype.isPositive = function() {
    return (this.numerator() > 0);
}
Rational.prototype.isNegative = function() {
    return (this.numerator() < 0);
}

Rational.prototype.eq = function(rat) {
    return this.dup().subtract(rat).isZero();
}
Rational.prototype.ne = function(rat) {
    return !(this.eq(rat));
}
Rational.prototype.lt = function(rat) {
    return this.dup().subtract(rat).isNegative();
}
Rational.prototype.gt = function(rat) {
    return this.dup().subtract(rat).isPositive();
}
Rational.prototype.le = function(rat) {
    return !(this.gt(rat));
}
Rational.prototype.ge = function(rat) {
    return !(this.lt(rat));
}

;Testing

function assert(cond, msg) { if (!cond) throw msg; }

print('testing')
var a, b, c, d, e, f;

//test creation
a = new Rational(0); assert(a.toString() == "0", "Rational(0).toString() == '0'")
a = new Rational(2); assert(a.toString() == "2", "Rational(2).toString() == '2'")
a = new Rational(1,2); assert(a.toString() == "1/2", "Rational(1,2).toString() == '1/2'")
b = new Rational(2,-12); assert(b.toString() == "-1/6", "Rational(1,6).toString() == '1/6'")
f = new Rational(0,9)

a = new Rational(1,3)
b = new Rational(1,2)
c = new Rational(1,3)

assert(!(a.eq(b)), "1/3 == 1/2")
assert(a.eq(c), "1/3 == 1/3")
assert(a.ne(b), "1/3 != 1/2")
assert(!(a.ne(c)), "1/3 != 1/3")
assert(a.lt(b), "1/3 < 1/2")
assert(!(b.lt(a)), "1/2 < 1/3")
assert(!(a.lt(c)), "1/3 < 1/3")
assert(!(a.gt(b)), "1/3 > 1/2")
assert(b.gt(a), "1/2 > 1/3")
assert(!(a.gt(c)), "1/3 > 1/3")

assert(a.le(b), "1/3 <= 1/2")
assert(!(b.le(a)), "1/2 <= 1/3")
assert(a.le(c), "1/3 <= 1/3")
assert(!(a.ge(b)), "1/3 >= 1/2")
assert(b.ge(a), "1/2 >= 1/3")
assert(a.ge(c), "1/3 >= 1/3")

a = new Rational(1,2)
b = new Rational(1,6)
a.add(b); assert(a.eq(new Rational(2,3)), "1/2 + 1/6 == 2/3")
c = a.neg(); assert(a.eq(new Rational(2,3)), "neg(1/2) == -1/2")
             assert(c.eq(new Rational(2,-3)), "neg(1/2) == -1/2")
d = c.abs(); assert(c.eq(new Rational(-2,3)), "abs(neg(1/2)) == 1/2")
             assert(d.eq(new Rational(2,3)), "abs(neg(1/2)) == 1/2")
b.subtract(a); assert(b.eq(new Rational(-1,2)), "1/6 - 1/2 == -1/3")

c = a.neg().abs(); assert(c.eq(a), "abs(neg(1/2)) == 1/2")
c = (new Rational(-1,3)).inv(); assert(c.toString() == '-3', "inv(1/6 - 1/2) == -3")
try {
    e = f.inv();
    throw "should have been an error: " +f + '.inv() = ' + e
} catch (e) {
    assert(e == "divide by zero", "0.inv() === error")
}

b = new Rational(1,6)
b.add(new Rational(2,3), new Rational(4,2)); assert(b.toString() == "17/6", "1/6+2/3+4/2 == 17/6");

a = new Rational(1,3);
b = new Rational(1,6)
c = new Rational(5,6);
d = new Rational(1/5);
e = new Rational(2);
f = new Rational(0,9);


assert(c.dup().multiply(d).eq(b), "5/6 * 1/5 = 1/6")
assert(c.dup().multiply(d,e).eq(a), "5/6 * 1/5 *2 = 1/3")
assert(c.dup().multiply(d,e,f).eq(f), "5/6 * 1/5 *2*0 = 0")

c.divide(new Rational(5));
assert(c.eq(b), "5/6 / 5 = 1/6b")

try {
    e = c.divide(f)
    throw "should have been an error: " + c + "/" + f + '= ' + e
} catch (e) {
    assert(e == "divide by zero", "0.inv() === error")
}


print('all tests passed');

;Finding perfect numbers

function factors(num) {
    var factors = new Array();
    var sqrt = Math.floor(Math.sqrt(num)); 
    for (var i = 1; i <= sqrt; i++) {
        if (num % i == 0) {
            factors.push(i);
            if (num / i != i) 
                factors.push(num / i);
        }
    }
    factors.sort(function(a,b){return a-b});  // numeric sort
    return factors;
}

function isPerfect(n) {
    var sum = new Rational(0);
    var fctrs = factors(n);
    for (var i = 0; i < fctrs.length; i++) 
        sum.add(new Rational(1, fctrs[i]));

    // note, fctrs includes 1, so sum should be 2
    return sum.toFloat() == 2.0;
}

// find perfect numbers less than 2^19
for (var n = 2; n < Math.pow(2,19); n++)
    if (isPerfect(n))
        print("perfect: " + n);

// test 5th perfect number
var n = Math.pow(2,12) * (Math.pow(2,13) - 1);
if (isPerfect(n))
    print("perfect: " + n);

{{out}}

perfect: 6
perfect: 28
perfect: 496
perfect: 8128
perfect: 33550336