Skip to content

Instantly share code, notes, and snippets.

@zz85
Created April 12, 2012 18:55
Show Gist options
  • Save zz85/2370065 to your computer and use it in GitHub Desktop.
Save zz85/2370065 to your computer and use it in GitHub Desktop.
Fractional / Rational Number Class for performing fractional calculations
// fraction / rational number class
// @author zz85
Vex.Flow.Fraction = function(numerator, denominator) {
this.set(numerator, denominator);
};
Vex.Flow.Fraction.prototype.constructor = Vex.Flow.Fraction;
Vex.Flow.Fraction.prototype.set = function(numerator, denominator) {
this.numerator = numerator === undefined ? 1 : numerator;
this.denominator = denominator === undefined ? 1 : denominator;
return this;
};
Vex.Flow.Fraction.prototype.value = function() {
return this.numerator / this.denominator;
};
Vex.Flow.Fraction.prototype.simplify = function() {
var u = this.numerator;
var d = this.denominator;
var gcd = this.gcd(u, d);
u /= gcd;
d /= gcd;
if (d < 0) {
d = -d;
u = -u;
}
return this.set(u, d);
};
Vex.Flow.Fraction.prototype.add = function(f1, f2) {
var lcm = this.lcm(f1.denominator, f2.denominator);
var a = lcm / f1.denominator;
var b = lcm / f2.denominator;
var u = f1.numerator * a + f2.numerator * b;
return this.set(u, lcm);
};
Vex.Flow.Fraction.prototype.subtract = function(f1, f2) {
var lcm = this.lcm(f1.denominator, f2.denominator);
var a = lcm / f1.denominator;
var b = lcm / f2.denominator;
var u = f1.numerator * a - f2.numerator * b;
return this.set(u, lcm);
};
Vex.Flow.Fraction.prototype.multiply = function(f1, f2) {
return this.set(f1.numerator * f2.numerator, f1.denominator * f2.denominator);
};
Vex.Flow.Fraction.prototype.divide = function(f1, f2) {
return this.set(f1.numerator * f2.denominator, f1.denominator * f2.numerator);
};
// temporary cached objects
Vex.Flow.Fraction.__compareA = new Vex.Flow.Fraction();
Vex.Flow.Fraction.__compareB = new Vex.Flow.Fraction();
Vex.Flow.Fraction.__tmp = new Vex.Flow.Fraction();
// Simplifies both sides and check if they are equals
Vex.Flow.Fraction.prototype.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);
};
// Creates a new copy with this current values
Vex.Flow.Fraction.prototype.clone = function() {
return new Vex.Flow.Fraction(this.numerator, this.denominator);
};
// Copies value of another Fraction into itself
Vex.Flow.Fraction.prototype.copy = function(copy) {
return this.set(copy.numerator, copy.denominator);
};
// return integer component eg. (4/2) == 2
Vex.Flow.Fraction.prototype.quotient = function() {
return~~ (this.numerator / this.denominator);
};
// Turns positive
Vex.Flow.Fraction.prototype.abs = function() {
this.denominator = Math.abs(this.denominator);
this.numerator = Math.abs(this.numerator);
return this;
};
// Raw string representation
Vex.Flow.Fraction.prototype.toString = function() {
return this.numerator + '/' + this.denominator;
};
// Simplified string respresentation
Vex.Flow.Fraction.prototype.toSimplifiedString = function() {
return Vex.Flow.Fraction.__tmp.copy(this).simplify().toString();
};
// Return string representation in mixed form
Vex.Flow.Fraction.prototype.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;
};
// the fraction component when reduced to a mixed number
Vex.Flow.Fraction.prototype.fraction = function() {
this.numerator %= this.denominator;
return this;
};
// parse a fraction string
Vex.Flow.Fraction.prototype.parse = function(str) {
var i = str.split('/');
var n = parseInt(i[0], 0);
var d = (i[1]) ? parseInt(i[1], 0) : 1;
return this.set(n, d);
};
// Utils
// Greatest Common Divisor
Vex.Flow.Fraction.prototype.gcd = function(a, b) {
return b ? this.gcd(b, a % b) : a;
};
// Lowest Common Multiple
Vex.Flow.Fraction.prototype.lcm = function(a, b) {
var gcd = this.gcd(a, b);
return Math.abs(a) / gcd * Math.abs(b);
};
// TESTS
var test = new Vex.Flow.Fraction();
var test2 = new Vex.Flow.Fraction();
var check = new Vex.Flow.Fraction();
console.assert(test.parse('-1/5').toMixedString() === '-1/5');
console.assert(test.parse('10/5').toMixedString() === '2');
console.assert(test.parse('-13/5').toMixedString() === '-2 3/5');
console.assert(test.parse('10/3').quotient() === 3);
console.assert(test.parse('12/7').fraction().toString() === '5/7');
console.assert(test.subtract(test.parse('1/12'), test.parse('1/12')).toSimplifiedString() === '0/1');
console.assert(test.divide(test.parse('1/12'), test.parse('1/12')).toSimplifiedString() === '1/1');
console.assert(test.add(test.parse('1/12'), test.parse('1/12')).toSimplifiedString() === '1/6');
console.assert(test.add(test.parse('1/12'), test.parse('1/12')).toString() === '2/12');
console.assert(test.set(1, 3).toString() === '1/3', 'toString()');
console.assert(test.set(1, 3).equals(check.set(1, 3)), 'equals()');
console.assert(test.set(2, 6).equals(check.set(1, 3)), 'simplify equals()');
console.assert(test.set(2, 6).toString() === '2/6', 'toString()');
console.assert(test.set(2, 6).toSimplifiedString() === '1/3', 'toSimplifiedString()');
console.assert(test.gcd(12, 4) === 4, 'gcd1');
console.assert(test.gcd(7, 4) === 1, 'gcd2');
console.assert(test.lcm(7, 4) === 28, 'lcm1');
console.assert(test.lcm(7, 3) === 21, 'lcm2');
console.assert(test.parse('1/3').equals(check.set(1, 3)), 'parse()'); //​​​​​​​​​​​​​​
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment