Fraction class that represents a rational number @author zz85 @author incompleteopus (modifications)

Vex.Flow.Fraction = (function() {
  function Fraction(numerator, denominator) {
    this.set(numerator, denominator);
  }

  /**
   * GCD: Find greatest common divisor using Euclidean algorithm
   */
  Fraction.GCD = function(a, b) {
    if (typeof a !== "number" || typeof b !== "number") {
      throw new Vex.RERR("BadArgument", "Invalid numbers: " + a + ", " + b);
    }

    var t;

    while (b !== 0) {
      t = b;
      b = a % b;
      a = t;
    }

    return a;
  };

  /**
   * LCM: Lowest common multiple
   */
  Fraction.LCM = function(a, b) {
    return ((a * b) / Fraction.GCD(a, b));
  };

  /**
   * LCMM: Lowest common multiple for more than two numbers
   */
  Fraction.LCMM = function(args) {
    if (args.length === 0) {
      return 0;
    } else if (args.length == 1) {
      return args[0];
    } else if (args.length == 2) {
      return Vex.Flow.Fraction.LCM(args[0], args[1]);
    } else {
      var arg0 = args[0];
      args.shift();
      return Fraction.LCM(arg0, Vex.Flow.Fraction.LCMM(args));
    }
  };

  Fraction.prototype = {
    set: function(numerator, denominator) {
      this.numerator = numerator === undefined ? 1 : numerator;
      this.denominator = denominator === undefined ? 1 : denominator;
      return this;
    },

    value: function() {
      return this.numerator / this.denominator;
    },

    simplify: function() {
      var u = this.numerator;
      var d = this.denominator;

      var gcd = Vex.Flow.Fraction.GCD(u, d);
      u /= gcd;
      d /= gcd;

      if (d < 0) {
        d = -d;
        u = -u;
      }
      return this.set(u, d);
    },

    add: function(param1, param2) {
      var otherNumerator;
      var otherDenominator;

      if (param1 instanceof Vex.Flow.Fraction) {
        otherNumerator = param1.numerator;
        otherDenominator = param1.denominator;
      } else {
        if (param1 !== undefined) {
          otherNumerator = param1;
        } else {
          otherNumerator = 0;
        }

        if (param2 !== undefined) {
          otherDenominator = param2;
        } else {
          otherDenominator = 1;
        }
      }

      var lcm = Vex.Flow.Fraction.LCM(this.denominator, otherDenominator);
      var a = lcm / this.denominator;
      var b = lcm / otherDenominator;

      var u = this.numerator * a + otherNumerator * b;
      return this.set(u, lcm);
    },

    subtract: function(param1, param2) {
      var otherNumerator;
      var otherDenominator;

      if (param1 instanceof Vex.Flow.Fraction) {
        otherNumerator = param1.numerator;
        otherDenominator = param1.denominator;
      } else {
        if (param1 !== undefined) {
          otherNumerator = param1;
        } else {
          otherNumerator = 0;
        }

        if (param2 !== undefined) {
          otherDenominator = param2;
        } else {
          otherDenominator = 1;
        }
      }

      var lcm = Vex.Flow.Fraction.LCM(this.denominator, otherDenominator);
      var a = lcm / this.denominator;
      var b = lcm / otherDenominator;

      var u = this.numerator * a - otherNumerator * b;
      return this.set(u, lcm);
    },

    multiply: function(param1, param2) {
      var otherNumerator;
      var otherDenominator;

      if (param1 instanceof Vex.Flow.Fraction) {
        otherNumerator = param1.numerator;
        otherDenominator = param1.denominator;
      } else {
        if (param1 !== undefined) {
          otherNumerator = param1;
        } else {
          otherNumerator = 1;
        }

        if (param2 !== undefined) {
          otherDenominator = param2;
        } else {
          otherDenominator = 1;
        }
      }

      return this.set(this.numerator * otherNumerator, this.denominator * otherDenominator);
    },

    divide: function(param1, param2) {
      var otherNumerator;
      var otherDenominator;

      if (param1 instanceof Vex.Flow.Fraction) {
        otherNumerator = param1.numerator;
        otherDenominator = param1.denominator;
      } else {
        if (param1 !== undefined) {
          otherNumerator = param1;
        } else {
          otherNumerator = 1;
        }

        if (param2 !== undefined) {
          otherDenominator = param2;
        } else {
          otherDenominator = 1;
        }
      }

      return this.set(this.numerator * otherDenominator, this.denominator * otherNumerator);
    },

Simplifies both sides and checks if they are equal.

    equals: function(compare) {
      var a = Vex.Flow.Fraction.__compareA.copy(compare).simplify();
      var b = Vex.Flow.Fraction.__compareB.copy(this).simplify();

      return (a.numerator === b.numerator) && (a.denominator === b.denominator);
    },

Greater than operator.

    greaterThan: function(compare) {
      var a = Vex.Flow.Fraction.__compareB.copy(this);
      a.subtract(compare);
      return (a.numerator > 0);
    },

Greater than or equals operator.

    greaterThanEquals: function(compare) {
      var a = Vex.Flow.Fraction.__compareB.copy(this);
      a.subtract(compare);
      return (a.numerator >= 0);
    },

Less than operator.

    lessThan: function(compare) {
      return !(this.greaterThanEquals(compare));  
    },

Less than or equals operator.

    lessThanEquals: function(compare) {
      return !(this.greaterThan(compare));  
    },

Creates a new copy with this current values.

    clone: function() {
      return new Vex.Flow.Fraction(this.numerator, this.denominator);
    },

Copies value of another Fraction into itself.

    copy: function(copy) {
      return this.set(copy.numerator, copy.denominator);
    },

Returns the integer component eg. (4/2) == 2

    quotient: function() {
      return Math.floor(this.numerator / this.denominator);
    },

Returns the fraction component when reduced to a mixed number

    fraction: function() {
      return this.numerator % this.denominator;
    },

Returns the absolute value

    abs: function() {
      this.denominator = Math.abs(this.denominator);
      this.numerator = Math.abs(this.numerator);
      return this;
    },

Returns a raw string representation

    toString: function() {
      return this.numerator + '/' + this.denominator;
    },

Returns a simplified string respresentation

    toSimplifiedString: function() {
      return Vex.Flow.Fraction.__tmp.copy(this).simplify().toString();
    },

Returns string representation in mixed form

    toMixedString: function() {
      var s = '';
      var q = this.quotient();
      var f = Vex.Flow.Fraction.__tmp.copy(this);

      if (q < 0) {
        f.abs().fraction();
      } else {
        f.fraction();
      }

      if (q !== 0) {
        s += q;

        if (f.numerator !== 0) {
          s += ' ' + f.toSimplifiedString();
        }
      } else {
        if (f.numerator === 0) {
          s = '0';
        } else {
          s = f.toSimplifiedString();
        }
      }

      return s;
    },

Parses a fraction string

    parse: function(str) {
      var i = str.split('/');
      var n = parseInt(i[0], 10);
      var d = (i[1]) ? parseInt(i[1], 10) : 1;

      return this.set(n, d);
    }
  };

Temporary cached objects

  Fraction.__compareA = new Fraction();
  Fraction.__compareB = new Fraction();
  Fraction.__tmp = new Fraction();

  return Fraction;
}());
h