diff --git a/src/equations.js b/src/equations.js index 1d5eaa9..29d4ae8 100644 --- a/src/equations.js +++ b/src/equations.js @@ -47,7 +47,7 @@ Equation.prototype._crossMultiply = function() { // Both sides should now be rationals and we are ready to cross-multiply this.lhs = newLhs.numer.multiply(newRhs.denom); this.rhs = newLhs.denom.multiply(newRhs.numer); -} +}; Equation.prototype.solveFor = function(variable) { this._crossMultiply(); @@ -162,6 +162,9 @@ Equation.prototype.solveFor = function(variable) { var c = coefs.c; var d = coefs.d; + if (a == undefined) + throw new Error("No Solution"); + // Calculate D and D0. var D = a.multiply(b).multiply(c).multiply(d).multiply(18); D = D.subtract(b.pow(3).multiply(d).multiply(4)); @@ -314,7 +317,7 @@ Equation.prototype._maxDegreeOfVariable = function(variable) { }; Equation.prototype._variableCanBeIsolated = function(variable) { - return this._maxDegreeOfVariable(variable) === 1; + return this._maxDegreeOfVariable(variable) === 1 && this._noCrossProductsWithVariable(variable); }; Equation.prototype._noCrossProductsWithVariable = function(variable) { diff --git a/src/expressions.js b/src/expressions.js index 21685ef..7eb56c2 100644 --- a/src/expressions.js +++ b/src/expressions.js @@ -10,7 +10,7 @@ var Expression = function(variable) { var t = new Term(v); this.terms = [t]; } else if (isInt(variable)) { - var f = new Fraction(variable, 1) + var f = new Fraction(variable, 1); var t = new Term(f); this.terms = [t]; } else if (variable instanceof Fraction) { @@ -37,7 +37,7 @@ Expression.prototype.coefficients = function() { arr.push(this.terms[i].coefficient()); } return arr; -} +}; Expression.prototype.simplify = function() { var copy = this.copy(); @@ -133,7 +133,7 @@ Expression.prototype.divide = function(a, simplify) { var copy = this.copy(); for (var i = 0; i < copy.terms.length; i++) { - copy.terms[i] = copy.terms[i].divide(thatTerm); + copy.terms[i] = copy.terms[i].divide(thatTerm, simplify); } return copy; @@ -143,15 +143,9 @@ Expression.prototype.divide = function(a, simplify) { return new Expression(rational); } } else if (a instanceof Fraction || isInt(a)) { - var copy = this.copy(); - - for (var i = 0; i < copy.terms.length; i++) { - copy.terms[i] = copy.terms[i].divide(a); - } - - return copy; + return this.divide(new Expression(a), simplify); } else { - throw new TypeError("Invalid Argument (" + a.toString() + "): Divisor must be of type Fraction or Integer."); + throw new TypeError("Invalid Argument (" + a.toString() + "): Divisor must be of type Expression, Fraction or Integer."); } }; @@ -177,7 +171,6 @@ Expression.prototype.pow = function(a, simplify) { Expression.prototype.eval = function(values, simplify) { var exp = new Expression(); - exp.constants = (simplify ? [this.constant()] : this.constants.slice()); //add all evaluated terms of this to exp exp = this.terms.reduce(function(p, c) { return p.add(c.eval(values, simplify), simplify); }, exp); @@ -201,7 +194,7 @@ Expression.prototype.toRational = function() { var numer = copy; var denom = new Expression(1); return new Rational(numer, denom); -} +}; Expression.prototype.toString = function(options) { var str = ""; @@ -493,9 +486,9 @@ Term.prototype.subtract = function(term) { var copy = this.copy(); copy.coefficients = [copy.coefficient().subtract(term.coefficient())]; return copy; - } else if (a instanceof Rational) { + } else if (term instanceof Rational) { var exp = new Expression(this); - return exp.toRational().subtract(a); + return exp.toRational().subtract(term); } else { throw new TypeError("Invalid Argument (" + term.toString() + "): Subtrahend must be of type String, Expression, Term, Fraction or Integer."); } @@ -506,7 +499,13 @@ Term.prototype.multiply = function(a, simplify) { if (a instanceof Term) { thisTerm.variables = thisTerm.variables.concat(a.variables); - thisTerm.coefficients = a.coefficients.concat(thisTerm.coefficients); + thisTerm.coefficients = a.coefficients + .concat(thisTerm.coefficients) + .filter(function(a) { + return !(a.numer == 1 && a.denom == 1); + }); + if (thisTerm.coefficients.length == 0) + thisTerm.coefficients.push(new Fraction(1, 1)); } else if (isInt(a) || a instanceof Fraction) { var newCoef = (isInt(a) ? new Fraction(a, 1) : a); @@ -619,13 +618,15 @@ Term.prototype.maxDegreeOfVariable = function(variable) { }; Term.prototype.canBeCombinedWith = function(term) { - if (term instanceof Rational) + if (term instanceof Rational || + (this.maxDegree() == 0 && + term.maxDegree() == 0)) return true; var thisVars = this.variables; var thatVars = term.variables; if (thisVars.length != thatVars.length) { - return this.maxDegree() == 0 && term.maxDegree() == 0; + return false; } var matches = 0; @@ -762,7 +763,7 @@ var Rational = function(a, b) { this.denom = b; else this.denom = new Expression(b); -} +}; function toFraction(a) { var i; @@ -797,11 +798,11 @@ function gcd_term(a, b) { Rational.prototype.copy = function() { return new Rational(this.numer.copy(), this.denom.copy()); -} +}; Rational.prototype.canBeCombinedWith = function(a) { return true; -} +}; Rational.prototype.add = function(a) { if (a instanceof Rational) { @@ -818,7 +819,7 @@ Rational.prototype.add = function(a) { } else { return this.add(new Expression(a)); } -} +}; Rational.prototype.subtract = function(a) { if (a instanceof Rational) { @@ -828,7 +829,7 @@ Rational.prototype.subtract = function(a) { } else { return this.subtract(new Expression(a)); } -} +}; Rational.prototype.multiply = function(a) { if (a instanceof Rational) { @@ -842,7 +843,7 @@ Rational.prototype.multiply = function(a) { } else { return this.multiply(new Expression(a)); } -} +}; Rational.prototype.divide = function(a) { if (a instanceof Rational) { @@ -856,7 +857,7 @@ Rational.prototype.divide = function(a) { } else { return this.divide(new Expression(a)); } -} +}; Rational.prototype.reduce = function() { var copy = this.copy(); @@ -878,11 +879,11 @@ Rational.prototype.reduce = function() { copy.denom = copy.denom.divide(g); return copy; -} +}; Rational.prototype.toString = function() { return "(" + this.numer + ") / (" + this.denom + ")"; -} +}; module.exports = { Rational: Rational, diff --git a/src/parser.js b/src/parser.js index e3fc786..2df2250 100644 --- a/src/parser.js +++ b/src/parser.js @@ -158,7 +158,7 @@ Parser.prototype.parseTermRest = function(factor) { } else if (this.match('divide')) { this.update(); var devfactor = this.parseFactor(); - return factor.divide(this.parseTermRest(devfactor)); + return this.parseTermRest(factor.divide(devfactor)); } else if (this.match('epsilon')) { return factor; } else { @@ -176,7 +176,7 @@ Parser.prototype.parseTermRest = function(factor) { * Is used to convert expressions to fractions, as dividing by expressions is not possible **/ Parser.prototype.convertToFraction = function(expression) { - if (expression.terms.filter(function(term) { return term.maxDegree() > 0 }).length > 0) { + if (expression.terms.filter(function(term) { return term.maxDegree() > 0; }).length > 0) { throw new TypeError('Invalid Argument (' + expression.toString() + '): Divisor must be of type Integer or Fraction.'); } else { var c = expression.constant(); diff --git a/test/equation-spec.js b/test/equation-spec.js index 72f509b..a4854e2 100644 --- a/test/equation-spec.js +++ b/test/equation-spec.js @@ -1,4 +1,5 @@ var Expression = require('../src/expressions').Expression; +var Rational = require('../src/expressions').Rational; var Equation = require('../src/equations'); var Fraction = require('../src/fractions'); var round = require('../src/helper').round; @@ -26,9 +27,9 @@ describe("A linear equation with one variable", function() { expect(algebra.toTex(eq)).toEqual("\\frac{1}{5}x + \\frac{4}{5} = x - \\frac{1}{6}"); }); - it("should return a fraction when solving for the one variable", function() { + it("should return a Rational when solving for the one variable", function() { var answer = eq.solveFor("x"); - expect(answer instanceof Fraction).toBe(true); + expect(answer instanceof Expression).toBe(true); }); it("should throw an exception when solving for a variable that isn't there", function() { diff --git a/test/expression-spec.js b/test/expression-spec.js index 6d4ad46..068e7d2 100644 --- a/test/expression-spec.js +++ b/test/expression-spec.js @@ -357,18 +357,6 @@ describe("Expression division", function() { expect(x.divide(y).toString()).toEqual("xy^-1"); }); - it("should not allow division of a multinominal denomenator", function() { - var multi = new Expression("x").add(3); - - expect(function(){x.divide(multi);}).toThrow("Invalid Argument ((x)/(x + 3)): Only monomial expressions can be divided."); - }); - - it("should not allow division of a multinominal numenator", function() { - var multi = new Expression("x").add(3); - - expect(function(){multi.divide(x);}).toThrow("Invalid Argument ((x + 3)/(x)): Only monomial expressions can be divided."); - }); - it("should throw an exception if dividing by zero", function() { expect(function(){x.divide(0);}).toThrow("Divide By Zero"); }); @@ -683,7 +671,7 @@ describe("Expression evaluation with unsimplified expressions", function() { it("works when there's multiple coefficients", function() { var exp = new Expression("x").multiply(5).multiply(4, false); // 4 * 5x var answer = exp.eval({x:2}, false); - expect(answer.toString()).toEqual("4 * 5 * 2"); + expect(answer.toString()).toEqual("2 * 5 * 4"); }); diff --git a/test/parser-spec.js b/test/parser-spec.js index 8a9d2e6..a33bda1 100644 --- a/test/parser-spec.js +++ b/test/parser-spec.js @@ -9,11 +9,6 @@ var Parser = require("../src/parser.js"), describe("Input validity", function() { var p = new Parser(); - it("should throw an error if the input contains invalid divisions.", function(){ - var input = "1/(x-3)=1"; - expect(function(){p.parse(input);}).toThrow(new Error("Invalid Argument (x - 3): Divisor must be of type Integer or Fraction.")); - }); - it("does not accept special characters", function(){ var input = "2+4*x-€"; expect(function(){p.parse(input);}).toThrow(new Error("Token error at character € at position 6")); @@ -101,7 +96,7 @@ describe("Operators", function() { expect(p.parse(input).toString()).toEqual(new Equation(lhs,rhs).toString()); }); - it("should parse / correctly", function(){ + it("should parse / correctly", function() { var input = "x/2 = 8"; var lhs = new Expression("x").divide(2); var rhs = new Expression(8); diff --git a/test/term-spec.js b/test/term-spec.js index 3df8d5a..88a8019 100644 --- a/test/term-spec.js +++ b/test/term-spec.js @@ -241,7 +241,7 @@ describe("Term evaluation", function() { var e = t.eval({x:2}, false); - expect(e.toString()).toEqual("5 * 3 * 2"); + expect(e.toString()).toEqual("2 * 3 * 5"); }); it("should work when there is more than 1 coefficient and more than 1 variable and simplify = false", function() { @@ -255,7 +255,7 @@ describe("Term evaluation", function() { t = t.multiply(6, false); // 6 * 5 * 3xy var answer = t.eval({x:2}, false); // 6 * 5 * 3 * 2y - expect(answer.toString()).toEqual("6 * 5 * 3 * 2y"); + expect(answer.toString()).toEqual("2 * 3 * 5 * 6y"); }); it("works with negative numbers", function() { @@ -269,9 +269,9 @@ describe("Term evaluation", function() { t = t.multiply(6, false); // 6 * 5 * 3xy - var answer = t.eval({x:-2}, false); // 6 * 5 * 3 * -2y + var answer = t.eval({x:-2}, true); // 6 * 5 * 3 * -2y - expect(answer.toString()).toEqual("6 * 5 * 3 * -2y"); + expect(answer.toString()).toEqual("-180y"); }); });