Created
April 12, 2012 18:55
-
-
Save zz85/2370065 to your computer and use it in GitHub Desktop.
Fractional / Rational Number Class for performing fractional calculations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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