Arithmetic/Rational/JavaScript

From Rosetta Code
Arithmetic/Rational/JavaScript is part of Rational Arithmetic. You may find other members of Rational Arithmetic at Category:Rational Arithmetic.
The core of the Rational class

<lang javascript>// 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));

}</lang>

Testing

<lang javascript>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');</lang>

Finding perfect numbers

<lang javascript>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);</lang>
Output:
perfect: 6
perfect: 28
perfect: 496
perfect: 8128
perfect: 33550336