Repository: josdejong/mathjs
Pull request #1001 (2018-01-02 06:04:39)

Added rule to simplify that allows for combining of like terms in embedded quantities
Currently, expressions such as `10 - (x - 2)` and `x - (y + x)` are not at all simplified. The reason why is the outer negation turns into a multiply by `-1`, but this blocks the flattening of the entire expression, so the outer and inner like terms never get a chance to combine.

This pull request adds an additional rule to `simplify` that allows for this flattening to take place by distributing the `-1` factor over the inner terms. While this does fix the cases above (simplifying them to `12 - x` and `-y`, respectively) and related expressions, it does not help with expressions such as `10 - 5 * (x - 2)`. However, the more general cases (such as this one) are more difficult, since we cannot guarantee that these transformations will be useful (consider `10 + (y + 2) * (y - 2)`, should this be multiplied out or not?). As such, for now I think the rule specifically for negatives added in this pull request is a good improvement for now, and at some later point in time we can have a discussion about what behavior we want for `simplify` in such cases.

Also, sorry for all the whitespace changes! My editor makes them automatically.


Patch:
diff --git a/HISTORY.md b/HISTORY.md
index 48d631b24..05211f337 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,11 +1,5 @@
 # History
 
-## not yet released, version 3.19.0
-
-- Extended functions `distance` and `intersect` with support for BigNumbers.
-  Thanks @ovk. 
-
-
 ## 2017-12-28, version 3.18.1
 
 - Fixed #998: An issue with simplifying an expression containing a subtraction.
diff --git a/README.md b/README.md
index 7c6ef3c7a..2f4edccf6 100644
--- a/README.md
+++ b/README.md
@@ -25,16 +25,12 @@ Math.js is an extensive math library for JavaScript and Node.js. It features a f
 
 ## Usage
 
