⚠️ 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

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();
};

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);
};

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();
};

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

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

// decrement
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++)

// 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
```