diff --git a/Fraction.js b/Fraction.js new file mode 100644 index 0000000..c63fb4c --- /dev/null +++ b/Fraction.js @@ -0,0 +1,205 @@ +var Fraction = function() { + this.numerator; + this.denominator; + + var numerator = 0; + var denominator = 1; + var frac; + + //If two numbers-like arguments are passed into the function + if (!isNaN(arguments[0]) && !isNaN(arguments[1])) { + numerator = Number(arguments[0]); + denominator = Number(arguments[1]); + + } + //Only a single number is present + else if (!isNaN(arguments[0])) { + numerator = Number(arguments[0]); + } + //If a string is passed into the function + else if (Fraction.isString(arguments[0])) { + var number = arguments[0]; + if (number.indexOf('/') != -1) { + numerator = Number(number.substring(0, number.indexOf('/'))); + denominator = Number(number.substring(number.indexOf('/') + 1, number.length)); + } else { + numerator = number; + } + } + else { + throw new Error("Arguments invalid"); + } + + if (!Number.isInteger(numerator) || !Number.isInteger(denominator)) { + if (!Number.isInteger(numerator)) { numerator = Fraction.decimalToFraction(numerator); } + if (!Number.isInteger(denominator)) { denominator = Fraction.decimalToFraction(denominator); } + frac = Fraction.divide(numerator, denominator); + numerator = frac.numerator; + denominator = frac.denominator; + } + if (denominator == 0) { + throw new Error("Cannot divide by zero"); + } + + this.numerator = numerator; + this.denominator = denominator; + + this.simplify(); + +} + +Fraction.prototype.multiply = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + return Fraction.change(this, Fraction.multiply(this, frac)); +} +Fraction.prototype.divide = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + return Fraction.change(this, Fraction.divide(this, frac)); +} +Fraction.prototype.add = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + return Fraction.change(this, Fraction.add(this, frac)); +} +Fraction.prototype.subtract = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + return Fraction.change(this, Fraction.subtract(this, frac)); +} +Fraction.prototype.simplify = function() { + Fraction.correctArgumentLength(0, arguments.length); + return Fraction.change(this, Fraction.simplify(this)); +} +Fraction.prototype.toString = function() { + return Fraction.toString(this); +} +Fraction.prototype.equals = function(frac) { + return Fraction.equals(this, frac); +} +Fraction.prototype.valueOf = function() { + return Fraction.valueOf(this); +} + +Fraction.add = function(frac1, frac2) { + Fraction.correctArgumentLength(2, arguments.length); + frac1 = Fraction.toFraction(frac1) + frac2 = Fraction.toFraction(frac2) + + var newFrac = frac1; + newFrac.numerator = frac1.numerator * frac2.denominator + frac1.denominator * frac2.numerator; + newFrac.denominator = frac1.denominator * frac2.denominator; + return Fraction.simplify(newFrac); +} +Fraction.subtract = function(frac1, frac2) { + Fraction.correctArgumentLength(2, arguments.length); + frac1 = Fraction.toFraction(frac1); + frac2 = Fraction.toFraction(frac2); + + var newFrac = frac1; + newFrac.numerator = frac1.numerator * frac2.denominator - frac1.denominator * frac2.numerator; + newFrac.denominator = frac1.denominator * frac2.denominator; + return Fraction.simplify(newFrac); +} +Fraction.multiply = function(frac1, frac2) { + Fraction.correctArgumentLength(2, arguments.length); + frac1 = Fraction.toFraction(frac1); + frac2 = Fraction.toFraction(frac2); + + var newFrac = frac1; + newFrac.numerator = frac1.numerator * frac2.numerator; + newFrac.denominator = frac1.denominator * frac2.denominator; + return Fraction.simplify(newFrac); +} +Fraction.divide = function(frac1, frac2) { + Fraction.correctArgumentLength(2, arguments.length); + frac1 = Fraction.toFraction(frac1); + frac2 = Fraction.toFraction(frac2); + + var newFrac = frac1; + newFrac.numerator = frac1.numerator * frac2.denominator; + newFrac.denominator = frac1.denominator * frac2.numerator; + return Fraction.simplify(newFrac); +} + +Fraction.simplify = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + frac = Fraction.toFraction(frac); + + var gcd = Fraction.greatestCommonDivisor(frac.numerator, frac.denominator); + if (gcd == 1) { return frac; } + frac.numerator /= gcd; + frac.denominator /= gcd; + return frac; +} +Fraction.greatestCommonDivisor = function(num1, num2) { + var greater; + var lesser; + + num1 = Math.abs(num1); + num2 = Math.abs(num2); + greater = Math.max(num1, num2); + lesser = Math.min(num1, num2); + + while (lesser != 0) { + var t = lesser; + lesser = greater % lesser; + greater = t; + } + return greater; +} +Fraction.toString = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + if (frac.denominator == 1) { return "" + frac.numerator; } + return "" + frac.numerator + "/" + frac.denominator; +} +Fraction.equals = function(frac1, frac2) { + Fraction.correctArgumentLength(2, arguments.length); + frac1 = Fraction.toFraction(frac1); + frac2 = Fraction.toFraction(frac2); + + frac1 = Fraction.simplify(frac1); + frac2 = Fraction.simplify(frac2); + return frac1.numerator == frac2.numerator && frac1.denominator == frac2.denominator; +} +Fraction.valueOf = function(frac) { + Fraction.correctArgumentLength(1, arguments.length); + frac = Fraction.toFraction(frac); + return frac.numerator / frac.denominator; +} +Fraction.correctArgumentLength = function(ideal, actual) { + if (ideal != actual) { throw new Error("" + ideal + " arguments needed"); } +} +Fraction.change = function(oldFrac, newFrac) { + Fraction.correctArgumentLength(2, arguments.length); + oldFrac.numerator = newFrac.numerator; + oldFrac.denominator = newFrac.denominator; + return oldFrac; +} +Fraction.isString = function(s) { + return typeof(s) == "string" || (typeof(s) == 'object' && s.constructor == String) +} +Fraction.fromFraction = function(frac) { + return typeof(frac) == 'object' && frac.constructor == Fraction; +} +Fraction.toFraction = function(x) { + if (!Fraction.fromFraction(x)) { + return new Fraction(x); + } + return x; +} +Fraction.decimalToFraction = function(x) { + Fraction.correctArgumentLength(1, arguments.length); + if (isNaN(x)) { throw new Error("Argument invalid") } + + x = String(x); + var decLocation = x.indexOf('.'); + if (decLocation != -1) { + var whole = x.substring(0, decLocation); + var remainder = x.substring(decLocation + 1, x.length); + var nthPlace = Math.pow(10, remainder.length); + return Fraction.add(new Fraction(Number(whole), 1), new Fraction(Number(remainder), nthPlace)) + } + else { return new Fraction(Number(x)); } +} + +if (typeof module !== "undefined" && module.exports) { + module.exports = Fraction; +} diff --git a/Tests.js b/Tests.js new file mode 100644 index 0000000..79adb39 --- /dev/null +++ b/Tests.js @@ -0,0 +1,132 @@ +if (typeof(module) != 'undefined' && module.exports) { + var Fraction = require('./Fraction.js'); +} +var assert = function(expected, actual) { + if (typeof(expected) == 'object' && typeof(actual) == 'object') { + if (!expected.equals(actual)) { + throw new Error("Assertion Error: " + expected.toString() + " is not the same as " + actual.toString()); + } + } else { + if (expected != actual) { + throw new Error("Assertion Error: " + expected + " does not equal " + actual); + } + } +} +var describe = function(objective, test) { + console.log(objective); + if (typeof(test) == 'function') { + test(); + console.log("Tests passed\n"); + } else { + console.log("No tests written\n"); + } +} +var section = function(label, tests) { + console.log("\n*********************\n" +label +"\n*********************"); + if (typeof(tests) == 'function') { + tests(); + console.log("\nSection tests passed"); + } else { + console.log("No tests written for this section"); + } +} + +section("Helper Functions -- Basic", function() { + describe("Check if input is a string", function() { + assert(Fraction.isString(""), true); + assert(Fraction.isString(true), false); + assert(Fraction.isString("Hello"), true); + assert(Fraction.isString(5), false); + assert(Fraction.isString("12"), true); + }); + describe("Greatest Common Divisor algorithm", function() { + assert(Fraction.greatestCommonDivisor(4, 20), 4); + assert(Fraction.greatestCommonDivisor(3, 30), 3); + assert(Fraction.greatestCommonDivisor(1, 5), 1); + assert(Fraction.greatestCommonDivisor(6, 20), 2); + assert(Fraction.greatestCommonDivisor(15, 33), 3); + }); +}) + +section("Constructor -- Basic \n\nEncounter any errors? Check out \n\nFraction\nFraction.simplify\nfraction.change", function() { + describe("Tests for new Fraction()", function() { + assert(new Fraction(1, 2), .5); + assert(new Fraction(4, 4), 1); + assert(new Fraction("1/10"), .1); + assert(new Fraction(4), 4); + assert(new Fraction("10", "5"), 2); + assert(new Fraction("1/5"), new Fraction(1,5)); + assert(new Fraction("10", "5"), new Fraction(10.0, 5.0)); + }); +}); +section("Helper Functions -- Intermediate", function() { + describe("Did the input come from the Fraction constructor?", function() { + assert(Fraction.fromFraction(123), false); + assert(Fraction.fromFraction(new Fraction("5.0/3.0")), true); + assert(Fraction.fromFraction(3/2), false); + assert(Fraction.fromFraction(new Fraction(2,5)), true); + assert(Fraction.fromFraction("1/2"), false); + assert(Fraction.fromFraction(new Fraction(3.4,23)), true); + assert(Fraction.fromFraction(true), false); + assert(Fraction.fromFraction(new Fraction("1/2")), true); + }); + describe("To String", function() { + assert(Fraction.toString(new Fraction(1,4)), "1/4"); + assert(new Fraction("5/6").toString(), "5/6"); + assert(Fraction.toString(new Fraction("8.0", "9.0")), "8/9"); + assert(new Fraction(5).toString(), "5"); + assert(Fraction.toString(new Fraction("7.0/3.0")), "7/3"); + }); + describe("Convert to Fraction", function() { + assert(Fraction.toFraction(2), new Fraction(2, 1)); + assert(Fraction.toFraction("1/4"), new Fraction(1,4)); + assert(Fraction.toFraction(.7), new Fraction(7, 10)); + assert(Fraction.toFraction("8/10"), new Fraction(4,5)); + }); +}); +section("Arithmetic Operations", function() { + describe("The addition operator", function() { + assert(Fraction.add("2/12", "4/6"), new Fraction("5/6")); + assert(new Fraction("4/8").add("1/4"), .75); + assert(Fraction.add("2/10", "2/5"), new Fraction(3,5)); + assert(new Fraction("3/6").add("2/12"), new Fraction(2,3)); + }); + describe("The subtraction operator", function() { + assert(Fraction.subtract("4/8", "1/4"), .25); + assert(new Fraction("7/12").subtract("3/6"), new Fraction(1,12)); + assert(Fraction.subtract("5/12", "1/6"), .25); + assert(new Fraction("1/2").subtract("1/3"), new Fraction(1, 6)); + }); + describe("The multiplication operator", function() { + assert(Fraction.multiply(.9, "5/18"), .25); + assert(new Fraction("2/3").multiply(9), 6); + assert(Fraction.multiply("6/15", "6/7"), new Fraction(12, 35)); + assert(new Fraction("14/3").multiply("3/4"), 3.5); + }); + describe("The division operator", function() { + assert(Fraction.divide("2/3", "7/8"), new Fraction(16, 21)); + assert(new Fraction("5/9").divide("105/36"), new Fraction(4, 21)); + assert(Fraction.divide("5/12", "9/4"), new Fraction(5, 27)); + assert(new Fraction(19).divide("38/6"), 3); + }); +}); +section("Helper Functions -- Advanced", function() { + describe("Convert decimal to fraction", function() { + assert(Fraction.decimalToFraction(.5), new Fraction(1, 2)); + assert(Fraction.decimalToFraction(.25), new Fraction(1,4)); + assert(Fraction.decimalToFraction(1.7), new Fraction(17, 10)); + assert(Fraction.decimalToFraction(8), new Fraction(8,1)); + }); +}); + +section("Constructor -- Advanced", function() { + describe("Tests for new Fraction()", function() { + assert(new Fraction(.25), .25); + assert(new Fraction("1.2/4.5"), (1.2/4.5)); + assert(new Fraction("1.8", "1.0"), 1.8); + assert(new Fraction(2.5, 0.1), 25); + assert(new Fraction("1.2/4.5"), new Fraction("1.2", "4.5")); + }); +}); + +console.log("\nFinished!!!! All tests passed."); diff --git a/package.json b/package.json new file mode 100644 index 0000000..3262a57 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "fractions", + "version": "1.0.0", + "description": "A library to help do more precise fractional math", + "main": "Fraction.js", + "repository": { + "type": "git", + "url": "https://github.com/brandonrozek/Fraction.js" + }, + "bugs": { + "url": "https://github.com/brandonrozek/Fraction.js/issues" + }, + "scripts": { + "test": "node Tests.js" + }, + "keywords": [ + "fractions", + "math" + ], + "author": "Brandon Rozek (https://brandonrozek.com)", + "license": "MIT" +}