Create Rational number class - Node.js Object

Node.js examples for Object:Class Definition

Description

Create Rational number class

Demo Code


// This file is a part of PowerBlock.
// Copyright (C) 2014 Matthew Blount

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

(function(exports) {
  exports.create = function(deps) {
    var Value = (function() {
      function Value() {}
      Value.prototype.canCopy = function() { return true; };
      Value.prototype.canDrop = function() { return true; };
      return Value;
    })();/* w w  w . j a  v  a2 s .  co m*/
    
    var Unit = (function() {
      function Unit() {}
      Unit.prototype = new Value();
      Unit.instance  = new Unit();
      Unit.prototype.equals = function(u) { return u instanceof Unit; };
      Unit.prototype.quote  = function()  { return "vvrwlk";          };
      Unit.prototype.show   = function()  { return "unit";            };
      return Unit;
    })();

    function gcd(a, b) {
      if (a === 0) { return b; }
      if (b === 0) { return a; } // ?
      if (a < 0) a = -a;
      if (b < 0) b = -b;
      if (b > a) { var temp = a; a = b; b = temp; }
      for (var i = 0; i < 1000; i++) {
        a %= b;
        if (a === 0) return b;
        b %= a;
        if (b === 0) return a;
      }
      throw "GCD timed out: " + a + ", " + b;
    }

    var Rational = (function() {
      function Rational(num, den) {
        var g = gcd(num, den);
        if (den < 0) {
          this.numerator   = ~~((0 - num) / g);
          this.denominator = ~~((0 - den) / g);
        } else {
          this.numerator   = ~~(num / g);
          this.denominator = ~~(den / g);
        }
      }
      Rational.prototype = new Value();
      Rational.prototype.add = function(s) {
        var num = this.numerator * s.denominator + s.numerator * this.denominator;
        var den = this.denominator * s.denominator;
        return new Rational(num, den);
      };
      Rational.prototype.multiply = function(s) {
        var num = this.numerator * s.numerator;
        var den = this.denominator * s.denominator;
        return new Rational(num, den);
      };
      Rational.prototype.negate = function() {
        return new Rational(0 - this.numerator, this.denominator);
      };
      Rational.prototype.reciprocal = function() {
        return new Rational(this.denominator, this.numerator);
      };
      Rational.prototype.isNatural = function() {
        return this.denominator === 1 && this.numerator >= 0;
      };
      Rational.prototype.greaterThan = function(s) {
        return this.numerator * s.denominator > s.numerator * this.denominator;
      };
      Rational.prototype.equals = function(s) {
        return (s instanceof Rational &&
                this.numerator * s.denominator ===
                s.numerator    * this.denominator);
      };
      Rational.prototype.quote = function() {
        var negate = false;
        var num = this.numerator;
        var den = this.denominator;
        var code = "%";
        if (this.numerator < 0) {
          negate = true;
          num = (0 - num);
        }
        if (den === 1) {
          code = code + num;
        } else if (num === 1) {
          code = code + den + "/";
        } else {
          code = code + num + "%" + den + "/*";
        }
        if (negate) {
          code = code + "-";
        }
        return code;
      };
      Rational.prototype.show = function() {
        return "" + this.numerator + "/" + this.denominator;
      };
      Rational.zero  = new Rational(0, 1);
      Rational.one   = new Rational(1, 1);
      Rational.two   = new Rational(2, 1);
      Rational.three = new Rational(3, 1);
      Rational.four  = new Rational(4, 1);
      Rational.five  = new Rational(5, 1);
      Rational.six   = new Rational(6, 1);
      Rational.seven = new Rational(7, 1);
      Rational.eight = new Rational(8, 1);
      Rational.nine  = new Rational(9, 1);
      Rational.ten   = new Rational(10, 1);
      Rational.fromDigit = function(digit) {
        switch (digit) {
        case "0":
          return Rational.zero;
          break;
        case "1":
          return Rational.one;
          break;
        case "2":
          return Rational.two;
          break;
        case "3":
          return Rational.three;
          break;
        case "4":
          return Rational.four;
          break;
        case "5":
          return Rational.five;
          break;
        case "6":
          return Rational.six;
          break;
        case "7":
          return Rational.seven;
          break;
        case "8":
          return Rational.eight;
          break;
        case "9":
          return Rational.nine;
          break;
        default:
          throw "Rational.fromDigit: unknown digit: " + digit;
          break;
        }
      };
      return Rational;
    })();

    var Left = (function() {
      function Left(value) {
        this.value = value;
        this.copy  = value.canCopy();
        this.drop  = value.canDrop();
      }
      Left.prototype = new Value();
      Left.prototype.canCopy = function() { return this.copy; };
      Left.prototype.canDrop = function() { return this.drop; };
      Left.prototype.equals = function(v) {
        return v instanceof Left && this.value.equals(v.value);
      };
      Left.prototype.quote = function() {
        return this.value.quote() + "V";
      };
      Left.prototype.show = function() {
        return "inl(" + this.value.show() + ")";
      };
      return Left;
    })();

    var Right = (function() {
      function Right(value) {
        this.value = value;
        this.copy  = value.canCopy();
        this.drop  = value.canDrop();
      }
      Right.prototype = new Value();
      Right.prototype.canCopy = function() { return this.copy; };
      Right.prototype.canDrop = function() { return this.drop; };
      Right.prototype.equals = function(v) {
        return v instanceof Right && this.value.equals(v.value);
      };
      Right.prototype.quote = function() {
        return this.value.quote() + "VVRWLK";
      };
      Right.prototype.show = function() {
        return "inr(" + this.value.show() + ")";
      };
      return Right;
    })();

    var Product = (function() {
      function Product(first, second) {
        this.first  = first;
        this.second = second;
        this.copy   = first.canCopy() && second.canCopy();
        this.drop   = first.canCopy() && second.canCopy();
      }
      Product.prototype = new Value();
      Product.prototype.canCopy = function() { return this.copy; };
      Product.prototype.canDrop = function() { return this.drop; };
      Product.prototype.equals = function(v) {
        return (v instanceof Product       &&
                this.first.equals(v.first) &&
                this.second.equals(v.second));
      };
      Product.prototype.quote = function() {
        return this.first.quote() + this.second.quote() + "wl";
      };
      Product.prototype.show = function() {
        return "(" + this.first.show() + ", " + this.second.show() + ")";
      };
      return Product;
    })();

    var Block = (function() {
      // We use affine/relevant here so that
      // the default is that you CAN copy and drop
      // e.g. `!undefined` is true
      function Block(code, affine, relevant) {
        this.code = code;
        this.copy = !affine;
        this.drop = !relevant;
      }
      Block.prototype = new Value();
      Block.prototype.canCopy = function() { return this.copy; };
      Block.prototype.canDrop = function() { return this.drop; };
      Block.prototype.noCopy = function() {
        return new Block(this.code, true, !this.drop);
      };
      Block.prototype.noDrop = function() {
        return new Block(this.code, !this.copy, true);
      };
      Block.prototype.equals = function(v) {
        return (v instanceof Block   &&
                this.code === v.code &&
                this.copy === v.copy &&
                this.drop === v.drop);

      };
      Block.prototype.quote = function() {
        var quote = "[" + this.code + "]";
        if (!this.copy) { quote += "C"; }
        if (!this.drop) { quote += "D"; }
        return quote;
      };
      Block.prototype.show = function() { return this.quote(); };
      return Block;
    })();

    var Seal = (function() {
      // We use affine/relevant here so that
      // the default is that you CAN copy and drop
      // e.g. `!undefined` is true
      function Seal(label, value, affine, relevant) {
        this.label = label;
        this.value = value;
        this.copy  = value.canCopy() && !affine;
        this.drop  = value.canDrop() && !relevant;
      }
      Seal.prototype = new Value();
      Seal.prototype.canCopy = function() { return this.copy; };
      Seal.prototype.canDrop = function() { return this.drop; };
      Seal.prototype.noCopy = function() {
        return new Seal(this.label, this.value, true, !this.drop);
      };
      Seal.prototype.noDrop = function() {
        return new Seal(this.label, this.value, !this.copy, true);
      };
      Seal.prototype.equals = function(u) {
        return (u instanceof Seal          &&
                this.label === u.label     &&
                this.value.equals(u.value) &&
                this.copy === u.copy       &&
                this.drop === u.drop);
      };
      Seal.prototype.quote = function() {
        var string = this.value.quote() + "{:" + this.label + "}";
        if (!this.copy) { string += "C"; }
        if (!this.drop) { string += "D"; }
        return string;
      };
      Seal.prototype.show = function() {
        var string = "{:" + this.label + " " + this.value.show() + "}";
        if (!this.copy) { string += "C"; }
        if (!this.drop) { string += "D"; }
        return string;
      };
      return Seal;
    })();

    String.prototype.canCopy = function() { return true; };
    String.prototype.canDrop = function() { return true; };
    String.prototype.equals = function(s) {
      return (typeof(s) === "string" && this.valueOf() === s);
    };
    String.prototype.show  = function() { return '"' + this.valueOf() + "~"; };
    String.prototype.quote = function() { return this.show();                };

    var unit = Unit.instance;

    function rational(num, den)           { return new Rational(num, den);           }
    function natural(num)                 { return new Rational(num, 1);             }
    function left(value)                  { return new Left(value);                  }
    function right(value)                 { return new Right(value);                 }
    function product(fst, snd)            { return new Product(fst, snd);            }
    function block(code, aff, rel)        { return new Block(code, aff, rel);        }
    function seal(label, value, aff, rel) { return new Seal(label, value, aff, rel); }

    function isUnit(x)     { return x instanceof Unit;              }
    function isRational(x) { return x instanceof Rational;          }
    function isNatural(x)  { return isRational(x) && x.isNatural(); }
    function isString(x)   { return typeof(x) === "string";         }
    function isLeft(x)     { return x instanceof Left;              }
    function isRight(x)    { return x instanceof Right;             }
    function isSum(x)      { return isLeft(x) || isRight(x);        }
    function isProduct(x)  { return x instanceof Product;           }
    function isBlock(x)    { return x instanceof Block;             }
    function isSeal(x)     { return x instanceof Seal;              }
    function isResource(x) { return isBlock(x) || isSeal(x);        }
    function isResource(x) { return isBlock(x) || isSeal(x);        }
    function canCopy(x)    { return x.canCopy();                    }
    function canDrop(x)    { return x.canDrop();                    }

    function noCopy(x) { return x.noCopy(); }
    function noDrop(x) { return x.noDrop(); }
    
    function add(x, y)         { return x.add(y);         }
    function multiply(x, y)    { return x.multiply(y);    }
    function negate(x)         { return x.negate();       }
    function reciprocal(x)     { return x.reciprocal();   }
    function greaterThan(x, y) { return x.greaterThan(y); }
    
    function equals(x, y) { return x.equals(y); }
    function quote(x)     { return x.quote();   }
    function show(x)      { return x.show();    }

    function list() {
      var xs = left(unit);
      for (var i = arguments.length - 1; i >= 0; i--) {
        xs = right(product(arguments[i], xs));
      }
      return xs;
    }

    return {
      unit       : unit,
      natural    : natural,
      rational   : rational,
      left       : left,
      right      : right,
      product    : product,
      block      : block,
      seal       : seal,
      isUnit     : isUnit,
      isRational : isRational,
      isNatural  : isNatural,
      isString   : isString,
      isLeft     : isLeft,
      isRight    : isRight,
      isSum      : isSum,
      isProduct  : isProduct,
      isBlock    : isBlock,
      isSeal     : isSeal,
      isResource : isResource,
      canCopy    : canCopy,
      canDrop    : canDrop,
      noCopy     : noCopy,
      noDrop     : noDrop,
      add        : add,
      multiply   : multiply,
      negate     : negate,
      reciprocal : reciprocal,
      greaterThan: greaterThan,
      digit      : Rational.fromDigit,
      zero       : Rational.zero,
      one        : Rational.one,
      two        : Rational.two,
      three      : Rational.three,
      four       : Rational.four,
      five       : Rational.five,
      six        : Rational.six,
      seven      : Rational.seven,
      eight      : Rational.eight,
      nine       : Rational.nine,
      ten        : Rational.ten,
      list       : list,
      equals     : equals,
      quote      : quote,
      show       : show
    };
  };
})(typeof(exports) === "undefined"? window.value = {} : exports);

Related Tutorials