Comparing version 1.0.0 to 1.0.1
@@ -0,0 +0,0 @@ *version 0.8.0* |
@@ -9,3 +9,3 @@ { | ||
"license": "MIT", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"homepage": "http://nerdamer.com/", | ||
@@ -12,0 +12,0 @@ "directory": { |
1354
Solve.js
@@ -22,2 +22,3 @@ /* | ||
explode = _C.integration.decompose_arg, | ||
evaluate = core.Utils.evaluate, | ||
remove = core.Utils.remove, | ||
@@ -36,11 +37,3 @@ format = core.Utils.format, | ||
//version solve | ||
core.Solve = { | ||
version: '1.2.7', | ||
solve: function (eq, variable) { | ||
var solution = solve(eq, String(variable)); | ||
return new core.Vector(solution); | ||
//return new core.Vector(solve(eq.toString(), variable ? variable.toString() : variable)); | ||
} | ||
}; | ||
// The search radius for the roots | ||
@@ -54,2 +47,12 @@ core.Settings.SOLVE_RADIUS = 1000; | ||
core.Settings.STEP_SIZE = 0.1; | ||
//the maximum iterations for Newton's method | ||
core.Settings.MAX_NEWTON_ITERATIONS = 200; | ||
//the maximum number of time non-linear solve tries another jump point | ||
core.Settings.MAX_NON_LINEAR_TRIES = 12; | ||
//the amount of iterations the function will start to jump at | ||
core.Settings.NON_LINEAR_JUMP_AT = 50; | ||
//the size of the jump | ||
core.Settings.NON_LINEAR_JUMP_SIZE = 100; | ||
//the original starting point for nonlinear solving | ||
core.Settings.NON_LINEAR_START = 0.01; | ||
@@ -70,3 +73,3 @@ core.Symbol.prototype.hasTrig = function () { | ||
}; | ||
/* nerdamer version 0.7.x and up allows us to make better use of operator overloading | ||
@@ -81,60 +84,6 @@ * As such we can have this data type be supported completely outside of the core. | ||
this.RHS = rhs; //right and side | ||
} | ||
; | ||
}; | ||
//UTILS ##!! | ||
/** | ||
* This function is confusing and should be refactored. Why is removeDenom bringing the equation to LHS???? | ||
* @param {Symbol} a | ||
* @param {Symbol} b | ||
* @returns {Symbol} | ||
*/ | ||
var removeDenom = function (a, b) { | ||
//remove the denominator on both sides | ||
var den = _.multiply(a.getDenom(), b.getDenom()); | ||
a = _.expand(_.multiply(a, den.clone())); | ||
b = _.expand(_.multiply(b, den)); | ||
//swap the groups | ||
if (b.group === CP && b.group !== CP) { | ||
var t = a; | ||
a = b; | ||
b = t; //swap | ||
} | ||
//scan to eliminate denominators | ||
if (a.group === CB) { | ||
var t = new Symbol(a.multiplier), | ||
newRHS = b.clone(); | ||
a.each(function (y) { | ||
if (y.power.lessThan(0)) | ||
newRHS = _.divide(newRHS, y); | ||
else | ||
t = _.multiply(t, y); | ||
}); | ||
a = t; | ||
b = newRHS; | ||
} | ||
else if (a.group === CP) { | ||
//the logic: loop through each and if it has a denominator then multiply it out on both ends | ||
//and then start over | ||
for (var x in a.symbols) { | ||
var sym = a.symbols[x]; | ||
if (sym.group === CB) { | ||
for (var y in sym.symbols) { | ||
var sym2 = sym.symbols[y]; | ||
if (sym2.power.lessThan(0)) { | ||
return removeDenom( | ||
_.expand(_.multiply(sym2.clone().toLinear(), a)), | ||
_.expand(_.multiply(sym2.clone().toLinear(), b)) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return _.expand(_.subtract(a, b)); | ||
}; | ||
Equation.prototype = { | ||
@@ -148,4 +97,54 @@ toString: function () { | ||
toLHS: function () { | ||
return removeDenom(this.LHS.clone(), this.RHS.clone()); | ||
var eqn = this.removeDenom(); | ||
return _.expand(_.subtract(eqn.LHS, eqn.RHS));; | ||
}, | ||
removeDenom: function () { | ||
var a = this.LHS.clone(); | ||
var b = this.RHS.clone(); | ||
//remove the denominator on both sides | ||
var den = _.multiply(a.getDenom(), b.getDenom()); | ||
a = _.expand(_.multiply(a, den.clone())); | ||
b = _.expand(_.multiply(b, den)); | ||
//swap the groups | ||
if (b.group === CP && b.group !== CP) { | ||
var t = a; | ||
a = b; | ||
b = t; //swap | ||
} | ||
//scan to eliminate denominators | ||
if (a.group === CB) { | ||
var t = new Symbol(a.multiplier), | ||
newRHS = b.clone(); | ||
a.each(function (y) { | ||
if (y.power.lessThan(0)) | ||
newRHS = _.divide(newRHS, y); | ||
else | ||
t = _.multiply(t, y); | ||
}); | ||
a = t; | ||
b = newRHS; | ||
} | ||
else if (a.group === CP) { | ||
//the logic: loop through each and if it has a denominator then multiply it out on both ends | ||
//and then start over | ||
for (var x in a.symbols) { | ||
var sym = a.symbols[x]; | ||
if (sym.group === CB) { | ||
for (var y in sym.symbols) { | ||
var sym2 = sym.symbols[y]; | ||
if (sym2.power.lessThan(0)) { | ||
return new Equation( | ||
_.expand(_.multiply(sym2.clone().toLinear(), a)), | ||
_.expand(_.multiply(sym2.clone().toLinear(), b)) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return new Equation(a, b); | ||
}, | ||
clone: function () { | ||
@@ -171,13 +170,68 @@ return new Equation(this.LHS.clone(), this.RHS.clone()); | ||
}; | ||
// A utility function to parse an expression to left hand side when working with strings | ||
var toLHS = function (eqn) { | ||
//If it's an equation then call its toLHS function instead | ||
if (eqn instanceof Equation) | ||
return eqn.toLHS(); | ||
var es = eqn.split('='); | ||
if (es[1] === undefined) | ||
es[1] = '0'; | ||
var e1 = _.parse(es[0]), e2 = _.parse(es[1]); | ||
return removeDenom(e1, e2); | ||
core.Expression.prototype.solveFor = function (x) { | ||
return solve(core.Utils.isSymbol(this.symbol) ? this.symbol : this.symbol.toLHS(), x).map(function (x) { | ||
return new core.Expression(x); | ||
}); | ||
}; | ||
core.Expression.prototype.expand = function () { | ||
if (this.symbol instanceof Equation) { | ||
var clone = this.symbol.clone(); | ||
clone.RHS = _.expand(clone.RHS); | ||
clone.LHS = _.expand(clone.LHS); | ||
return new core.Expression(clone); | ||
} | ||
return new core.Expression(_.expand(this.symbol)); | ||
}; | ||
core.Expression.prototype.variables = function () { | ||
if (this.symbol instanceof Equation) | ||
return core.Utils.arrayUnique(variables(this.symbol.LHS).concat(variables(this.symbol.RHS))); | ||
return variables(this.symbol); | ||
}; | ||
core.Matrix.jacobian = function(eqns, vars) { | ||
var jacobian = new core.Matrix(); | ||
//get the variables if not supplied | ||
if(!vars) { | ||
vars = __.getSystemVariables(eqns); | ||
} | ||
vars.forEach(function(v, i) { | ||
eqns.forEach(function(eq, j) { | ||
var e = core.Calculus.diff(eq.clone(), v); | ||
jacobian.set(j, i, e); | ||
}); | ||
}); | ||
return jacobian; | ||
}; | ||
core.Matrix.prototype.max = function() { | ||
var max = new Symbol(0); | ||
this.each(function(x) { | ||
var e = x.abs(); | ||
if(e.gt(max)) | ||
max = e; | ||
}); | ||
return max; | ||
}; | ||
core.Matrix.cMatrix = function(value, vars) { | ||
var m = new core.Matrix(); | ||
//make an initial guess | ||
vars.forEach(function(v, i) { | ||
m.set(i, 0, _.parse(value)); | ||
}); | ||
return m; | ||
}; | ||
var setEq = function (a, b) { | ||
return _.equals(a, b); | ||
}; | ||
//link the Equation class back to the core | ||
core.Equation = Equation; | ||
//Loops through an array and attempts to fails a test. Stops if manages to fail. | ||
@@ -190,13 +244,196 @@ var checkAll = core.Utils.checkAll = function (args, test) { | ||
}; | ||
//version solve | ||
var __ = core.Solve = { | ||
version: '2.0.0', | ||
solutions: [], | ||
solve: function (eq, variable) { | ||
var solution = solve(eq, String(variable)); | ||
return new core.Vector(solution); | ||
//return new core.Vector(solve(eq.toString(), variable ? variable.toString() : variable)); | ||
}, | ||
/** | ||
* Brings the equation to LHS. A string can be supplied which will be converted to an Equation | ||
* @param {Equation|String} eqn | ||
* @returns {Symbol} | ||
*/ | ||
toLHS: function (eqn) { | ||
//If it's an equation then call its toLHS function instead | ||
if (!(eqn instanceof Equation)) { | ||
var es = eqn.split('='); | ||
//convert falsey values to zero | ||
es[1] = es[1] || '0'; | ||
eqn = new Equation(_.parse(es[0]), _.parse(es[1])); | ||
} | ||
return eqn.toLHS(); | ||
}, | ||
getSystemVariables: function(eqns) { | ||
vars = variables(eqns[0], null, null, true); | ||
// Solves a system of equations | ||
var sys_solve = function (eqns, var_array) { | ||
//check if a var_array was specified | ||
//nerdamer.clearVars();// this deleted ALL variables: not what we want | ||
//parse all the equations to LHS. Remember that they come in as strings | ||
for (var i = 0; i < eqns.length; i++) | ||
eqns[i] = toLHS(eqns[i]); | ||
//get all variables | ||
for (var i = 1, l=eqns.length; i < l; i++) | ||
vars = vars.concat(variables(eqns[i])); | ||
//remove duplicates | ||
vars = core.Utils.arrayUnique(vars).sort(); | ||
//done | ||
return vars; | ||
}, | ||
solveNonLinearSystem: function(eqns, tries, start) { | ||
if(tries < 0) | ||
return [];//can't find a solution | ||
start = typeof start === 'undefined' ? core.Settings.NON_LINEAR_START : start; | ||
var l = eqns.length, | ||
//the maximum number of times to jump | ||
var max_tries = core.Settings.MAX_NON_LINEAR_TRIES; | ||
//halfway through the tries | ||
var halfway = Math.floor(max_tries/2); | ||
//initialize the number of tries to 10 if not specified | ||
tries = typeof tries === 'undefined' ? max_tries : tries; | ||
//a point at which we check to see if we're converging. By inspection it seems that we can | ||
//use around 20 iterations to see if we're converging. If not then we retry a jump of x | ||
var jump_at = core.Settings.NON_LINEAR_JUMP_AT; | ||
//we jump by this many points at each pivot point | ||
var jump = core.Settings.NON_LINEAR_JUMP_SIZE; | ||
//used to check if we actually found a solution or if we gave up. Assume we will find a solution. | ||
var found = true; | ||
var create_subs = function(vars, matrix) { | ||
return vars.map(function(x, i) { | ||
return Number(matrix.get(i, 0)); | ||
}); | ||
}; | ||
var vars = __.getSystemVariables(eqns); | ||
var jacobian = core.Matrix.jacobian(eqns, vars, function(x) { | ||
return build(x, vars); | ||
}, true); | ||
var max_iter = core.Settings.MAX_NEWTON_ITERATIONS; | ||
var o, y, iters, xn1, norm, lnorm, xn, d; | ||
var f_eqns = eqns.map(function(eq) { | ||
return build(eq, vars); | ||
}); | ||
var J = jacobian.map(function(e) { | ||
return build(e, vars); | ||
}, true); | ||
//initial values | ||
xn1 = core.Matrix.cMatrix(0, vars);; | ||
//initialize the c matrix with something close to 0. | ||
var c = core.Matrix.cMatrix(start, vars); | ||
iters = 0; | ||
//start of algorithm | ||
do { | ||
//if we've reached the max iterations then exit | ||
if(iters > max_iter) { | ||
break; | ||
found = false; | ||
} | ||
//set the substitution object | ||
o = create_subs(vars, c); | ||
//set xn | ||
xn = c.clone(); | ||
//make all the substitutions for each of the equations | ||
f_eqns.forEach(function(f, i) { | ||
c.set(i, 0, f.apply(null, o)); | ||
}); | ||
var m = new core.Matrix(); | ||
J.each(function(fn, i, j) { | ||
var ans = fn.apply(null, o); | ||
m.set(i, j, ans); | ||
}); | ||
m = m.invert(); | ||
//preform the elimination | ||
y = _.multiply(m, c).negate(); | ||
//the callback is to avoid overflow in the coeffient denonimator | ||
//it converts it to a decimal and then back to a fraction. Some precision | ||
//is lost be it's better than overflow. | ||
d = y.subtract(xn1, function(x) { return _.parse(Number(x)); }); | ||
xn1 = xn.add(y, function(x) { return _.parse(Number(x)); }); | ||
//move c is now xn1 | ||
c = xn1; | ||
//get the norm | ||
//the expectation is that we're converging to some answer as this point regardless of where we start | ||
//this may have to be adjusted at some point because of erroneous assumptions | ||
if(iters >= jump_at) { | ||
//check the norm. If the norm is greater than one then it's time to try another point | ||
if(norm > 1) { | ||
//reset the start point at halway | ||
if(tries === halfway) | ||
start = 0; | ||
var sign = tries > halfway ? 1 : -1; //which side are we incrementing | ||
//we increment +n at one side and -n at the other. | ||
n = (tries % Math.floor(halfway))+1; | ||
//adjust the start point | ||
start += (sign*n*jump); | ||
//call restart | ||
return __.solveNonLinearSystem(eqns, --tries, start); | ||
} | ||
} | ||
lnorm = norm; | ||
iters++; | ||
norm = d.max(); | ||
//exit early. Revisit if we get bugs | ||
if(Number(norm) === Number(lnorm)) | ||
break; | ||
} | ||
while(Number(norm) >= Number.EPSILON) | ||
//return a blank set if nothing was found; | ||
if(!found) | ||
return []; | ||
//return c since that's the answer | ||
return __.systemSolutions(c, vars, true, function(x) { | ||
return core.Utils.round(Number(x), 14); | ||
}); | ||
}, | ||
systemSolutions: function(result, vars, expand_result, callback) { | ||
var solutions = core.Settings.SOLUTIONS_AS_OBJECT ? {} : []; | ||
result.each(function (e, idx) { | ||
var solution = (expand_result ? _.expand(e) : e).valueOf(); | ||
if(callback) | ||
solution = callback.call(e, solution); | ||
var variable = vars[idx]; | ||
if (core.Settings.SOLUTIONS_AS_OBJECT) { | ||
solutions[variable] = solution; | ||
} | ||
else | ||
solutions.push([variable, solution]); /*NO*/ | ||
}); | ||
//done | ||
return solutions; | ||
}, | ||
//https://www.lakeheadu.ca/sites/default/files/uploads/77/docs/RemaniFinal.pdf | ||
solveSystem: function (eqns, var_array) { | ||
//check if a var_array was specified | ||
//nerdamer.clearVars();// this deleted ALL variables: not what we want | ||
//parse all the equations to LHS. Remember that they come in as strings | ||
for (var i = 0; i < eqns.length; i++) | ||
eqns[i] = __.toLHS(eqns[i]); | ||
var l = eqns.length, | ||
m = new core.Matrix(), | ||
@@ -207,321 +444,412 @@ c = new core.Matrix(), | ||
if (typeof var_array === 'undefined') { | ||
//check to make sure that all the equations are linear | ||
if (!_A.allLinear(eqns)) | ||
core.err('System must contain all linear equations!'); | ||
vars = variables(eqns[0], null, null, true); | ||
if (typeof var_array === 'undefined') { | ||
//check to make sure that all the equations are linear | ||
if (!_A.allLinear(eqns)) | ||
return __.solveNonLinearSystem(eqns); | ||
//core.err('System must contain all linear equations!'); | ||
vars = __.getSystemVariables(eqns); | ||
// deletes only the variables of the linear equations in the nerdamer namespace | ||
for (var i = 0; i < vars.length; i++) { | ||
nerdamer.setVar(vars[i], "delete"); | ||
} | ||
// populate the matrix | ||
for (var i = 0; i < l; i++) { | ||
var e = eqns[i]; //store the expression | ||
for (var j = 0; j < l; j++) { | ||
var v = vars[j]; | ||
var coeffs = []; | ||
e.each(function(x) { | ||
if(x.contains(v)) { | ||
coeffs = coeffs.concat(x.coeffs()); | ||
} | ||
}); | ||
//get all variables | ||
for (var i = 1; i < l; i++) | ||
vars = vars.concat(variables(eqns[i])); | ||
//remove duplicates | ||
vars = core.Utils.arrayUnique(vars).sort(); | ||
var cf = core.Utils.arraySum(coeffs); | ||
m.set(i, j, cf); | ||
} | ||
// deletes only the variables of the linear equations in the nerdamer namespace | ||
for (var i = 0; i < vars.length; i++) { | ||
nerdamer.setVar(vars[i], "delete"); | ||
} | ||
// populate the matrix | ||
for (var i = 0; i < l; i++) { | ||
var e = eqns[i]; //store the expression | ||
// for (var j = 0; j < l; j++) { | ||
// var variable = e.symbols[vars[j]]; | ||
// m.set(i, j, variable ? variable.multiplier : 0); | ||
// } | ||
for (var j = 0; j < l; j++) { | ||
var v = vars[j]; | ||
var coeffs = []; | ||
e.each(function(x) { | ||
if(x.contains(v)) { | ||
coeffs = coeffs.concat(x.coeffs()); | ||
} | ||
//strip the variables from the symbol so we're left with only the zeroth coefficient | ||
//start with the symbol and remove each variable and its coefficient | ||
var num = e.clone(); | ||
vars.map(function(e) { | ||
num = num.stripVar(e); | ||
}); | ||
var cf = core.Utils.arraySum(coeffs); | ||
m.set(i, j, cf); | ||
c.set(i, 0, num.negate()); | ||
} | ||
//strip the variables from the symbol so we're left with only the zeroth coefficient | ||
//start with the symbol and remove each variable and its coefficient | ||
var num = e.clone(); | ||
vars.map(function(e) { | ||
num = num.stripVar(e); | ||
}); | ||
c.set(i, 0, num.negate()); | ||
} | ||
} | ||
else { | ||
/** | ||
* The idea is that we loop through each equation and then expand it. Afterwards we loop | ||
* through each term and see if and check to see if it matches one of the variables. | ||
* When a match is found we mark it. No other match should be found for that term. If it | ||
* is we stop since it's not linear. | ||
*/ | ||
vars = var_array; | ||
expand_result = true; | ||
for (i = 0; i < l; i++) { | ||
//prefill | ||
c.set(i, 0, new Symbol(0)); | ||
var e = _.expand(eqns[i]).collectSymbols(); //expand and store | ||
//go trough each of the variables | ||
for (var j = 0; j < var_array.length; j++) { | ||
m.set(i, j, new Symbol(0)); | ||
var v = var_array[j]; | ||
//go through the terms and sort the variables | ||
for (var k = 0; k < e.length; k++) { | ||
var term = e[k], | ||
check = false; | ||
for (var z = 0; z < var_array.length; z++) { | ||
//check to see if terms contain multiple variables | ||
if (term.contains(var_array[z])) { | ||
if (check) | ||
core.err('Multiple variables found for term ' + term); | ||
check = true; | ||
else { | ||
/** | ||
* The idea is that we loop through each equation and then expand it. Afterwards we loop | ||
* through each term and see if and check to see if it matches one of the variables. | ||
* When a match is found we mark it. No other match should be found for that term. If it | ||
* is we stop since it's not linear. | ||
*/ | ||
vars = var_array; | ||
expand_result = true; | ||
for (i = 0; i < l; i++) { | ||
//prefill | ||
c.set(i, 0, new Symbol(0)); | ||
var e = _.expand(eqns[i]).collectSymbols(); //expand and store | ||
//go trough each of the variables | ||
for (var j = 0; j < var_array.length; j++) { | ||
m.set(i, j, new Symbol(0)); | ||
var v = var_array[j]; | ||
//go through the terms and sort the variables | ||
for (var k = 0; k < e.length; k++) { | ||
var term = e[k], | ||
check = false; | ||
for (var z = 0; z < var_array.length; z++) { | ||
//check to see if terms contain multiple variables | ||
if (term.contains(var_array[z])) { | ||
if (check) | ||
core.err('Multiple variables found for term ' + term); | ||
check = true; | ||
} | ||
} | ||
//we made sure that every term contains one variable so it's safe to assume that if the | ||
//variable is found then the remainder is the coefficient. | ||
if (term.contains(v)) { | ||
var tparts = explode(remove(e, k), v); | ||
m.set(i, j, _.add(m.get(i, j), tparts[0])); | ||
} | ||
} | ||
//we made sure that every term contains one variable so it's safe to assume that if the | ||
//variable is found then the remainder is the coefficient. | ||
if (term.contains(v)) { | ||
var tparts = explode(remove(e, k), v); | ||
m.set(i, j, _.add(m.get(i, j), tparts[0])); | ||
} | ||
} | ||
//all the remaining terms go to the c matrix | ||
for (k = 0; k < e.length; k++) { | ||
c.set(i, 0, _.add(c.get(i, 0), e[k])); | ||
} | ||
} | ||
//all the remaining terms go to the c matrix | ||
for (k = 0; k < e.length; k++) { | ||
c.set(i, 0, _.add(c.get(i, 0), e[k])); | ||
} | ||
//consider case (a+b)*I+u | ||
} | ||
//consider case (a+b)*I+u | ||
} | ||
//check if the system has a distinct solution | ||
if(m.determinant().equals(0)) | ||
throw new core.exceptions.SolveError('System does not have a distinct solution'); | ||
// Use M^-1*c to solve system | ||
m = m.invert(); | ||
var result = m.multiply(c); | ||
//correct the sign as per issue #410 | ||
if (core.Utils.isArray(var_array)) | ||
result.each(function (x) { | ||
return x.negate(); | ||
//check if the system has a distinct solution | ||
if(m.determinant().equals(0)) | ||
throw new core.exceptions.SolveError('System does not have a distinct solution'); | ||
// Use M^-1*c to solve system | ||
m = m.invert(); | ||
var result = m.multiply(c); | ||
//correct the sign as per issue #410 | ||
if (core.Utils.isArray(var_array)) | ||
result.each(function (x) { | ||
return x.negate(); | ||
}); | ||
return __.systemSolutions(result, vars, expand_result); | ||
}, | ||
/** | ||
* The quadratic function but only one side. | ||
* @param {Symbol} c | ||
* @param {Symbol} b | ||
* @param {Symbol} a | ||
* @returns {Symbol} | ||
*/ | ||
quad: function (c, b, a) { | ||
var bsqmin4ac = _.subtract(_.pow(b.clone(), Symbol(2)), _.multiply(_.multiply(a.clone(), c.clone()), Symbol(4)))/*b^2 - 4ac*/; | ||
var det = _.pow(bsqmin4ac, Symbol(0.5)); | ||
var retval = [ | ||
_.parse(_.divide(_.add(b.clone().negate(), det.clone()), _.multiply(new Symbol(2), a.clone()))), | ||
_.parse(_.divide(_.subtract(b.clone().negate(), det.clone()), _.multiply(new Symbol(2), a.clone()))) | ||
]; | ||
return retval; | ||
}, | ||
/** | ||
* The cubic equation | ||
* http://math.stackexchange.com/questions/61725/is-there-a-systematic-way-of-solving-cubic-equations | ||
* @param {Symbol} d_o | ||
* @param {Symbol} c_o | ||
* @param {Symbol} b_o | ||
* @param {Symbol} a_o | ||
* @returns {Array} | ||
*/ | ||
cubic:function (d_o, c_o, b_o, a_o) { | ||
//convert everything to text | ||
var a = a_o.text(), b = b_o.text(), c = c_o.text(), d = d_o.text(); | ||
var d0s = '({1})^2-3*({0})*({2})', | ||
d0 = _.parse(format(d0s, a, b, c)), | ||
Q = _.parse(format('((2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3}))^2-4*(({1})^2-3*({0})*({2}))^3)^(1/2)', a, b, c, d)), | ||
C = _.parse(format('((1/2)*(({4})+2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3})))^(1/3)', a, b, c, d, Q)); | ||
//check if C equals 0 | ||
var scope = {}; | ||
//populate the scope object | ||
variables(C).map(function (x) { | ||
scope[x] = 1; | ||
}); | ||
var solutions = core.Settings.SOLUTIONS_AS_OBJECT ? {} : []; | ||
result.each(function (e, idx) { | ||
var solution = (expand_result ? _.expand(e) : e).valueOf(); | ||
var variable = vars[idx]; | ||
if (core.Settings.SOLUTIONS_AS_OBJECT) { | ||
solutions[variable] = solution; | ||
} | ||
else | ||
solutions.push([variable, solution]); | ||
}); | ||
//done | ||
return solutions; | ||
}; | ||
// solve quad oder polynomials symbolically | ||
var quad = function (c, b, a, plus_or_min) { | ||
var plus_or_minus = plus_or_min === '-' ? 'subtract' : 'add'; | ||
var bsqmin4ac = _.subtract(_.pow(b.clone(), Symbol(2)), _.multiply(_.multiply(a.clone(), c.clone()), Symbol(4)))/*b^2 - 4ac*/; | ||
var det = _.pow(bsqmin4ac, Symbol(0.5)); | ||
var retval = _.divide(_[plus_or_minus](b.clone().negate(), det), _.multiply(new Symbol(2), a.clone())); | ||
return retval; | ||
}; | ||
//http://math.stackexchange.com/questions/61725/is-there-a-systematic-way-of-solving-cubic-equations | ||
var cubic = function (d_o, c_o, b_o, a_o) { | ||
//convert everything to text | ||
var a = a_o.text(), b = b_o.text(), c = c_o.text(), d = d_o.text(); | ||
var d0s = '({1})^2-3*({0})*({2})', | ||
d0 = _.parse(format(d0s, a, b, c)), | ||
Q = _.parse(format('((2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3}))^2-4*(({1})^2-3*({0})*({2}))^3)^(1/2)', a, b, c, d)), | ||
C = _.parse(format('((1/2)*(({4})+2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3})))^(1/3)', a, b, c, d, Q)); | ||
//check if C equals 0 | ||
var scope = {}; | ||
//populate the scope object | ||
variables(C).map(function (x) { | ||
scope[x] = 1; | ||
}); | ||
var Ct = core.Utils.block('PARSE2NUMBER', function () { | ||
return _.parse(C, scope); | ||
}); | ||
var Ct = core.Utils.block('PARSE2NUMBER', function () { | ||
return _.parse(C, scope); | ||
}); | ||
if (Number(d0) === 0 && Number(Ct) === 0) //negate Q such that C != 0 | ||
C = _.parse(format('((1/2)*(-({4})+2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3})))^(1/3)', a, b, c, d, Q)); | ||
if (Number(d0) === 0 && Number(Ct) === 0) //negate Q such that C != 0 | ||
C = _.parse(format('((1/2)*(-({4})+2*({1})^3-9*({0})*({1})*({2})+27*({0})^2*({3})))^(1/3)', a, b, c, d, Q)); | ||
var xs = [ | ||
'-(b/(3*a))-C/(3*a)-(((b^2-3*a*c))/(3*a*C))', | ||
'-(b/(3*a))+(C*(1+i*sqrt(3)))/(6*a)+((1-i*sqrt(3))*(b^2-3*a*c))/(6*a*C)'.replace(/i/g, core.Settings.IMAGINARY), | ||
'-(b/(3*a))+(C*(1-i*sqrt(3)))/(6*a)+((1+i*sqrt(3))*(b^2-3*a*c))/(6*a*C)'.replace(/i/g, core.Settings.IMAGINARY) | ||
]; | ||
var xs = [ | ||
'-(b/(3*a))-C/(3*a)-(((b^2-3*a*c))/(3*a*C))', | ||
'-(b/(3*a))+(C*(1+i*sqrt(3)))/(6*a)+((1-i*sqrt(3))*(b^2-3*a*c))/(6*a*C)'.replace(/i/g, core.Settings.IMAGINARY), | ||
'-(b/(3*a))+(C*(1-i*sqrt(3)))/(6*a)+((1+i*sqrt(3))*(b^2-3*a*c))/(6*a*C)'.replace(/i/g, core.Settings.IMAGINARY) | ||
]; | ||
return xs.map(function (e, i) { | ||
var o = {a: a_o.clone(), b: b_o.clone(), c: c_o.clone(), d: d_o.clone(), C: C.clone()}; | ||
return _.parse(e, o); | ||
}); | ||
}, | ||
/** | ||
* The quartic equation | ||
* @param {Symbol} e | ||
* @param {Symbol} d | ||
* @param {Symbol} c | ||
* @param {Symbol} b | ||
* @param {Symbol} a | ||
* @returns {Array} | ||
*/ | ||
quartic: function (e, d, c, b, a) { | ||
var scope = {}; | ||
core.Utils.arrayUnique(variables(a).concat(variables(b)) | ||
.concat(variables(c)).concat(variables(d)).concat(variables(e))) | ||
.map(function (x) { | ||
scope[x] = 1; | ||
}); | ||
a = a.toString(); | ||
b = b.toString(); | ||
c = c.toString(); | ||
d = d.toString(); | ||
e = e.toString(); | ||
var p, q, D, D0, D1, Q, x1, x2, x3, x4; | ||
/*var D = core.Utils.block('PARSE2NUMBER', function() { | ||
return _.parse(format("256*({0})^3*({4})^3-192*({0})^2*({1})*({3})*({4})^2-128*({0})^2*({2})^2*({4})^2+144*({0})^2*({2})*({3})^2*({4})"+ | ||
"-27*({0})^2*({3})^4+144*({0})*({1})^2*({2})*({4})^2-6*({0})*({1})^2*({3})^2*({4})-80*({0})*({1})*({2})^2*({3})*({4})+18*({0})*({1})*({2})*({3})^3"+ | ||
"+16*({0})*({2})^4*({4})-4*({0})*({2})^3*({3})^2-27*({1})^4*({4})^2+18*({1})^3*({2})*({3})*({4})-4*({1})^3*({3})^3-4*({1})^2*({2})^3*({4})+({1})^2*({2})^2*({3})^2", | ||
a, b, c, d, e), scope); | ||
});*/ | ||
return xs.map(function (e, i) { | ||
var o = {a: a_o.clone(), b: b_o.clone(), c: c_o.clone(), d: d_o.clone(), C: C.clone()}; | ||
return _.parse(e, o); | ||
}); | ||
}; | ||
/* in progress */ | ||
//solve(x^4+x+0.1, x) | ||
var quartic = function (e, d, c, b, a) { | ||
var scope = {}; | ||
core.Utils.arrayUnique(variables(a).concat(variables(b)) | ||
.concat(variables(c)).concat(variables(d)).concat(variables(e))) | ||
.map(function (x) { | ||
scope[x] = 1; | ||
p = _.parse(format("(8*({0})*({2})-3*({1})^2)/(8*({0})^2)", a, b, c)).toString(); //a, b, c | ||
q = _.parse(format("(({1})^3-4*({0})*({1})*({2})+8*({0})^2*({3}))/(8*({0})^3)", a, b, c, d)).toString();//a, b, c, d, e | ||
D0 = _.parse(format("12*({0})*({4})-3*({1})*({3})+({2})^2", a, b, c, d, e)).toString(); //a, b, c, d, e | ||
D1 = _.parse(format("2*({2})^3-9*({1})*({2})*({3})+27*({1})^2*({4})+27*({0})*({3})^2-72*({0})*({2})*({4})", a, b, c, d, e)).toString(); //a, b, c, d, e | ||
Q = _.parse(format("((({1})+(({1})^2-4*({0})^3)^(1/2))/2)^(1/3)", D0, D1)).toString(); //D0, D1 | ||
S = _.parse(format("(1/2)*(-(2/3)*({1})+(1/(3*({0}))*(({2})+(({3})/({2})))))^(1/2)", a, p, Q, D0)).toString(); //a, p, Q, D0 | ||
x1 = _.parse(format("-(({1})/(4*({0})))-({4})+(1/2)*sqrt(-4*({4})^2-2*({2})+(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x2 = _.parse(format("-(({1})/(4*({0})))-({4})-(1/2)*sqrt(-4*({4})^2-2*({2})+(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x3 = _.parse(format("-(({1})/(4*({0})))+({4})+(1/2)*sqrt(-4*({4})^2-2*({2})-(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x4 = _.parse(format("-(({1})/(4*({0})))+({4})-(1/2)*sqrt(-4*({4})^2-2*({2})-(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
return [x1, x2, x3, x4]; | ||
}, | ||
/** | ||
* Breaks the equation up in its factors and tries to solve the smaller parts | ||
* @param {Symbol} symbol | ||
* @param {String} solve_for | ||
* @returns {Array} | ||
*/ | ||
divideAndConquer: function (symbol, solve_for) { | ||
var sols = []; | ||
//see if we can solve the factors | ||
var factors = core.Algebra.Factor.factor(symbol); | ||
if (factors.group === CB) { | ||
factors.each(function (x) { | ||
x = Symbol.unwrapPARENS(x); | ||
sols = sols.concat(solve(x, solve_for)); | ||
}); | ||
a = a.toString(); | ||
b = b.toString(); | ||
c = c.toString(); | ||
d = d.toString(); | ||
e = e.toString(); | ||
var p, q, D, D0, D1, Q, x1, x2, x3, x4; | ||
/*var D = core.Utils.block('PARSE2NUMBER', function() { | ||
return _.parse(format("256*({0})^3*({4})^3-192*({0})^2*({1})*({3})*({4})^2-128*({0})^2*({2})^2*({4})^2+144*({0})^2*({2})*({3})^2*({4})"+ | ||
"-27*({0})^2*({3})^4+144*({0})*({1})^2*({2})*({4})^2-6*({0})*({1})^2*({3})^2*({4})-80*({0})*({1})*({2})^2*({3})*({4})+18*({0})*({1})*({2})*({3})^3"+ | ||
"+16*({0})*({2})^4*({4})-4*({0})*({2})^3*({3})^2-27*({1})^4*({4})^2+18*({1})^3*({2})*({3})*({4})-4*({1})^3*({3})^3-4*({1})^2*({2})^3*({4})+({1})^2*({2})^2*({3})^2", | ||
a, b, c, d, e), scope); | ||
});*/ | ||
p = _.parse(format("(8*({0})*({2})-3*({1})^2)/(8*({0})^2)", a, b, c)).toString(); //a, b, c | ||
q = _.parse(format("(({1})^3-4*({0})*({1})*({2})+8*({0})^2*({3}))/(8*({0})^3)", a, b, c, d)).toString();//a, b, c, d, e | ||
D0 = _.parse(format("12*({0})*({4})-3*({1})*({3})+({2})^2", a, b, c, d, e)).toString(); //a, b, c, d, e | ||
D1 = _.parse(format("2*({2})^3-9*({1})*({2})*({3})+27*({1})^2*({4})+27*({0})*({3})^2-72*({0})*({2})*({4})", a, b, c, d, e)).toString(); //a, b, c, d, e | ||
Q = _.parse(format("((({1})+(({1})^2-4*({0})^3)^(1/2))/2)^(1/3)", D0, D1)).toString(); //D0, D1 | ||
S = _.parse(format("(1/2)*(-(2/3)*({1})+(1/(3*({0}))*(({2})+(({3})/({2})))))^(1/2)", a, p, Q, D0)).toString(); //a, p, Q, D0 | ||
x1 = _.parse(format("-(({1})/(4*({0})))-({4})+(1/2)*sqrt(-4*({4})^2-2*({2})+(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x2 = _.parse(format("-(({1})/(4*({0})))-({4})-(1/2)*sqrt(-4*({4})^2-2*({2})+(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x3 = _.parse(format("-(({1})/(4*({0})))+({4})+(1/2)*sqrt(-4*({4})^2-2*({2})-(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
x4 = _.parse(format("-(({1})/(4*({0})))+({4})-(1/2)*sqrt(-4*({4})^2-2*({2})-(({3})/({4})))", a, b, p, q, S)); //a, b, p, q, S | ||
return [x1, x2, x3, x4]; | ||
}; | ||
//solve by divide and conquer | ||
var divnconsolve = function (symbol, solve_for) { | ||
var sols = []; | ||
//see if we can solve the factors | ||
var factors = core.Algebra.Factor.factor(symbol); | ||
if (factors.group === CB) { | ||
factors.each(function (x) { | ||
x = Symbol.unwrapPARENS(x); | ||
sols = sols.concat(solve(x, solve_for)); | ||
} | ||
return sols; | ||
}, | ||
/** | ||
* Attempts to solve the equation assuming it's a polynomial with numeric coefficients | ||
* @param {Symbol} eq | ||
* @param {String} solve_for | ||
* @returns {Array} | ||
*/ | ||
csolve: function (eq, solve_for) { | ||
return core.Utils.block('IGNORE_E', function () { | ||
var f, p, pn, n, pf, r, theta, sr, sp, roots; | ||
roots = []; | ||
f = core.Utils.decompose_fn(eq, solve_for, true); | ||
if (f.x.group === S) { | ||
p = _.parse(f.x.power); | ||
pn = Number(p); | ||
n = _.pow(_.divide(f.b.negate(), f.a), p.invert()); | ||
pf = Symbol.toPolarFormArray(n); | ||
r = pf[0]; | ||
theta = pf[1]; | ||
sr = r.toString(); | ||
sp = p.toString(); | ||
var k, root, str; | ||
for (var i = 0; i < pn; i++) { | ||
k = i; | ||
str = format('({0})*e^(2*{1}*pi*{2}*{3})', sr, k, p, core.Settings.IMAGINARY); | ||
root = _.parse(str); | ||
roots.push(root); | ||
} | ||
} | ||
return roots; | ||
}, true); | ||
}, | ||
/** | ||
* Generates starting points for the Newton solver given an expression at zero. | ||
* It beings by check if zero is a good point and starts expanding by a provided step size. | ||
* Builds on the fact that if the sign changes over an interval then a zero | ||
* must exist on that interval | ||
* @param {Symbol} symbol | ||
* @param {Number} step | ||
* @returns {Array} | ||
*/ | ||
getPoints: function (symbol, step) { | ||
step = step || 0.01; | ||
var f = build(symbol); | ||
var start = Math.round(f(0)), | ||
last = f(start), | ||
last_sign = last / Math.abs(last), | ||
points = [], | ||
rside = core.Settings.ROOTS_PER_SIDE, // the max number of roots on right side | ||
lside = rside * 2 + 1; // the max number of roots on left side | ||
// check around the starting point | ||
points.push(Math.floor(start / 2)); //half way from zero might be a good start | ||
points.push(Math.abs(start)); //|f(0)| could be a good start | ||
points.push(start);//|f(0)| could be a good start | ||
//adjust for log. A good starting point to include for log is 0.1 | ||
symbol.each(function (x) { | ||
if (x.containsFunction('log')) | ||
points.push(0.1); | ||
}); | ||
} | ||
return sols; | ||
}; | ||
// Possible issue #1. If the step size exceeds the zeros then they'll be missed. Consider the case | ||
// where the function dips to negative and then back the positive with a step size of 0.1. The function | ||
// will miss the zeros because it will jump right over it. Think of a case where this can happen. | ||
for (var i = start; (i) < core.Settings.SOLVE_RADIUS; i++) { | ||
var val = f(i * step), | ||
sign = val / Math.abs(val); | ||
if (isNaN(val) || !isFinite(val) || points.length > rside) { | ||
break; | ||
} | ||
//compare the signs. The have to be different if they cross a zero | ||
if (sign !== last_sign) { | ||
points.push((i - 1) / 2); //take note of the possible zero location | ||
} | ||
last_sign = sign; | ||
} | ||
var csolve = function (symbol, solve_for) { | ||
return core.Utils.block('IGNORE_E', function () { | ||
var f, p, pn, n, pf, r, theta, sr, sp, roots; | ||
roots = []; | ||
f = core.Utils.decompose_fn(symbol, solve_for, true); | ||
if (f.x.group === S) { | ||
p = _.parse(f.x.power); | ||
pn = Number(p); | ||
n = _.pow(_.divide(f.b.negate(), f.a), p.invert()); | ||
pf = Symbol.toPolarFormArray(n); | ||
r = pf[0]; | ||
theta = pf[1]; | ||
sr = r.toString(); | ||
sp = p.toString(); | ||
var k, root, str; | ||
for (var i = 0; i < pn; i++) { | ||
k = i; | ||
str = format('({0})*e^(2*{1}*pi*{2}*{3})', sr, k, p, core.Settings.IMAGINARY); | ||
root = _.parse(str); | ||
roots.push(root); | ||
//check the other side | ||
for (var i = start - 1; i > -core.Settings.SOLVE_RADIUS; i--) { | ||
var val = f(i), | ||
sign = val / Math.abs(val); | ||
if (isNaN(val) || !isFinite(val) || points.length > lside) | ||
break; | ||
//compare the signs. The have to be different if they cross a zero | ||
if (sign !== last_sign) | ||
points.push((i - 1) / 2); //take note of the possible zero location | ||
last_sign = sign; | ||
} | ||
return points; | ||
}, | ||
Newton: function (point, f, fp) { | ||
var maxiter = core.Settings.MAX_NEWTON_ITERATIONS, | ||
iter = 0; | ||
//first try the point itself. If it's zero viola. We're done | ||
var x0 = point, x; | ||
do { | ||
var fx0 = f(x0); //store the result of the function | ||
//if the value is zero then we're done because 0 - (0/d f(x0)) = 0 | ||
if (x0 === 0 && fx0 === 0) { | ||
x = 0; | ||
break; | ||
} | ||
iter++; | ||
if (iter > maxiter) | ||
return; //naximum iterations reached | ||
x = x0 - fx0 / fp(x0); | ||
var e = Math.abs(x - x0); | ||
x0 = x; | ||
} | ||
return roots; | ||
}, true); | ||
}; | ||
while (e > Number.EPSILON) | ||
var polysolve = function (EQ, solve_for) { | ||
solve_for = solve_for.toString(); | ||
var eq = core.Utils.isSymbol(EQ) ? EQ : toLHS(EQ); | ||
var factors = _A.Factor.factor(eq); | ||
var solutions = []; | ||
factors.each(function (x) { | ||
var sols = solve(x.arg ? x.args[0] : x, solve_for).map(function (a) { | ||
solutions.push(a); | ||
}); | ||
}); | ||
return new core.Vector(solutions); | ||
}; | ||
return x; | ||
}, | ||
rewrite: function (rhs, lhs, for_variable) { | ||
lhs = lhs || new Symbol(0); | ||
if(rhs.isComposite() && rhs.isLinear()) { | ||
//try to isolate the square root | ||
//container for the square roots | ||
var sqrts = []; | ||
//all else | ||
var rem = []; | ||
rhs.each(function(x) { | ||
x = x.clone(); | ||
if(x.fname === 'sqrt' && x.contains(for_variable)) { | ||
sqrts.push(x); | ||
} | ||
else { | ||
rem.push(x); | ||
} | ||
}, true); | ||
var get_points = function (symbol, step) { | ||
step = step || 0.01; | ||
var f = build(symbol); | ||
var start = Math.round(f(0)), | ||
last = f(start), | ||
last_sign = last / Math.abs(last), | ||
points = [], | ||
rside = core.Settings.ROOTS_PER_SIDE, // the max number of roots on right side | ||
lside = rside * 2 + 1; // the max number of roots on left side | ||
// check around the starting point | ||
points.push(Math.floor(start / 2)); //half way from zero might be a good start | ||
points.push(Math.abs(start)); //|f(0)| could be a good start | ||
points.push(start);//|f(0)| could be a good start | ||
//adjust for log. A good starting point to include for log is 0.1 | ||
symbol.each(function (x) { | ||
if (x.containsFunction('log')) | ||
points.push(0.1); | ||
}); | ||
// Possible issue #1. If the step size exceeds the zeros then they'll be missed. Consider the case | ||
// where the function dips to negative and then back the positive with a step size of 0.1. The function | ||
// will miss the zeros because it will jump right over it. Think of a case where this can happen. | ||
for (var i = start; (i) < core.Settings.SOLVE_RADIUS; i++) { | ||
var val = f(i * step), | ||
sign = val / Math.abs(val); | ||
if (isNaN(val) || !isFinite(val) || points.length > rside) { | ||
break; | ||
if(sqrts.length === 1) { | ||
//move the remainder to the RHS | ||
lhs = _.expand(_.pow(_.subtract(lhs, core.Utils.arraySum(rem)), new Symbol(2))); | ||
//square both sides | ||
rhs = _.expand(_.pow(Symbol.unwrapSQRT(sqrts[0]), new Symbol(2))); | ||
} | ||
} | ||
//compare the signs. The have to be different if they cross a zero | ||
if (sign !== last_sign) { | ||
points.push((i - 1) / 2); //take note of the possible zero location | ||
else { | ||
rhs = Symbol.unwrapSQRT(_.expand(rhs)); //expand the term expression go get rid of quotients when possible | ||
} | ||
last_sign = sign; | ||
} | ||
var c = 0, //a counter to see if we have all terms with the variable | ||
l = rhs.length; | ||
//try to rewrite the whole thing | ||
if (rhs.group === CP && rhs.contains(for_variable) && rhs.isLinear()) { | ||
rhs.distributeMultiplier(); | ||
var t = new Symbol(0); | ||
//first bring all the terms containing the variable to the lhs | ||
rhs.each(function (x) { | ||
if (x.contains(for_variable)) { | ||
c++; | ||
t = _.add(t, x.clone()); | ||
} | ||
else | ||
lhs = _.subtract(lhs, x.clone()); | ||
}); | ||
rhs = t; | ||
//check the other side | ||
for (var i = start - 1; i > -core.Settings.SOLVE_RADIUS; i--) { | ||
var val = f(i), | ||
sign = val / Math.abs(val); | ||
if (isNaN(val) || !isFinite(val) || points.length > lside) | ||
break; | ||
//compare the signs. The have to be different if they cross a zero | ||
if (sign !== last_sign) | ||
points.push((i - 1) / 2); //take note of the possible zero location | ||
last_sign = sign; | ||
} | ||
return points; | ||
}; | ||
//Newton's iteration | ||
var Newton = function (point, f, fp) { | ||
var maxiter = 200, | ||
iter = 0; | ||
//first try the point itself. If it's zero viola. We're done | ||
var x0 = point, x; | ||
do { | ||
var fx0 = f(x0); //store the result of the function | ||
//if the value is zero then we're done because 0 - (0/d f(x0)) = 0 | ||
if (x0 === 0 && fx0 === 0) { | ||
x = 0; | ||
break; | ||
//if not all the terms contain the variable so it's in the form | ||
//a*x^2+x | ||
if (c !== l) { | ||
return __.rewrite(rhs, lhs, for_variable); | ||
} | ||
else { | ||
return [rhs, lhs]; | ||
} | ||
} | ||
iter++; | ||
if (iter > maxiter) | ||
return; //naximum iterations reached | ||
else if (rhs.group === CB && rhs.contains(for_variable) && rhs.isLinear()) { | ||
if (rhs.multiplier.lessThan(0)) { | ||
rhs.multiplier = rhs.multiplier.multiply(new core.Frac(-1)); | ||
lhs.multiplier = lhs.multiplier.multiply(new core.Frac(-1)); | ||
} | ||
if (lhs.equals(0)) | ||
return new Symbol(0); | ||
else { | ||
var t = new Symbol(1); | ||
rhs.each(function (x) { | ||
if (x.contains(for_variable)) | ||
t = _.multiply(t, x.clone()); | ||
else | ||
lhs = _.divide(lhs, x.clone()); | ||
}); | ||
rhs = t; | ||
return __.rewrite(rhs, lhs, for_variable); | ||
x = x0 - fx0 / fp(x0); | ||
var e = Math.abs(x - x0); | ||
x0 = x; | ||
} | ||
} | ||
else if (!rhs.isLinear() && rhs.contains(for_variable)) { | ||
var p = _.parse(rhs.power.clone().invert()); | ||
rhs = _.pow(rhs, p.clone()); | ||
lhs = _.pow(_.expand(lhs), p.clone()); | ||
return __.rewrite(rhs, lhs, for_variable); | ||
} | ||
else if (rhs.group === FN || rhs.group === S || rhs.group === PL) { | ||
return [rhs, lhs]; | ||
} | ||
} | ||
while (e > Number.EPSILON) | ||
}; | ||
return x; | ||
}; | ||
/* | ||
@@ -534,3 +862,4 @@ * | ||
var solve = function (eqns, solve_for, solutions) { | ||
//make preparations if it's an Equation | ||
if (eqns instanceof Equation) { | ||
@@ -556,3 +885,3 @@ //if it's zero then we're done | ||
if (isArray(eqns)) { | ||
return sys_solve.apply(undefined, arguments); | ||
return __.solveSystem.apply(undefined, arguments); | ||
} | ||
@@ -563,5 +892,40 @@ //parse out functions. Fix for issue #300 | ||
solutions = solutions || []; | ||
//mark existing solutions as not to have duplicates | ||
var existing = {}; | ||
//Is usued to add solutions to set. | ||
//TODO: Set is now implemented and should be utilized | ||
var add_to_result = function (r, has_trig) { | ||
var r_is_symbol = isSymbol(r); | ||
if (r === undefined || typeof r === 'number' && isNaN(r)) | ||
return; | ||
if (isArray(r)) { | ||
r.map(function(sol) { | ||
add_to_result(sol); | ||
}); | ||
} | ||
else { | ||
if (r.valueOf() !== 'null') { | ||
if (!r_is_symbol) | ||
r = _.parse(r); | ||
//try to convert the number to multiples of pi | ||
if (core.Settings.make_pi_conversions && has_trig) { | ||
var temp = _.divide(r.clone(), new Symbol(Math.PI)), | ||
m = temp.multiplier, | ||
a = Math.abs(m.num), | ||
b = Math.abs(m.den); | ||
if (a < 10 && b < 10) | ||
r = _.multiply(temp, new Symbol('pi')); | ||
} | ||
//convert to a string so we can mark it as a known solution | ||
var r_str = r.toString(); | ||
if (!existing[r_str]) | ||
solutions.push(r); /*NO*/ | ||
//mark the answer as seen | ||
existing[r_str] = true; | ||
} | ||
} | ||
}; | ||
//maybe we get lucky | ||
if (eqns.group === S && eqns.contains(solve_for)) { | ||
solutions.push(new Symbol(0)); | ||
add_to_result(new Symbol(0)); | ||
return solutions; | ||
@@ -582,56 +946,4 @@ } | ||
} | ||
var existing = {}, //mark existing solutions as not to have duplicates | ||
add_to_result = function (r, has_trig) { | ||
var r_is_symbol = isSymbol(r); | ||
if (r === undefined || typeof r === 'number' && isNaN(r)) | ||
return; | ||
if (isArray(r)) | ||
solutions = solutions.concat(r); | ||
else { | ||
if (r.valueOf() !== 'null') { | ||
if (!r_is_symbol) | ||
r = _.parse(r); | ||
//try to convert the number to multiples of pi | ||
if (core.Settings.make_pi_conversions && has_trig) { | ||
var temp = _.divide(r.clone(), new Symbol(Math.PI)), | ||
m = temp.multiplier, | ||
a = Math.abs(m.num), | ||
b = Math.abs(m.den); | ||
if (a < 10 && b < 10) | ||
r = _.multiply(temp, new Symbol('pi')); | ||
} | ||
//convert to a string so we can mark it as a known solution | ||
var r_str = r.toString(); | ||
if (!existing[r_str]) | ||
solutions.push(r); | ||
//mark the answer as seen | ||
existing[r_str] = true; | ||
} | ||
} | ||
}; | ||
//gets points around which to solve. It does that because it builds on the principle that if | ||
//the sign changes over an interval then there must be a zero on that interval | ||
var attempt_Newton = function (symbol) { | ||
var has_trig = symbol.hasTrig(); | ||
// we get all the points where a possible zero might exist | ||
var points1 = get_points(symbol, 0.1); | ||
var points2 = get_points(symbol, 0.05); | ||
var points3 = get_points(symbol, 0.01); | ||
var points = core.Utils.arrayUnique(points1.concat(points2).concat(points3)), | ||
l = points.length; | ||
//compile the function and the derivative of the function | ||
var f = build(symbol.clone()); | ||
var d = _C.diff(symbol.clone()); | ||
var fp = build(d); | ||
for (var i = 0; i < l; i++) { | ||
var point = points[i]; | ||
add_to_result(Newton(point, f, fp), has_trig); | ||
} | ||
solutions.sort(); | ||
}; | ||
var eq = core.Utils.isSymbol(eqns) ? eqns : toLHS(eqns), | ||
var eq = core.Utils.isSymbol(eqns) ? eqns : __.toLHS(eqns), | ||
vars = core.Utils.variables(eq), //get a list of all the variables | ||
@@ -697,62 +1009,3 @@ numvars = vars.length;//how many variables are we dealing with | ||
}; | ||
//rewrites equations/expression in simpler form | ||
var rewrite = function (rhs, lhs) { | ||
lhs = lhs || new Symbol(0); | ||
rhs = Symbol.unwrapSQRT(_.expand(rhs)); //expand the term expression go get rid of quotients when possible | ||
var c = 0, //a counter to see if we have all terms with the variable | ||
l = rhs.length; | ||
//try to rewrite the whole thing | ||
if (rhs.group === CP && rhs.contains(solve_for) && rhs.isLinear()) { | ||
rhs.distributeMultiplier(); | ||
var t = new Symbol(0); | ||
//first bring all the terms containing the variable to the lhs | ||
rhs.each(function (x) { | ||
if (x.contains(solve_for)) { | ||
c++; | ||
t = _.add(t, x.clone()); | ||
} | ||
else | ||
lhs = _.subtract(lhs, x.clone()); | ||
}); | ||
rhs = t; | ||
//if not all the terms contain the variable so it's in the form | ||
//a*x^2+x | ||
if (c !== l) | ||
return rewrite(rhs, lhs); | ||
else { | ||
return [rhs, lhs]; | ||
} | ||
} | ||
else if (rhs.group === CB && rhs.contains(solve_for) && rhs.isLinear()) { | ||
if (rhs.multiplier.lessThan(0)) { | ||
rhs.multiplier = rhs.multiplier.multiply(new core.Frac(-1)); | ||
lhs.multiplier = lhs.multiplier.multiply(new core.Frac(-1)); | ||
} | ||
if (lhs.equals(0)) | ||
return new Symbol(0); | ||
else { | ||
var t = new Symbol(1); | ||
rhs.each(function (x) { | ||
if (x.contains(solve_for)) | ||
t = _.multiply(t, x.clone()); | ||
else | ||
lhs = _.divide(lhs, x.clone()); | ||
}); | ||
rhs = t; | ||
return rewrite(rhs, lhs); | ||
} | ||
} | ||
else if (!rhs.isLinear() && rhs.contains(solve_for)) { | ||
var p = _.parse(rhs.power.clone().invert()); | ||
rhs = _.pow(rhs, p.clone()); | ||
lhs = _.pow(_.expand(lhs), p.clone()); | ||
return rewrite(rhs, lhs); | ||
} | ||
else if (rhs.group === FN || rhs.group === S || rhs.group === PL) { | ||
return [rhs, lhs]; | ||
} | ||
}; | ||
//separate the equation | ||
@@ -837,8 +1090,12 @@ var separate = function (eq) { | ||
else if (deg === 2) { | ||
add_to_result(_.expand(quad.apply(undefined, coeffs))); | ||
coeffs.push('-'); | ||
add_to_result(_.expand(quad.apply(undefined, coeffs))); | ||
add_to_result(_.expand(__.quad.apply(undefined, coeffs))); | ||
} | ||
else if (deg === 3) | ||
add_to_result(cubic.apply(undefined, coeffs)); | ||
else if (deg === 3) { | ||
//first try to factor and solve | ||
var solutions = solve(core.Algebra.Factor.factor(eqns)); | ||
if(solutions.length > 0) | ||
add_to_result(solutions); | ||
else | ||
add_to_result(__.cubic.apply(undefined, coeffs)); | ||
} | ||
else { | ||
@@ -860,5 +1117,26 @@ /* | ||
else { | ||
//since it's not a polynomial then we'll try to look for a solution using Newton's method | ||
//this is not a very broad search but takes the positions that something is better than nothing | ||
attempt_Newton(eq); | ||
try { | ||
//Attempt Newton | ||
//since it's not a polynomial then we'll try to look for a solution using Newton's method | ||
//this is not a very broad search but takes the positions that something is better than nothing | ||
var has_trig = eq.hasTrig(); | ||
// we get all the points where a possible zero might exist. | ||
var points1 = __.getPoints(eq, 0.1); | ||
var points2 = __.getPoints(eq, 0.05); | ||
var points3 = __.getPoints(eq, 0.01); | ||
var points = core.Utils.arrayUnique(points1.concat(points2).concat(points3)), | ||
l = points.length; | ||
//compile the function and the derivative of the function | ||
var f = build(eq.clone()); | ||
var d = _C.diff(eq.clone()); | ||
var fp = build(d); | ||
for (var i = 0; i < l; i++) { | ||
var point = points[i]; | ||
add_to_result(__.Newton(point, f, fp), has_trig); | ||
} | ||
solutions.sort(); | ||
} | ||
catch(e) { | ||
; | ||
} | ||
} | ||
@@ -891,16 +1169,14 @@ } | ||
case 2: | ||
add_to_result(quad.apply(undefined, coeffs)); | ||
coeffs.push('-'); | ||
add_to_result(quad.apply(undefined, coeffs)); | ||
add_to_result(__.quad.apply(undefined, coeffs)); | ||
break; | ||
case 3: | ||
add_to_result(cubic.apply(undefined, coeffs)); | ||
add_to_result(__.cubic.apply(undefined, coeffs)); | ||
break; | ||
case 4: | ||
add_to_result(quartic.apply(undefined, coeffs)); | ||
add_to_result(__.quartic.apply(undefined, coeffs)); | ||
break; | ||
default: | ||
add_to_result(csolve(eq, solve_for)); | ||
add_to_result(__.csolve(eq, solve_for)); | ||
if (solutions.length === 0) | ||
add_to_result(divnconsolve(eq, solve_for)); | ||
add_to_result(__.divideAndConquer(eq, solve_for)); | ||
} | ||
@@ -914,3 +1190,3 @@ } | ||
try { | ||
var rw = rewrite(eq); | ||
var rw = __.rewrite(eq, null, solve_for); | ||
var lhs = rw[0]; | ||
@@ -920,24 +1196,38 @@ var rhs = rw[1]; | ||
if (lhs.fname === 'abs') { | ||
solutions.push(rhs.clone()); | ||
solutions.push(rhs.negate()); | ||
add_to_result([rhs.clone(), rhs.negate()]); | ||
} | ||
else if(lhs.fname === 'sin') { | ||
//asin | ||
solutions.push(inverse_function('asin', lhs, rhs)); | ||
add_to_result(inverse_function('asin', lhs, rhs)); | ||
} | ||
else if(lhs.fname === 'cos') { | ||
//asin | ||
solutions.push(inverse_function('acos', lhs, rhs)); | ||
add_to_result(inverse_function('acos', lhs, rhs)); | ||
} | ||
else if(lhs.fname === 'tan') { | ||
//asin | ||
solutions.push(inverse_function('atan', lhs, rhs)); | ||
add_to_result(inverse_function('atan', lhs, rhs)); | ||
} | ||
else if(lhs.fname === 'log') { | ||
//asin | ||
solutions.push(_.pow(new Symbol('e'), _.divide(rhs, _.parse(lhs.multiplier)))); | ||
//ax+b comes back as [a, x, ax, b]; | ||
var parts = explode(lhs.args[0], solve_for); | ||
//check if x is by itself | ||
var x = parts[1]; | ||
if(x.group === S) { | ||
rhs = _.divide(_.subtract(_.pow(new Symbol('e'), _.divide(rhs, _.parse(lhs.multiplier))), parts[3]), parts[0]); | ||
var eq = new Equation(x, rhs).toLHS(); | ||
add_to_result(solve(eq, solve_for)); | ||
} | ||
} | ||
else | ||
solutions.push(_.subtract(lhs, rhs)); | ||
add_to_result(_.subtract(lhs, rhs)); | ||
} | ||
else { | ||
var neq = new Equation(lhs, rhs).toLHS(); //create a new equation | ||
if(neq.equals(eq)) | ||
throw new Error('Stopping. No stop condition exists'); | ||
add_to_result(solve(neq, solve_for)); | ||
} | ||
} | ||
@@ -950,3 +1240,3 @@ catch (error) { | ||
if (eq.group === CB) | ||
solutions.push(0); | ||
add_to_result(0); | ||
else if (eq.group === CP) { | ||
@@ -961,3 +1251,3 @@ var separated = separate(eq); | ||
var p = lhs.power.clone().invert(); | ||
solutions.push(_.pow(rhs, p)); | ||
add_to_result(_.pow(rhs, p)); | ||
} | ||
@@ -981,32 +1271,4 @@ } | ||
}; | ||
core.Expression.prototype.solveFor = function (x) { | ||
return solve(core.Utils.isSymbol(this.symbol) ? this.symbol : this.symbol.toLHS(), x).map(function (x) { | ||
return new core.Expression(x); | ||
}); | ||
}; | ||
core.Expression.prototype.expand = function () { | ||
if (this.symbol instanceof Equation) { | ||
var clone = this.symbol.clone(); | ||
clone.RHS = _.expand(clone.RHS); | ||
clone.LHS = _.expand(clone.LHS); | ||
return new core.Expression(clone); | ||
} | ||
return new core.Expression(_.expand(this.symbol)); | ||
}; | ||
core.Expression.prototype.variables = function () { | ||
if (this.symbol instanceof Equation) | ||
return core.Utils.arrayUnique(variables(this.symbol.LHS).concat(variables(this.symbol.RHS))); | ||
return variables(this.symbol); | ||
}; | ||
var setEq = function (a, b) { | ||
return _.equals(a, b); | ||
}; | ||
//link the Equation class back to the core | ||
core.Equation = Equation; | ||
//Register the functions for external use | ||
nerdamer.register([ | ||
@@ -1013,0 +1275,0 @@ { |
@@ -0,0 +0,0 @@ /* global expect */ |
@@ -0,0 +0,0 @@ /* global expect */ |
@@ -257,2 +257,6 @@ /* global expect */ | ||
}, | ||
{ | ||
given: 'limit(cos(sin(x)+2), x, Infinity)', | ||
expected: '[cos(1),cos(3)]' | ||
}, | ||
/* | ||
@@ -259,0 +263,0 @@ { |
@@ -63,3 +63,3 @@ /* global expect */ | ||
given: 'solve(-5 sqrt(14)x-14x^2 sqrt(83)-10=0,x)', | ||
expected: '[(-1/28)*sqrt(-560*sqrt(83)+350)*sqrt(83)^(-1)+(-5/28)*sqrt(14)*sqrt(83)^(-1),(-5/28)*sqrt(14)*sqrt(83)^(-1)+(1/28)*sqrt(-560*sqrt(83)+350)*sqrt(83)^(-1)]' | ||
expected: '[(-1/28)*(5*sqrt(14)+sqrt(-560*sqrt(83)+350))*sqrt(83)^(-1),(-1/28)*(-sqrt(-560*sqrt(83)+350)+5*sqrt(14))*sqrt(83)^(-1)]' | ||
}, | ||
@@ -110,7 +110,4 @@ { | ||
given: 'solve(sqrt(97)x^2-sqrt(13)x+sqrt(14)x+sqrt(43)x^2+sqrt(3)*sqrt(101)=0,x)', | ||
expected: '[(-1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(14)+(1/2)*(sqrt(43)+sqrt(97))^(-1)'+ | ||
'*sqrt((-sqrt(13)+sqrt(14))^2-4*(sqrt(43)+sqrt(97))*sqrt(101)*sqrt(3))+(1/2)*'+ | ||
'(sqrt(43)+sqrt(97))^(-1)*sqrt(13),(-1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt((-sqrt(13)'+ | ||
'+sqrt(14))^2-4*(sqrt(43)+sqrt(97))*sqrt(101)*sqrt(3))+(-1/2)*(sqrt(43)+sqrt(97))^(-1)'+ | ||
'*sqrt(14)+(1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(13)]' | ||
expected: '[(1/2)*(-sqrt(14)+sqrt((-sqrt(13)+sqrt(14))^2-4*(sqrt(43)+sqrt(97))*sqrt(101)*sqrt(3))+sqrt(13))*(sqrt(43)+sqrt(97))^(-1),'+ | ||
'(1/2)*(-sqrt((-sqrt(13)+sqrt(14))^2-4*(sqrt(43)+sqrt(97))*sqrt(101)*sqrt(3))-sqrt(14)+sqrt(13))*(sqrt(43)+sqrt(97))^(-1)]' | ||
}, | ||
@@ -154,2 +151,10 @@ { | ||
expected: '[1]' | ||
}, | ||
{ | ||
given: 'solve((1/2)*sqrt(-4*x+4*y)-2+y, y)', | ||
expected: '[(-1/2)*(-5+sqrt(-4*x+9)),(-1/2)*(-5-sqrt(-4*x+9))]' | ||
}, | ||
{ | ||
given: 'solve(log(a*x-c)-b=21, x)', | ||
expected: '[-(-c-e^(21+b))*a^(-1)]' | ||
} | ||
@@ -186,2 +191,15 @@ ]; | ||
expected: '(1/2)*(1+sqrt(-15-4*y)),(1/2)*(-sqrt(-15-4*y)+1)' | ||
}, | ||
//non-linear systems | ||
{ | ||
given: ['x+y=3','y^3-x=7'], | ||
expected: 'x,1,y,2' | ||
}, | ||
{ | ||
given: ['x^2+y=3','x+y+z=6', 'z^2-y=7'], | ||
expected: 'x,1,y,2,z,3' | ||
}, | ||
{ | ||
given: ['x*y-cos(z)=-3', '3*z^3-y^2+1=12', '3*sin(x)*cos(y)-x^3=-4'], | ||
expected: 'x,1.10523895006979,y,-2.98980336936266,z,1.88015428627437' | ||
} | ||
@@ -188,0 +206,0 @@ |
@@ -0,0 +0,0 @@ /* global expect */ |
@@ -0,0 +0,0 @@ 'use strict'; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1204896
23698