// Created by Matt Johnson, see http://satchamo.com/fraction.txt for more details // Example /* window.onload = function() { var x = new Fraction(1,2); var y = new Fraction(-3,4); var z = Fractions.fromString(' -10 / 12 '); var a = Fractions.fromArray([1,2]); var sum = Fractions.add(x, y, z, a, z); var diff = Fractions.subtract(x, y, z, a, z); var prod = Fractions.multiply(x, y, z, a, z); var quo = Fractions.divide(x, y, z, a, z); alert(sum.toString()); } */ // to save memory, everything is public var Fraction = function(numerator, denominator) { // move negative sign to numerator if(denominator < 0) { numerator *= -1; denominator *= -1; } this.numerator = numerator; this.denominator = denominator || 1; }; // I still have accessors because I don't want Fractions to modify the numerator or denom by accident Fraction.prototype.getNumerator = function() { return this.numerator; }; Fraction.prototype.getDenominator = function() { return this.denominator; }; Fraction.prototype.toString = function(mixed, lowest_terms) { mixed = typeof(mixed) == 'undefined' ? true : mixed; lowest_terms = typeof(lowest_terms) == 'undefined' ? true : lowest_terms; return Fractions.toString(this, mixed, lowest_terms); }; Fraction.prototype.isLowestTerms = function() { return Fractions.equal(this, Fractions.toLowestTerms(this)); }; Fraction.prototype.isPositive = function() { return (this.numerator / this.denominator) >= 0; }; Fraction.prototype.debug = function() { return this.numerator + "/" + this.denominator; }; Fractions = { // reduce a fraction into lowest terms. // is a better name toLowestTerms()? toLowestTerms : function(fraction) { var gcf = this.gcf(fraction.getNumerator(), fraction.getDenominator()); return new Fraction(fraction.getNumerator() / gcf, fraction.getDenominator() / gcf); }, // take a fraction_parts array, and turn it into an improper fraction object toImproper : function(fraction_parts) { var numerator = 0; var fraction; // convert the strings to ints (assume they are strings) for(var i = 0; i < fraction_parts.length; i++) fraction_parts[i] = parseInt(fraction_parts[i], 10); if(fraction_parts.length == 3) // whole, numerator, denominator { numerator = fraction_parts[2] * Math.abs(fraction_parts[0]) + Math.abs(fraction_parts[1]); // denom * whole + numerator, remember from middle school? // apply the minus if(fraction_parts[0] < 0 || fraction_parts[1] < 0) { numerator = numerator * -1; } fraction = new Fraction(numerator, fraction_parts[2]); } else { fraction = new Fraction(fraction_parts[0], fraction_parts[1]); } return fraction; }, isFraction : function(fraction) { return (/^\s*[+-]?\s*(\d+)?\s*(\d+\s*\/\s*\d+)?\s*$/).test(fraction) //return (!(/^-?(\d+)? *(\d+\/\d+)?$/).test(fraction_str) }, // take a fraction string and turn it into a fraction object fromString : function(fraction_str) { if(!this.isFraction(fraction_str)) { alert("Invalid fraction"); return false; } var fraction; var fraction_parts ; if(fraction_str * 1 == fraction_str) // it's just a plain jane number { fraction = new Fraction(fraction_str, 1); } else { // there is probably a better way to parse this thing fraction_str = fraction_str.replace(/\s*-\s*/, '-'); // move minus sign fraction_str = fraction_str.replace(/s*\+\s*/, ''); // remove plus var part1 = fraction_str.split(/\s*\/\s*/); // seperate whole and numerator from denominator, and get rid of whitespace var part2 = part1[0].split(/\s+/); // serpate whole from numerator if(part2.length == 2) // mixed number { fraction_parts = [part2[0], part2[1], part1[1]]; } else // just numerator and denom { fraction_parts = [0, part1[0], part1[1]]; } fraction = this.toImproper(fraction_parts); } return fraction; }, fromArray : function(fraction) { return this.toImproper(fraction); }, // find the least common multiple of two ints lcm : function(a, b) { a = Math.abs(a); b = Math.abs(b); return a * b / this.gcf(a, b); }, // find the greatest common factor of two ints. Not using bitwise operations because they are slow in JS gcf : function(a, b) { do { var rest = a % b; a = b; b= rest; } while(rest!==0); return a; }, // take in a fraction object, and return a pretty string toString : function(fraction, mixed, lowest_terms) { mixed = typeof(mixed) == 'undefined' ? true : mixed; lowest_terms = typeof(lowest_terms) == 'undefined' ? true : lowest_terms; var whole = 0; var fraction_str = ''; if(mixed) { if(Math.abs(fraction.getNumerator()) >= fraction.getDenominator()) // it's improper { whole = parseInt(fraction.getNumerator() / fraction.getDenominator()); /*if(fraction.getNumerator() > 0) { whole = Math.floor(fraction.getNumerator() / fraction.getDenominator()); } else { whole = Math.ceil(fraction.getNumerator() / fraction.getDenominator()); }*/ fraction = new Fraction(fraction.getNumerator() - (whole * fraction.getDenominator()), fraction.getDenominator()); } } if(lowest_terms) { fraction = this.toLowestTerms(fraction); } // start building fraction string if(whole !== 0) { fraction_str = whole + " "; } if(fraction.getNumerator() !== 0) { fraction_str += fraction.getNumerator() + "/" + fraction.getDenominator(); } if(whole === 0 && fraction.getNumerator() === 0) { fraction_str = 0; } else { if((Math.abs(fraction.getNumerator()) > fraction.getNumerator()) || (Math.abs(fraction.getDenominator()) > fraction.getDenominator())) { fraction_str = fraction_str.replace(/-/g, ''); fraction_str = "-" + fraction_str; } } return fraction_str; }, add : function() { var numerator = 0; var denominator = arguments[0].getDenominator(); var i; for(i = 1; i < arguments.length; ++i) { denominator = this.lcm(denominator, arguments[i].getDenominator()); } for(i = 0; i < arguments.length; ++i) { numerator += arguments[i].getNumerator() * (denominator / arguments[i].getDenominator()); } return new Fraction(numerator, denominator); }, subtract : function() { var numerator = 0; var denominator = arguments[0].getDenominator(); var i; for(i = 0; i < arguments.length; ++i) { denominator = this.lcm(denominator, arguments[i].getDenominator()); } numerator = arguments[0].getNumerator() * (denominator / arguments[0].getDenominator()); for(i = 1; i < arguments.length; ++i) { numerator = numerator - arguments[i].getNumerator() * (denominator / arguments[i].getDenominator()); } return new Fraction(numerator, denominator); }, multiply : function() { var numerator = 1; var denominator = 1; for(var i = 0; i < arguments.length; ++i) { numerator *= arguments[i].getNumerator(); denominator *= arguments[i].getDenominator(); } return new Fraction(numerator, denominator); }, divide : function() { var numerator = arguments[0].getNumerator(); var denominator = arguments[0].getDenominator(); for(var i = 1; i < arguments.length; ++i) { numerator *= arguments[i].getDenominator(); denominator *= arguments[i].getNumerator(); } return new Fraction(numerator, denominator); }, equal : function() { for(var i = 0; i < arguments.length - 1; i++) { if(!this.equals(arguments[i], arguments[i + 1])) return false; } return true; }, equals : function(a, b) { return a.getNumerator() == b.getNumerator() && a.getDenominator() == b.getDenominator(); }, isProper : function(a) { return !(Math.abs(a.getNumerator()) >= a.getDenominator()); }, test : function() { } };