-Math.js can be used in both node.js and in the browser.
-
-Install math.js using [npm](https://www.npmjs.com/package/mathjs):
+Math.js can be installed using npm or bower, or by [downloading](http://mathjs.org/download.html) the library.
+The library can be used in both node.js and in the browser.
+See the [Getting Started](http://mathjs.org/docs/getting_started.html) for a more detailed tutorial. To install math.js using npm:
 
     npm install mathjs
 
-Or download mathjs via one of the CDN's listed on the downloads page: 
-
-&nbsp;&nbsp;&nbsp;&nbsp;[http://mathjs.org/download.html](http://mathjs.org/download.html#download)
-
 Math.js can be used similar to JavaScript's built-in Math library. Besides that,
 math.js can evaluate
 [expressions](http://mathjs.org/docs/expressions.html)
@@ -67,9 +63,6 @@ math.chain(3)
     .done(); // 14
 ```
 
-See the [Getting Started](http://mathjs.org/docs/getting_started.html) for a more detailed tutorial. 
-
-
 ## Browser support
 
 Math.js works on any ES5 compatible JavaScript engine: node.js 0.10, and Internet Explorer 9 and newer, and all other browsers (Chrome, Firefox, Safari). If support for old browsers like Internet Explorer 8 is required, the [es5-shim](https://github.com/kriskowal/es5-shim) library has to be loaded.
diff --git a/lib/function/algebra/simplify.js b/lib/function/algebra/simplify.js
index cf018bc8e..5152bcfb6 100644
--- a/lib/function/algebra/simplify.js
+++ b/lib/function/algebra/simplify.js
@@ -210,6 +210,9 @@ function factory (type, config, load, typed, math) {
     { l: 'n1*n2 + n2', r: '(n1+1)*n2' },
     { l: 'n1*n3 + n2*n3', r: '(n1+n2)*n3' },
 
+    // remove parenthesis in the case of negating a quantitiy
+    { l: 'n1 + -1 * (n2 + n3)', r:'n1 + -1 * n2 + -1 * n3' },
+
     simplifyConstant,
 
     { l: '(-n)*n1', r: '-(n*n1)' }, // make factors positive (and undo 'make non-constant terms positive')
@@ -227,8 +230,8 @@ function factory (type, config, load, typed, math) {
 
     { l: 'n*(n1/n2)', r:'(n*n1)/n2' }, // '*' before '/'
     { l: 'n-(n1+n2)', r:'n-n1-n2' }, // '-' before '+'
-    // { l: '(n1/n2)/n3', r: 'n1/(n2*n3)' }, 
-    // { l: '(n*n1)/(n*n2)', r: 'n1/n2' }, 
+    // { l: '(n1/n2)/n3', r: 'n1/(n2*n3)' },
+    // { l: '(n*n1)/(n*n2)', r: 'n1/n2' },
 
     { l: '1*n', r: 'n' } // this pattern can be produced by simplifyConstant
 
@@ -353,7 +356,7 @@ function factory (type, config, load, typed, math) {
 
         // Create a new node by cloning the rhs of the matched rule
         res = repl.clone();
-     
+
         // Replace placeholders with their respective nodes without traversing deeper into the replaced nodes
         var _transform = function(node) {
           if(node.isSymbolNode && matches.placeholders.hasOwnProperty(node.name)) {
@@ -363,9 +366,9 @@ function factory (type, config, load, typed, math) {
             return node.map(_transform);
           }
         }
-        
+
         res = _transform(res);
-        
+
         // var after = res.toString({parenthesis: 'all'});
         // console.log('Simplified ' + before + ' to ' + after);
       }
diff --git a/lib/function/geometry/distance.js b/lib/function/geometry/distance.js
index 9e95ef798..1826191fb 100644
--- a/lib/function/geometry/distance.js
+++ b/lib/function/geometry/distance.js
@@ -2,13 +2,6 @@
 
 function factory (type, config, load, typed) {
   var matrix = load(require('../../type/matrix/function/matrix'));
-  var add = load(require('../arithmetic/addScalar'));
-  var subtract = load(require('../arithmetic/subtract'));
-  var multiply = load(require('../arithmetic/multiplyScalar'));
-  var divide = load(require('../arithmetic/divideScalar'));
-  var negate = load(require('../arithmetic/unaryMinus'));
-  var sqrt = load(require('../arithmetic/sqrt'));
-  var abs = load(require('../arithmetic/abs'));
 
   /**
     * Calculates:
@@ -66,12 +59,12 @@ function factory (type, config, load, typed) {
     'Array, Array, Array': function(x, y, z){
       // Point to Line 2D; (x=Point, y=LinePoint1, z=LinePoint2)
       if (x.length == 2 && y.length == 2 && z.length == 2){
-        if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument'); }
-        if (!_2d(y)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument'); }
-        if (!_2d(z)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for third argument'); }
-        var m = divide(subtract(z[1], z[0]), subtract(y[1], y[0]));
-        var xCoeff = multiply(multiply(m, m), y[0]);
-        var yCoeff = negate(multiply(m, y[0]));
+        if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
+        if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
+        if (!_2d(z)) { throw new TypeError('Array with 2 numbers expected for third argument'); }
+        var m = (z[1]-z[0])/(y[1]-y[0]);
+        var xCoeff = m*m*y[0];
+        var yCoeff = -1*(m*y[0]);
         var constant = x[1];
 
         return _distancePointLine2D(x[0], x[1], xCoeff, yCoeff, constant);
@@ -82,14 +75,14 @@ function factory (type, config, load, typed) {
     },
     'Object, Object, Object': function(x, y, z){
       if (Object.keys(x).length == 2 && Object.keys(y).length == 2 && Object.keys(z).length == 2){
-        if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers or BigNumbers'); }
-        if (!_2d(y)) { throw new TypeError('Values of lineOnePtX and lineOnePtY should be numbers or BigNumbers'); }
-        if (!_2d(z)) { throw new TypeError('Values of lineTwoPtX and lineTwoPtY should be numbers or BigNumbers'); }
+        if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers'); }
+        if (!_2d(y)) { throw new TypeError('Values of lineOnePtX and lineOnePtY should be numbers'); }
+        if (!_2d(z)) { throw new TypeError('Values of lineTwoPtX and lineTwoPtY should be numbers'); }
         if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('lineOnePtX') &&
           y.hasOwnProperty('lineOnePtY') && z.hasOwnProperty('lineTwoPtX') && z.hasOwnProperty('lineTwoPtY')){
-          var m = divide(subtract(z.lineTwoPtY, z.lineTwoPtX), subtract(y.lineOnePtY, y.lineOnePtX));
-          var xCoeff = multiply(multiply(m, m), y.lineOnePtX);
-          var yCoeff = negate(multiply(m, y.lineOnePtX));
+          var m = (z.lineTwoPtY-z.lineTwoPtX)/(y.lineOnePtY-y.lineOnePtX);
+          var xCoeff = m*m*y.lineOnePtX;
+          var yCoeff = -1*(m*y.lineOnePtX);
           var constant = x.pointX;
 
           return _distancePointLine2D(x.pointX, x.pointY, xCoeff, yCoeff, constant);
@@ -105,29 +98,29 @@ function factory (type, config, load, typed) {
     'Array, Array': function(x, y){
       // Point to Line 2D; (x=[pointX, pointY], y=[x-coeff, y-coeff, const])
       if (x.length == 2 && y.length == 3){
-        if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument'); }
-        if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument'); }
+        if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
+        if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
 
         return _distancePointLine2D(x[0], x[1], y[0], y[1], y[2]);
       }
       // Point to Line 3D
       else if (x.length == 3 && y.length == 6){
-        if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument'); }
-        if (!_parametricLine(y)) { throw new TypeError('Array with 6 numbers or BigNumbers expected for second argument'); }
+        if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
+        if (!_parametricLine(y)) { throw new TypeError('Array with 6 numbers expected for second argument'); }
 
         return _distancePointLine3D(x[0], x[1], x[2], y[0], y[1], y[2], y[3], y[4], y[5]);
       }
       // Point to Point 2D
       else if (x.length == 2 && y.length == 2){
-        if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument'); }
-        if (!_2d(y)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument'); }
+        if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
+        if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
 
         return _distance2d(x[0], x[1], y[0], y[1]);
       }
       // Point to Point 3D
       else if(x.length == 3 && y.length == 3){
-        if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument'); }
-        if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument'); }
+        if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
+        if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
 
         return _distance3d(x[0], x[1], x[2], y[0], y[1], y[2]);
       }
@@ -137,10 +130,10 @@ function factory (type, config, load, typed) {
     },
     'Object, Object': function(x, y){
       if (Object.keys(x).length == 2 && Object.keys(y).length == 3){
-        if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers or BigNumbers'); }
-        if (!_3d(y)) { throw new TypeError('Values of xCoeffLine, yCoeffLine and constant should be numbers or BigNumbers'); }
+        if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers'); }
+        if (!_3d(y)) { throw new TypeError('Values of xCoeffLine, yCoeffLine and constant should be numbers'); }
         if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('xCoeffLine') &&
-          y.hasOwnProperty('yCoeffLine') && y.hasOwnProperty('constant')){
+          y.hasOwnProperty('yCoeffLine') && y.hasOwnProperty('yCoeffLine')){
 
           return _distancePointLine2D(x.pointX, x.pointY, y.xCoeffLine, y.yCoeffLine, y.constant);
         }
@@ -150,8 +143,8 @@ function factory (type, config, load, typed) {
       }
       // Point to Line 3D
       else if (Object.keys(x).length == 3 && Object.keys(y).length == 6){
-        if (!_3d(x)) { throw new TypeError('Values of pointX, pointY and pointZ should be numbers or BigNumbers'); }
-        if (!_parametricLine(y)) { throw new TypeError('Values of x0, y0, z0, a, b and c should be numbers or BigNumbers'); }
+        if (!_3d(x)) { throw new TypeError('Values of pointX, pointY and pointZ should be numbers'); }
+        if (!_parametricLine(y)) { throw new TypeError('Values of x0, y0, z0, a, b and c should be numbers'); }
         if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('x0') &&
           y.hasOwnProperty('y0') && y.hasOwnProperty('z0') && y.hasOwnProperty('a') &&
           y.hasOwnProperty('b') && y.hasOwnProperty('c')){
@@ -164,8 +157,8 @@ function factory (type, config, load, typed) {
       }
       // Point to Point 2D
       else if (Object.keys(x).length == 2 && Object.keys(y).length == 2){
-        if (!_2d(x)) { throw new TypeError('Values of pointOneX and pointOneY should be numbers or BigNumbers'); }
-        if (!_2d(y)) { throw new TypeError('Values of pointTwoX and pointTwoY should be numbers or BigNumbers'); }
+        if (!_2d(x)) { throw new TypeError('Values of pointOneX and pointOneY should be numbers'); }
+        if (!_2d(y)) { throw new TypeError('Values of pointTwoX and pointTwoY should be numbers'); }
         if (x.hasOwnProperty('pointOneX') && x.hasOwnProperty('pointOneY') &&
           y.hasOwnProperty('pointTwoX') && y.hasOwnProperty('pointTwoY')){
 
@@ -177,8 +170,8 @@ function factory (type, config, load, typed) {
       }
       // Point to Point 3D
       else if(Object.keys(x).length == 3 && Object.keys(y).length == 3){
-        if (!_3d(x)) { throw new TypeError('Values of pointOneX, pointOneY and pointOneZ should be numbers or BigNumbers'); }
-        if (!_3d(y)) { throw new TypeError('Values of pointTwoX, pointTwoY and pointTwoZ should be numbers or BigNumbers'); }
+        if (!_3d(x)) { throw new TypeError('Values of pointOneX, pointOneY and pointOneZ should be numbers'); }
+        if (!_3d(y)) { throw new TypeError('Values of pointTwoX, pointTwoY and pointTwoZ should be numbers'); }
         if (x.hasOwnProperty('pointOneX') && x.hasOwnProperty('pointOneY') && x.hasOwnProperty('pointOneZ') &&
           y.hasOwnProperty('pointTwoX') && y.hasOwnProperty('pointTwoY') && y.hasOwnProperty('pointTwoZ')){
 
@@ -198,117 +191,110 @@ function factory (type, config, load, typed) {
       return _distancePairwise(arr);
     }
   });
+  return distance;
+}
 
-  function _isNumber(a) {
-    // distance supports numbers and bignumbers
-    return (typeof a === 'number' || type.isBigNumber(a));
+function _2d(a){
+  // checks if the number of arguments are correct in count and are valid (should be numbers)
+  if (a.constructor !== Array){
+    a = _objectToArray(a);
   }
+  return typeof a[0] === 'number' && typeof a[1] === 'number';
+}
 
-  function _2d(a){
-    // checks if the number of arguments are correct in count and are valid (should be numbers)
-    if (a.constructor !== Array){
-      a = _objectToArray(a);
-    }
-    return _isNumber(a[0]) && _isNumber(a[1]);
+function _3d(a){
+  // checks if the number of arguments are correct in count and are valid (should be numbers)
+  if (a.constructor !== Array){
+    a = _objectToArray(a);
   }
+  return typeof a[0] === 'number' && typeof a[1] === 'number' && typeof a[2] === 'number';
+}
 
-  function _3d(a){
-    // checks if the number of arguments are correct in count and are valid (should be numbers)
-    if (a.constructor !== Array){
-      a = _objectToArray(a);
-    }
-    return _isNumber(a[0]) && _isNumber(a[1]) &&  _isNumber(a[2]);
+function _parametricLine(a){
+  if (a.constructor !== Array){
+    a = _objectToArray(a);
   }
+  return typeof a[0] === 'number' && typeof a[1] === 'number' && typeof a[2] === 'number' &&
+    typeof a[3] === 'number' && typeof a[4] === 'number' && typeof a[5] === 'number';
 
-  function _parametricLine(a){
-    if (a.constructor !== Array){
-      a = _objectToArray(a);
-    }
-    return _isNumber(a[0]) && _isNumber(a[1]) && _isNumber(a[2]) &&
-      _isNumber(a[3]) && _isNumber(a[4]) && _isNumber(a[5]);
-  }
+}
 
-  function _objectToArray(o){
-    var keys = Object.keys(o);
-    var a = [];
-    for (var i = 0; i < keys.length; i++) {
-      a.push(o[keys[i]]);
-    }
-    return a;
+function _objectToArray(o){
+  var keys = Object.keys(o);
+  var a = [];
+  for (var i = 0; i < keys.length; i++) {
+    a.push(o[keys[i]]);
   }
+  return a;
+}
 
-  function _pairwise(a){
-    //checks for valid arguments passed to _distancePairwise(Array)
-    if (a[0].length == 2 && _isNumber(a[0][0]) && _isNumber(a[0][1])){
-      for(var i in a){
-        if (a[i].length != 2 || !_isNumber(a[i][0]) || !_isNumber(a[i][1])){
-          return false;
-        }
+function _pairwise(a){
+  //checks for valid arguments passed to _distancePairwise(Array)
+  if (a[0].length == 2 && typeof a[0][0] === 'number' && typeof a[0][1] === 'number'){
+    for(var i in a){
+      if (a[i].length != 2 || typeof a[i][0] !== 'number' || typeof a[i][1] !== 'number'){
+        return false;
       }
     }
-    else if (a[0].length == 3 && _isNumber(a[0][0]) && _isNumber(a[0][1]) && _isNumber(a[0][2])){
-      for(var i in a){
-        if (a[i].length != 3 || !_isNumber(a[i][0]) || !_isNumber(a[i][1]) || !_isNumber(a[i][2])){
-          return false;
-        }
+  }
+  else if (a[0].length == 3 && typeof a[0][0] === 'number' && typeof a[0][1] === 'number' && typeof a[0][2] === 'number'){
+    for(var i in a){
+      if (a[i].length != 3 || typeof a[i][0] !== 'number' || typeof a[i][1] !== 'number' || typeof a[i][2] !== 'number'){
+        return false;
       }
     }
-    else{
-      return false;
-    }
-    return true;
   }
-
-  function _distancePointLine2D(x, y, a, b, c){
-    var num =  abs(add(add(multiply(a, x), multiply(b, y)), c));
-    var den = sqrt(add(multiply(a, a), multiply(b, b)));
-    var result = divide(num, den);
-    return result;
+  else{
+    return false;
   }
+  return true;
+}
 
-  function _distancePointLine3D(x, y, z, x0, y0, z0, a, b, c){
-    var num = [ subtract(multiply(subtract(y0, y), c), multiply(subtract(z0, z), b)),
-                subtract(multiply(subtract(z0, z), a), multiply(subtract(x0, x), c)),
-                subtract(multiply(subtract(x0, x), b), multiply(subtract(y0, y), a)) ];
-    num = sqrt(add(add(multiply(num[0], num[0]), multiply(num[1], num[1])), multiply(num[2], num[2])));
-    var den = sqrt(add(add(multiply(a, a), multiply(b, b)), multiply(c, c)));
-    var result = divide(num, den);
-    return result;
-  }
+function _distancePointLine2D(x, y, a, b, c){
+  var num =  Math.abs(a*x + b*y + c);
+  var den = Math.pow((a*a + b*b), 0.5);
+  var result = (num/den);
+  return result;
+}
 
-  function _distance2d(x1, y1, x2, y2){
-    var yDiff = subtract(y2, y1);
-    var xDiff = subtract(x2, x1);
-    var radicant = add(multiply(yDiff, yDiff), multiply(xDiff, xDiff));
-    var result = sqrt(radicant);
-    return result;
-  }
+function _distancePointLine3D(x, y, z, x0, y0, z0, a, b, c){
+  var num = [((y0-y)*(c))-((z0-z)*(b)), ((z0-z)*(a))-((x0-x)*(c)), ((x0-x)*(b))-((y0-y)*(a))]
+  num = Math.pow(num[0]*num[0] + num[1]*num[1] + num[2]*num[2], 0.5);
+  var den = Math.pow(a*a + b*b + c*c, 0.5);
+  var result = num/den;
+  return result;
+}
 
-  function _distance3d(x1, y1, z1, x2, y2, z2){
-    var zDiff = subtract(z2, z1);
-    var yDiff = subtract(y2, y1);
-    var xDiff = subtract(x2, x1);
-    var radicant = add(add(multiply(zDiff, zDiff), multiply(yDiff, yDiff)), multiply(xDiff, xDiff));
-    var result = sqrt(radicant);
-    return result;
-  }
+function _distance2d(x1, y1, x2, y2){
+  var yDiff = y2 - y1;
+  var xDiff = x2 - x1;
+  var radicant = yDiff * yDiff + xDiff * xDiff;
+  var result = Math.pow(radicant, 0.5);
+  return result;
+}
 
-  function _distancePairwise(a){
-    var result = [];
-    for(var i = 0; i < a.length-1; i++){
-      for(var j = i+1; j < a.length; j++){
-        if (a[0].length == 2){
-          result.push(_distance2d(a[i][0], a[i][1], a[j][0], a[j][1]));
-        }
-        else if (a[0].length == 3){
-          result.push(_distance3d(a[i][0], a[i][1], a[i][2], a[j][0], a[j][1], a[j][2]));
-        }
+function _distance3d(x1, y1, z1, x2, y2, z2){
+  var zDiff = z2 - z1;
+  var yDiff = y2 - y1;
+  var xDiff = x2 - x1;
+  var radicant = zDiff * zDiff + yDiff * yDiff + xDiff * xDiff;
+  var result = Math.pow(radicant, 0.5);
+  return result;
+}
+
+function _distancePairwise(a){
+  var result = [];
+  for(var i = 0; i < a.length-1; i++){
+    for(var j = i+1; j < a.length; j++){
+      if (a[0].length == 2){
+        result.push(_distance2d(a[i][0], a[i][1], a[j][0], a[j][1]));
+      }
+      else if (a[0].length == 3){
+        result.push(_distance3d(a[i][0], a[i][1], a[i][2], a[j][0], a[j][1], a[j][2]));
       }
     }
-    return result;
   }
-
-  return distance;
+  return result;
 }
 
 exports.name = 'distance';
diff --git a/lib/function/geometry/intersect.js b/lib/function/geometry/intersect.js
index 2fcc4fbe4..ad2be7e55 100644
--- a/lib/function/geometry/intersect.js
+++ b/lib/function/geometry/intersect.js
@@ -4,14 +4,9 @@ function factory (type, config, load, typed) {
 
   var abs = load(require('../arithmetic/abs'));
   var add = load(require('../arithmetic/add'));
-  var addScalar = load(require('../arithmetic/addScalar'));
   var matrix = load(require('../../type/matrix/function/matrix'));
   var multiply = load(require('../arithmetic/multiply'));
-  var multiplyScalar = load(require('../arithmetic/multiplyScalar'));
-  var divideScalar = load(require('../arithmetic/divideScalar'));
   var subtract = load(require('../arithmetic/subtract'));
-  var smaller = load(require('../relational/smaller'));
-  var equalScalar = load(require('../relational/equalScalar'));
 
   /**
    * Calculates the point of intersection of two lines in two or three dimensions
@@ -42,8 +37,8 @@ function factory (type, config, load, typed) {
    */
   var intersect = typed('intersect', {
     'Array, Array, Array': function (x, y, plane) {
-      if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument'); }
-      if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument'); }
+      if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
+      if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
       if (!_4d(plane)) { throw new TypeError('Array with 4 numbers expected as third argument'); }
 
       return _intersectLinePlane(x[0], x[1], x[2], y[0], y[1], y[2], plane[0], plane[1], plane[2], plane[3]);
@@ -51,18 +46,18 @@ function factory (type, config, load, typed) {
 
     'Array, Array, Array, Array': function (w, x, y, z) {
       if (w.length === 2) {
-        if (!_2d(w)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument'); }
-        if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument'); }
-        if (!_2d(y)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for third argument'); }
-        if (!_2d(z)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for fourth argument'); }
+        if (!_2d(w)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
+        if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
+        if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for third argument'); }
+        if (!_2d(z)) { throw new TypeError('Array with 2 numbers expected for fourth argument'); }
 
         return _intersect2d(w, x, y, z);
       }
       else if (w.length === 3) {
-        if (!_3d(w)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument'); }
-        if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument'); }
-        if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for third argument'); }
-        if (!_3d(z)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for fourth argument'); }
+        if (!_3d(w)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
+        if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
+        if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for third argument'); }
+        if (!_3d(z)) { throw new TypeError('Array with 3 numbers expected for fourth argument'); }
 
         return _intersect3d(w[0], w[1], w[2], x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]);
       }
@@ -81,21 +76,16 @@ function factory (type, config, load, typed) {
     }
   });
 
-  function _isNumber(a) {
-    // intersect supports numbers and bignumbers
-    return (typeof a === 'number' || type.isBigNumber(a));
-  }
-
   function _2d(x) {
-    return x.length === 2 && _isNumber(x[0]) && _isNumber(x[1]);
+    return x.length === 2 && typeof x[0] === 'number' && typeof x[1] === 'number';
   }
 
   function _3d(x) {
-    return x.length === 3 && _isNumber(x[0]) && _isNumber(x[1]) && _isNumber(x[2]);
+    return x.length === 3 && typeof x[0] === 'number' && typeof x[1] === 'number' && typeof x[2] === 'number';
   }
 
   function _4d(x) {
-    return x.length === 4 && _isNumber(x[0]) && _isNumber(x[1]) && _isNumber(x[2]) && _isNumber(x[3]);
+    return x.length === 4 && typeof x[0] === 'number' && typeof x[1] === 'number' && typeof x[2] === 'number' && typeof x[3] === 'number';
   }
 
   function _intersect2d(p1a, p1b, p2a, p2b){
@@ -103,44 +93,30 @@ function factory (type, config, load, typed) {
     var o2 = p2a;
     var d1 = subtract(o1, p1b);
     var d2 = subtract(o2, p2b);
-    var det = subtract(multiplyScalar(d1[0], d2[1]), multiplyScalar(d2[0], d1[1]));
-    if (smaller(abs(det), config.epsilon)) {
+    var det = d1[0]*d2[1] - d2[0]*d1[1];
+    if (abs(det) < config.epsilon) {
       return null;
     }
-    var d20o11 = multiplyScalar(d2[0], o1[1]);
-    var d21o10 = multiplyScalar(d2[1], o1[0]);
-    var d20o21 = multiplyScalar(d2[0], o2[1]);
-    var d21o20 = multiplyScalar(d2[1], o2[0]);
-    var t = divideScalar(addScalar(subtract(subtract(d20o11, d21o10), d20o21), d21o20), det);
-    return add(multiply(d1, t), o1);
-  }
-
-  function _intersect3dHelper(a, b, c, d, e, f, g, h, i, j, k, l){
-      // (a - b)*(c - d) + (e - f)*(g - h) + (i - j)*(k - l)
-      var add1 = multiplyScalar(subtract(a, b), subtract(c, d));
-      var add2 = multiplyScalar(subtract(e, f), subtract(g, h));
-      var add3 = multiplyScalar(subtract(i, j), subtract(k, l));
-      return addScalar(addScalar(add1, add2), add3);
+    var t = (d2[0]*o1[1] - d2[1]*o1[0] - d2[0]*o2[1] + d2[1]*o2[0]) / det;
+    return add(multiply(d1, t), o1); 
   }
 
   function _intersect3d(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4){
-    var d1343 = _intersect3dHelper(x1, x3, x4, x3, y1, y3, y4, y3, z1, z3, z4, z3);
-    var d4321 = _intersect3dHelper(x4, x3, x2, x1, y4, y3, y2, y1, z4, z3, z2, z1);
-    var d1321 = _intersect3dHelper(x1, x3, x2, x1, y1, y3, y2, y1, z1, z3, z2, z1);
-    var d4343 = _intersect3dHelper(x4, x3, x4, x3, y4, y3, y4, y3, z4, z3, z4, z3);
-    var d2121 = _intersect3dHelper(x2, x1, x2, x1, y2, y1, y2, y1, z2, z1, z2, z1);
-    var ta = divideScalar(
-      subtract(multiplyScalar(d1343, d4321), multiplyScalar(d1321, d4343)),
-      subtract(multiplyScalar(d2121, d4343), multiplyScalar(d4321, d4321)));
-    var tb = divideScalar(addScalar(d1343, multiplyScalar(ta, d4321)), d4343);
-
-    var pax = addScalar(x1, multiplyScalar(ta, subtract(x2, x1)));
-    var pay = addScalar(y1, multiplyScalar(ta, subtract(y2, y1)));
-    var paz = addScalar(z1, multiplyScalar(ta, subtract(z2, z1)));
-    var pbx = addScalar(x3, multiplyScalar(tb, subtract(x4, x3)));
-    var pby = addScalar(y3, multiplyScalar(tb, subtract(y4, y3)));
-    var pbz = addScalar(z3, multiplyScalar(tb, subtract(z4, z3)));
-    if (equalScalar(pax, pbx) && equalScalar(pay, pby) && equalScalar(paz, pbz)){
+    var d1343 = (x1 - x3)*(x4 - x3) + (y1 - y3)*(y4 - y3) + (z1 - z3)*(z4 - z3);
+    var d4321 = (x4 - x3)*(x2 - x1) + (y4 - y3)*(y2 - y1) + (z4 - z3)*(z2 - z1);
+    var d1321 = (x1 - x3)*(x2 - x1) + (y1 - y3)*(y2 - y1) + (z1 - z3)*(z2 - z1);
+    var d4343 = (x4 - x3)*(x4 - x3) + (y4 - y3)*(y4 - y3) + (z4 - z3)*(z4 - z3);
+    var d2121 = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1) + (z2 - z1)*(z2 - z1);
+    var ta = ( d1343*d4321 - d1321*d4343 ) / ( d2121*d4343 - d4321*d4321 );
+    var tb = ( d1343 + ta * d4321 ) / (d4343);
+
+    var pax = x1 + ta * (x2 - x1);
+    var pay = y1 + ta * (y2 - y1);
+    var paz = z1 + ta * (z2 - z1);
+    var pbx = x3 + tb * (x4 - x3);
+    var pby = y3 + tb * (y4 - y3);
+    var pbz = z3 + tb * (z4 - z3);
+    if (pax === pbx && pay === pby && paz === pbz){
       return [pax, pay, paz];
     }
     else{
@@ -149,18 +125,10 @@ function factory (type, config, load, typed) {
   }
 
   function _intersectLinePlane(x1, y1, z1, x2, y2, z2, x, y, z, c){
-    var x1x = multiplyScalar(x1, x);
-    var x2x = multiplyScalar(x2, x);
-    var y1y = multiplyScalar(y1, y);
-    var y2y = multiplyScalar(y2, y);
-    var z1z = multiplyScalar(z1, z);
-    var z2z = multiplyScalar(z2, z);
-    var t = divideScalar(
-      subtract(subtract(subtract(c, x1x), y1y), z1z),
-      subtract(subtract(subtract(addScalar(addScalar(x2x, y2y), z2z), x1x), y1y), z1z));
-    var px = addScalar(x1, multiplyScalar(t, subtract(x2, x1)));
-    var py = addScalar(y1, multiplyScalar(t, subtract(y2, y1)));
-    var pz = addScalar(z1, multiplyScalar(t, subtract(z2, z1)));
+    var t = (c - x1*x - y1*y - z1*z)/(x2*x + y2*y + z2*z - x1*x - y1*y - z1*z);
+    var px = x1 + t * (x2 - x1);
+    var py = y1 + t * (y2 - y1);
+    var pz = z1 + t * (z2 - z1);
     return [px, py, pz];
     // TODO: Add cases when line is parallel to the plane:
     //       (a) no intersection,
diff --git a/package.json b/package.json
index 00ed21456..999fe92df 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,6 @@
     "Mitchel Kelonye (https://github.com/kelonye)",
     "mtraynham (https://github.com/mtraynham)",
     "Niels Heisterkamp (https://github.com/nheisterkamp)",
-    "Oleg (https://github.com/ovk)",
     "Owen Versteeg (https://github.com/owenversteeg)",
     "Pavel Panchekha (https://github.com/pavpanchekha)",
     "Pat Grasso (https://github.com/patgrasso)",


Test patch:
diff --git a/test/function/algebra/simplify.test.js b/test/function/algebra/simplify.test.js
index 2bba34929..366961752 100644
--- a/test/function/algebra/simplify.test.js
+++ b/test/function/algebra/simplify.test.js
@@ -166,6 +166,12 @@ describe('simplify', function() {
     simplifyAndCompare('x-1-2x+2', '1-x');
   });
 
+  it('should collect like terms that are embedded in other terms', function() {
+    simplifyAndCompare('10 - (x - 2)', '12 - x');
+    simplifyAndCompare('x - (y + x)', '-y');
+    simplifyAndCompare('x - (y - (y - x))', '0');
+  });
+
   it('should collect separated like factors', function() {
     simplifyAndCompare('x*y*-x/(x^2)', '-y');
     simplifyAndCompare('x/2*x', 'x^2/2');
@@ -228,7 +234,7 @@ describe('simplify', function() {
 
   it('resolve() should substitute scoped constants', function() {
     assert.equal(
-        math.simplify.resolve(math.parse('x+y'), {x:1}).toString(), 
+        math.simplify.resolve(math.parse('x+y'), {x:1}).toString(),
         "1 + y"
     ); // direct
     simplifyAndCompare('x+y', 'x+y', {}); // operator
@@ -242,7 +248,7 @@ describe('simplify', function() {
     simplifyAndCompare('x+y', '6', {x:2,y:math.parse("x+x")});
     simplifyAndCompare('x+(y+2-1-1)', '6', {x:2,y:math.parse("x+x")}); // parentheses
     simplifyAndCompare('log(x+y)', String(Math.log(6)), {x:2,y:math.parse("x+x")}); // function
-    simplifyAndCompare('combinations( ceil(abs(sin(x)) * y), abs(x) )', 
+    simplifyAndCompare('combinations( ceil(abs(sin(x)) * y), abs(x) )',
         'combinations(ceil(0.9092974268256817 * y ), 2)', {x:-2});
 
     // TODO(deal with accessor nodes) simplifyAndCompare('size(text)[1]', '11', {text: "hello world"})
diff --git a/test/function/geometry/distance.test.js b/test/function/geometry/distance.test.js
index 51be58e7b..e4be63687 100644
--- a/test/function/geometry/distance.test.js
+++ b/test/function/geometry/distance.test.js
@@ -89,27 +89,4 @@ describe('distance', function() {
     assert.deepEqual(math.distance({pointX: 2, pointY: 3, pointZ: 1}, {x0: 1, y0: 1, z0: 2, a: 5, b: 0, c: 1}), 2.3204774044612857);
   });
 
-  it('should calculate the distance if coordinates are bignumbers', function() {
-    var bigmath = math.create({number: 'BigNumber', precision: 32});
-    var bigdistance = bigmath.distance;
-    var bignumber = bigmath.bignumber;
-
-    assert.deepEqual(bigmath.eval('distance([1, 2], [4, 6])'), bignumber(5));
-    assert.deepEqual(bigdistance([bignumber(1), bignumber(2)], [bignumber(4), bignumber(6)]), bignumber(5));
-    assert.deepEqual(bigdistance([bignumber(1), 2], [4, bignumber(6)]), bignumber(5));
-    assert.deepEqual(bigmath.eval('distance([3, 5], [10, 1])'), bignumber('8.0622577482985496523666132303038'));
-    assert.deepEqual(bigmath.eval('distance([1, 2, 3], [-2, -3, -4])'), bignumber('9.1104335791442988819456261046887'));
-    assert.deepEqual(bigmath.eval('distance({pointOneX: 0, pointOneY: 0}, {pointTwoX: 10, pointTwoY: 10})'), bignumber('14.142135623730950488016887242097'));
-    assert.deepEqual(bigmath.eval('distance({pointOneX: 4, pointOneY: 5, pointOneZ: 8}, {pointTwoX: 2, pointTwoY: 7, pointTwoZ: 10})'), bignumber('3.4641016151377545870548926830117'));
-    assert.deepEqual(bigmath.eval('distance([[0,2],[-2,0],[0,2]])'), [ bignumber('2.8284271247461900976033774484194'), bignumber('0'), bignumber('2.8284271247461900976033774484194') ]);
-    assert.deepEqual(bigmath.eval('distance([[1,2,4],[1,2,6],[8,1,3]])'), [bignumber('2'), bignumber('7.1414284285428499979993998113673'), bignumber('7.6811457478686081757696870217314')]);
-    assert.deepEqual(bigmath.eval('distance([0.23, -0.1240], [-0.232, 13.292], [-0.34, 0.346])'), bignumber('10.658908662088362142660358292758'));
-    assert.deepEqual(bigmath.eval('distance({pointX: 1, pointY: 4}, {lineOnePtX: 6, lineOnePtY: 3}, {lineTwoPtX: 2, lineTwoPtY: 8})'),
-      bignumber('2.7205493726247441306311612969564'));
-    assert.deepEqual(bigmath.eval('distance([0.1123, -0.242], [0.1316, -0.2421, 0.122135])'), bignumber('0.709482134734344383494644726006'));
-    assert.deepEqual(bigmath.eval('distance({pointX: 10, pointY: 10}, {xCoeffLine: 8, yCoeffLine: 1, constant: 3})'), bignumber('11.535230316796386425693769698742'));
-    assert.deepEqual(bigmath.eval('distance([2, 3, 1], [1, 1, 2, 5, 0, 1])'), bignumber('2.3204774044612855517320588018918'));
-    assert.deepEqual(bigmath.eval('distance({pointX: 2, pointY: 3, pointZ: 1}, {x0: 1, y0: 1, z0: 2, a: 5, b: 0, c: 1})'),
-      bignumber('2.3204774044612855517320588018918'));
-  });
 });
diff --git a/test/function/geometry/intersect.test.js b/test/function/geometry/intersect.test.js
index c31a8b744..f1964cf4f 100644
--- a/test/function/geometry/intersect.test.js
+++ b/test/function/geometry/intersect.test.js
@@ -27,7 +27,7 @@ describe('intersect', function() {
     assert.deepEqual(math.intersect(math.matrix([0, 30, 0]), [0, 21, 0], math.matrix([0, 9, 0, 0])), math.matrix([0, 0, 0]));
     assert.deepEqual(math.intersect(math.matrix([0, 30, 0]), math.matrix([0, 21, 0]), math.matrix([0, 9, 0, 0])), math.matrix([0, 0, 0]));
   });
-
+  
   it('should return null if the points do not intersect', function() {
     assert.deepEqual(math.intersect([0, 1, 0], [0, 0, 0], [1, 1, 0], [1, 0, 0]), null);
     assert.deepEqual(math.intersect([0, 1], [0, 0], [1, 1], [1, 0]), null);
@@ -37,7 +37,7 @@ describe('intersect', function() {
     assert.throws(function () {math.intersect([2, 0, 1], [1, 1, 1, 6])}, /TypeError: Too few arguments in function intersect/);
     assert.throws(function () {math.intersect([2, 0, 1], [1, 1, 6], [2, 0, 1], [1, 1, 6], [0, 8, 1])}, /TypeError: Too many arguments in function intersect/);
   });
-
+  
   it('should throw an error for incompatible parameter types', function() {
     assert.throws(function () {math.intersect(2, 3, 6)}, /TypeError: Unexpected type of argument in function intersect/);
     assert.throws(function () {math.intersect([2, 0, 1], [1, 1, 1], [5, 1, 10])}, /TypeError: Array with 4 numbers expected as third argument/);
@@ -45,19 +45,4 @@ describe('intersect', function() {
     assert.throws(function () {math.intersect([2, 8, 9], 3, 6)}, /TypeError: Unexpected type of argument in function intersect/);
     assert.throws(function () {math.intersect('a', 'b', 'c', 'd')}, /TypeError: Unexpected type of argument in function intersect/);
   });
-
-  it('should calculate the intersection point if coordinates are bignumbers', function() {
-      var bigmath = math.create({number: 'BigNumber', precision: 32});
-      var bigintersect = bigmath.intersect;
-      var bignumber = bigmath.bignumber;
-
-      assert.deepEqual(bigmath.eval('intersect([0, 0], [10, 10], [10, 0], [0, 10])'), math.matrix([bignumber(5), bignumber(5)]));
-      assert.deepEqual(bigintersect([bignumber(0), bignumber(0)], [bignumber(10), bignumber(10)], [bignumber(10), bignumber(0)], [bignumber(0), bignumber(10)]),
-        [bignumber(5), bignumber(5)]);
-      assert.deepEqual(bigintersect([bignumber(0), 0], [10, bignumber(10)], [10, bignumber(0)], [0, bignumber(10)]), [bignumber(5), bignumber(5)]);
-      assert.deepEqual(bigintersect([bignumber(0), bignumber(0), bignumber(0)], [bignumber(10), bignumber(10), bignumber(0)], [bignumber(10), bignumber(0), bignumber(0)], [bignumber(0), bignumber(10), bignumber(0)]),
-        [bignumber(5), bignumber(5), bignumber(0)]);
-      assert.deepEqual(bigintersect([bignumber(1), bignumber(0), bignumber(1)], [bignumber(4), bignumber(-2), bignumber(2)], [bignumber(1), bignumber(1), bignumber(1), bignumber(6)]),
-        [bignumber(7), bignumber(-4), bignumber(3)]);
-  });
 });