Repository: josdejong/mathjs
Pull request #274 (2015-02-09 09:41:18)

BigNumber Trigonometry
Implemented and tested BigNumber implementations of `acos`, `asin`, `atan`, `tan`, `cosh`, `sinh`, and `tanh`. I also fixed the test cases for `cos` to take in `1` extra bit of `pi` and they work; `tan` had a similar issue. I will refactor the others next weekend, so that they have this property as well (`tan` is all set though as can be seen in it's test cases).

While it may look like a lot, it's "only" 404 lines of code in `bignumber.js`, so hopefully it won't be too much of an eyesore to look over.


Patch:
diff --git a/.gitignore b/.gitignore
index 77473cc44..35620f14d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,5 @@
 .idea
-.c9
 _site
 coverage
 *swp
 node_modules
-*.log
diff --git a/HISTORY.md b/HISTORY.md
index 56e5615e4..94f689693 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -3,14 +3,13 @@
 
 ## not yet released, version 1.2.1-SNAPSHOT
 
-- Implemented function `trace`. Thanks @pcorey.
 - Faster loading of BigNumber configuration with a high precision by lazy
   loading constants like `pi` and `e`.
 - Fixed constants `NaN` and `Infinity` not being BigNumber objects when
   BigNumbers are configured.
 - Fixed missing parentheses in the `toTex` representation of function
   `permutations`.
-- Some minor fixes in the docs. Thanks @KenanY.
+- Some minor fixes in the docs.
 
 
 ## 2014-12-25, version 1.2.0
diff --git a/lib/expression/node/Node.js b/lib/expression/node/Node.js
index bb63208cf..226004872 100644
--- a/lib/expression/node/Node.js
+++ b/lib/expression/node/Node.js
@@ -262,10 +262,12 @@ function _validateScope (scope) {
 function _transform(math) {
   var transformed = Object.create(math);
 
-  if (math.expression && math.expression.transform) {
-    for (var name in math.expression.transform) {
-      if (math.expression.transform.hasOwnProperty(name)) {
-        transformed[name] = math.expression.transform[name];
+  for (var name in math) {
+    if (math.hasOwnProperty(name)) {
+      var fn = math[name];
+      var transform = fn && fn.transform;
+      if (transform) {
+        transformed[name] = transform;
       }
     }
   }
diff --git a/lib/expression/transform/concat.transform.js b/lib/expression/transform/concat.transform.js
index 4871ef22c..112fa7a3b 100644
--- a/lib/expression/transform/concat.transform.js
+++ b/lib/expression/transform/concat.transform.js
@@ -14,7 +14,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.concat.transform = function () {
     // copy arguments into an array
     var args = argsToArray(arguments);
 
@@ -35,8 +35,4 @@ module.exports = function (math) {
       throw errorTransform(err);
     }
   };
-
-  math.concat.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/filter.transform.js b/lib/expression/transform/filter.transform.js
index 9438dc74b..e1ba91cc6 100644
--- a/lib/expression/transform/filter.transform.js
+++ b/lib/expression/transform/filter.transform.js
@@ -16,7 +16,7 @@ var ArgumentsError = require('../../error/ArgumentsError');
 module.exports = function (math) {
   var _filter = math.filter;
 
-  var transform = function (args, math, scope) {
+  _filter.transform = function (args, math, scope) {
     if (args.length !== 2) {
       throw new ArgumentsError('filter', arguments.length, 2);
     }
@@ -57,8 +57,5 @@ module.exports = function (math) {
     return _filter(x, test);
   };
 
-  transform.rawArgs = true;
-  math.filter.transform = transform;
-
-  return transform;
+  math.filter.transform.rawArgs = true;
 };
diff --git a/lib/expression/transform/forEach.transform.js b/lib/expression/transform/forEach.transform.js
index bb050b9ad..20206ae81 100644
--- a/lib/expression/transform/forEach.transform.js
+++ b/lib/expression/transform/forEach.transform.js
@@ -10,7 +10,7 @@ var Matrix = require('../../type/Matrix');
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function (x, callback) {
+  math.forEach.transform = function (x, callback) {
     if (arguments.length != 2) {
       throw new math.error.ArgumentsError('forEach', arguments.length, 2);
     }
@@ -39,7 +39,4 @@ module.exports = function (math) {
     recurse(array, []);
   }
 
-  math.forEach.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/index.transform.js b/lib/expression/transform/index.transform.js
index 8827915b1..0bd575746 100644
--- a/lib/expression/transform/index.transform.js
+++ b/lib/expression/transform/index.transform.js
@@ -15,7 +15,7 @@ var isArray = Array.isArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.index.transform = function () {
     var args = [];
     for (var i = 0, ii = arguments.length; i < ii; i++) {
       var arg = arguments[i];
@@ -42,8 +42,4 @@ module.exports = function (math) {
     Index.apply(res, args);
     return res;
   };
-
-  math.index.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/map.transform.js b/lib/expression/transform/map.transform.js
index ca4b28c44..3ceb0cad0 100644
--- a/lib/expression/transform/map.transform.js
+++ b/lib/expression/transform/map.transform.js
@@ -15,7 +15,7 @@ var isArray = Array.isArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function (x, callback) {
+  math.map.transform = function (x, callback) {
     if (arguments.length != 2) {
       throw new math.error.ArgumentsError('map', arguments.length, 2);
     }
@@ -44,8 +44,4 @@ module.exports = function (math) {
 
     return recurse(arrayIn, []);
   }
-
-  math.map.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/max.transform.js b/lib/expression/transform/max.transform.js
index 6fbfadca4..0076167c6 100644
--- a/lib/expression/transform/max.transform.js
+++ b/lib/expression/transform/max.transform.js
@@ -15,7 +15,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.max.transform = function () {
     var args = argsToArray(arguments);
 
     // change last argument dim from one-based to zero-based
@@ -36,8 +36,4 @@ module.exports = function (math) {
       throw errorTransform(err);
     }
   };
-
-  math.max.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/mean.transform.js b/lib/expression/transform/mean.transform.js
index 9badae60b..52eb07218 100644
--- a/lib/expression/transform/mean.transform.js
+++ b/lib/expression/transform/mean.transform.js
@@ -15,7 +15,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.mean.transform = function () {
     var args = argsToArray(arguments);
 
     // change last argument dim from one-based to zero-based
@@ -36,8 +36,4 @@ module.exports = function (math) {
       throw errorTransform(err);
     }
   };
-
-  math.mean.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/min.transform.js b/lib/expression/transform/min.transform.js
index 3875db72c..368248565 100644
--- a/lib/expression/transform/min.transform.js
+++ b/lib/expression/transform/min.transform.js
@@ -15,7 +15,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.min.transform = function () {
     var args = argsToArray(arguments);
 
     // change last argument dim from one-based to zero-based
@@ -36,8 +36,4 @@ module.exports = function (math) {
       throw errorTransform(err);
     }
   };
-
-  math.min.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/range.transform.js b/lib/expression/transform/range.transform.js
index 14234af2f..bb29ddc2f 100644
--- a/lib/expression/transform/range.transform.js
+++ b/lib/expression/transform/range.transform.js
@@ -11,7 +11,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.range.transform = function () {
     var args = argsToArray(arguments);
 
     var lastIndex = args.length - 1;
@@ -22,8 +22,4 @@ module.exports = function (math) {
 
     return math.range.apply(math, args);
   };
-
-  math.range.transform = transform;
-
-  return transform;
 };
diff --git a/lib/expression/transform/subset.transform.js b/lib/expression/transform/subset.transform.js
index bc8defde1..2b9e59871 100644
--- a/lib/expression/transform/subset.transform.js
+++ b/lib/expression/transform/subset.transform.js
@@ -12,7 +12,7 @@ var argsToArray = require('../../util/array').argsToArray;
  * @param {Object} math
  */
 module.exports = function (math) {
-  var transform = function () {
+  math.subset.transform = function () {
     try {
       return math.subset.apply(math, argsToArray(arguments));
     }
@@ -20,8 +20,4 @@ module.exports = function (math) {
       throw errorTransform(err);
     }
   };
-
-  math.subset.transform = transform;
-
-  return transform;
 };
diff --git a/lib/function/matrix/trace.js b/lib/function/matrix/trace.js
deleted file mode 100644
index f75f8baa7..000000000
--- a/lib/function/matrix/trace.js
+++ /dev/null
@@ -1,97 +0,0 @@
-'use strict';
-
-module.exports = function (math) {
-  var util = require('../../util/index'),
-
-      Matrix = require('../../type/Matrix'),
-
-      object = util.object,
-      string = util.string;
-
-  /**
-   * Calculate the trace of a matrix.
-   *
-   * Syntax:
-   *
-   *    math.trace(x)
-   *
-   * Examples:
-   *
-   *    math.trace([[1, 2], [3, 4]]); // returns 5
-   *
-   *    var A = [
-   *      [1, 2, 3],
-   *      [-1, 2, 3],
-   *      [2, 0, 3]
-   *    ]
-   *    math.trace(A); // returns 6
-   *
-   * @param {Array | Matrix} x  A matrix
-   * @return {Number} The trace of `x`
-   */
-  math.trace = function trace (x) {
-    if (arguments.length != 1) {
-      throw new math.error.ArgumentsError('trace', arguments.length, 1);
-    }
-
-    var size;
-    if (x instanceof Matrix) {
-      size = x.size();
-    }
-    else if (x instanceof Array) {
-      x = new Matrix(x);
-      size = x.size();
-    }
-    else {
-      // a scalar
-      size = [];
-    }
-
-    switch (size.length) {
-      case 0:
-        // scalar
-        return object.clone(x);
-
-      case 1:
-        // vector
-        if (size[0] == 1) {
-          return object.clone(x.valueOf()[0]);
-        }
-        else {
-          throw new RangeError('Matrix must be square ' +
-              '(size: ' + string.format(size) + ')');
-        }
-
-      case 2:
-        // two dimensional array
-        var rows = size[0];
-        var cols = size[1];
-        if (rows == cols) {
-          return _trace(x.clone().valueOf());
-        }
-        else {
-          throw new RangeError('Matrix must be square ' +
-              '(size: ' + string.format(size) + ')');
-        }
-
-      default:
-        // multi dimensional array
-        throw new RangeError('Matrix must be two dimensional ' +
-            '(size: ' + string.format(size) + ')');
-    }
-  };
-
-  /**
-   * Calculate the trace of a matrix
-   * @param {Array[]} matrix  A square, two dimensional matrix
-   * @returns {Number} trace
-   * @private
-   */
-  function _trace (matrix) {
-    var sum = 0;
-    for (var i = 0; i < matrix.length; i++) {
-        sum = math.add(sum, matrix[i][i]);
-    }
-    return sum;
-  }
-};
diff --git a/lib/function/trigonometry/acos.js b/lib/function/trigonometry/acos.js
index 5f97dadb9..ecaabf2b2 100644
--- a/lib/function/trigonometry/acos.js
+++ b/lib/function/trigonometry/acos.js
@@ -10,7 +10,9 @@ module.exports = function (math) {
       isNumber = util.number.isNumber,
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigArcCos = util.bignumber.arccos;
 
   /**
    * Calculate the inverse cosine of a value.
@@ -78,9 +80,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return acos(x.toNumber());
+      return bigArcCos(x);
     }
 
     throw new math.error.UnsupportedTypeError('acos', math['typeof'](x));
diff --git a/lib/function/trigonometry/asin.js b/lib/function/trigonometry/asin.js
index a732183d4..75f3e35f8 100644
--- a/lib/function/trigonometry/asin.js
+++ b/lib/function/trigonometry/asin.js
@@ -10,7 +10,9 @@ module.exports = function (math) {
       isNumber = util.number.isNumber,
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigArcSin = util.bignumber.arcsin;
 
   /**
    * Calculate the inverse sine of a value.
@@ -76,9 +78,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return asin(x.toNumber());
+      return bigArcSin(x);
     }
 
     throw new math.error.UnsupportedTypeError('asin', math['typeof'](x));
diff --git a/lib/function/trigonometry/atan.js b/lib/function/trigonometry/atan.js
index b4706e75a..bf729fae4 100644
--- a/lib/function/trigonometry/atan.js
+++ b/lib/function/trigonometry/atan.js
@@ -10,7 +10,9 @@ module.exports = function (math) {
       isNumber = util.number.isNumber,
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigArcTan = util.bignumber.arctan;
 
   /**
    * Calculate the inverse tangent of a value.
@@ -71,9 +73,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return atan(x.toNumber());
+      return bigArcTan(x);
     }
 
     throw new math.error.UnsupportedTypeError('atan', math['typeof'](x));
diff --git a/lib/function/trigonometry/cosh.js b/lib/function/trigonometry/cosh.js
index dcdd86ecb..b7fae2bbc 100644
--- a/lib/function/trigonometry/cosh.js
+++ b/lib/function/trigonometry/cosh.js
@@ -12,7 +12,9 @@ module.exports = function (math) {
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
       isUnit = Unit.isUnit,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigCosh = util.bignumber.cosh_sinh;
 
   /**
    * Calculate the hyperbolic cosine of a value,
@@ -66,9 +68,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return cosh(x.toNumber());
+      return bigCosh(x, 0);
     }
 
     throw new math.error.UnsupportedTypeError('cosh', math['typeof'](x));
diff --git a/lib/function/trigonometry/sech.js b/lib/function/trigonometry/sech.js
index da572ec2b..0918233cc 100644
--- a/lib/function/trigonometry/sech.js
+++ b/lib/function/trigonometry/sech.js
@@ -28,7 +28,7 @@ module.exports = function (math) {
    *
    *    // sech(x) = 1/ cosh(x)
    *    math.sech(0.5);       // returns 0.886818883970074
-   *    1 / math.cosh(0.5);   // returns 0.886818883970074
+   *    1 / math.cosh(0.5);   // returns 1.9190347513349437
    *
    * See also:
    *
diff --git a/lib/function/trigonometry/sinh.js b/lib/function/trigonometry/sinh.js
index 17c8edc24..56d98df71 100644
--- a/lib/function/trigonometry/sinh.js
+++ b/lib/function/trigonometry/sinh.js
@@ -12,7 +12,9 @@ module.exports = function (math) {
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
       isUnit = Unit.isUnit,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigSinh = util.bignumber.cosh_sinh;
 
   /**
    * Calculate the hyperbolic sine of a value,
@@ -72,9 +74,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return sinh(x.toNumber());
+      return bigSinh(x, 1);
     }
 
     throw new math.error.UnsupportedTypeError('sinh', math['typeof'](x));
diff --git a/lib/function/trigonometry/tan.js b/lib/function/trigonometry/tan.js
index 61fb99baf..5eab0cebe 100644
--- a/lib/function/trigonometry/tan.js
+++ b/lib/function/trigonometry/tan.js
@@ -1,6 +1,6 @@
 'use strict';
 
-module.exports = function (math) {
+module.exports = function (math, config) {
   var util = require('../../util/index'),
 
       BigNumber = math.type.BigNumber,
@@ -12,7 +12,9 @@ module.exports = function (math) {
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
       isUnit = Unit.isUnit,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigTan = util.bignumber.tan;
 
   /**
    * Calculate the tangent of a value. `tan(x)` is equal to `sin(x) / cos(x)`.
@@ -73,9 +75,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return tan(x.toNumber());
+      return bigTan(x, config.precision);
     }
 
     throw new math.error.UnsupportedTypeError('tan', math['typeof'](x));
diff --git a/lib/function/trigonometry/tanh.js b/lib/function/trigonometry/tanh.js
index 471953e0b..56d782bd1 100644
--- a/lib/function/trigonometry/tanh.js
+++ b/lib/function/trigonometry/tanh.js
@@ -12,7 +12,9 @@ module.exports = function (math) {
       isBoolean = util['boolean'].isBoolean,
       isComplex = Complex.isComplex,
       isUnit = Unit.isUnit,
-      isCollection = collection.isCollection;
+      isCollection = collection.isCollection,
+
+      bigTanh = util.bignumber.tanh;
 
   /**
    * Calculate the hyperbolic tangent of a value,
@@ -75,9 +77,7 @@ module.exports = function (math) {
     }
 
     if (x instanceof BigNumber) {
-      // TODO: implement BigNumber support
-      // downgrade to Number
-      return tanh(x.toNumber());
+      return bigTanh(x);
     }
 
     throw new math.error.UnsupportedTypeError('tanh', math['typeof'](x));
diff --git a/lib/function/utils/import.js b/lib/function/utils/import.js
index 4c79487f2..ebcf57a6a 100644
--- a/lib/function/utils/import.js
+++ b/lib/function/utils/import.js
@@ -120,20 +120,12 @@ module.exports = function (math) {
           }
           return value.apply(math, args);
         };
-        if (value && value.transform) {
-          math[name].transform = value.transform;
-        }
       }
       else {
         // just create a link to the function or value
         math[name] = value;
       }
 
-      // register the transform function if any
-      if (value && value.transform) {
-        math.expression.transform[name] = value.transform;
-      }
-
       // create a proxy for the chain
       math.chaining.Chain.createProxy(name, value);
     }
diff --git a/lib/math.js b/lib/math.js
index 5e3420331..902ed8435 100644
--- a/lib/math.js
+++ b/lib/math.js
@@ -256,7 +256,6 @@ function create (config) {
   require('./function/matrix/size')(math, _config);
   require('./function/matrix/squeeze')(math, _config);
   require('./function/matrix/subset')(math, _config);
-  require('./function/matrix/trace')(math, _config);
   require('./function/matrix/transpose')(math, _config);
   require('./function/matrix/zeros')(math, _config);
 
@@ -331,18 +330,16 @@ function create (config) {
   require('./constants')(math, _config);
 
   // attach transform functions (for converting one-based indices to zero-based)
-  math.expression.transform = {
-    concat: require('./expression/transform/concat.transform')(math, _config),
-    filter: require('./expression/transform/filter.transform')(math, _config),
-    forEach:require('./expression/transform/forEach.transform')(math, _config),
-    index:  require('./expression/transform/index.transform')(math, _config),
-    map:    require('./expression/transform/map.transform')(math, _config),
-    max:    require('./expression/transform/max.transform')(math, _config),
-    mean:   require('./expression/transform/mean.transform')(math, _config),
-    min:    require('./expression/transform/min.transform')(math, _config),
-    range:  require('./expression/transform/range.transform')(math, _config),
-    subset: require('./expression/transform/subset.transform')(math, _config)
-  };
+  require('./expression/transform/concat.transform')(math, _config);
+  require('./expression/transform/filter.transform')(math, _config);
+  require('./expression/transform/forEach.transform')(math, _config);
+  require('./expression/transform/index.transform')(math, _config);
+  require('./expression/transform/map.transform')(math, _config);
+  require('./expression/transform/max.transform')(math, _config);
+  require('./expression/transform/mean.transform')(math, _config);
+  require('./expression/transform/min.transform')(math, _config);
+  require('./expression/transform/range.transform')(math, _config);
+  require('./expression/transform/subset.transform')(math, _config);
 
   // selector (we initialize after all functions are loaded)
   math.chaining = {};
diff --git a/lib/util/bignumber.js b/lib/util/bignumber.js
index d175b4b60..02f47e305 100644
--- a/lib/util/bignumber.js
+++ b/lib/util/bignumber.js
@@ -52,8 +52,8 @@ exports.phi = memoize(function (precision) {
 exports.pi = memoize(function (precision) {
   // we calculate pi with a few decimal places extra to prevent round off issues
   var Big = BigNumber.constructor({precision: precision + 4});
-  var pi4th = new Big(4).times(exports.arctan(new Big(1).div(5)))
-      .minus(exports.arctan(new Big(1).div(239)));
+  var pi4th = new Big(4).times(arctan_taylor(new Big(1).div(5)))
+      .minus(arctan_taylor(new Big(1).div(239)));
 
   Big.config({precision: precision});
 
@@ -484,31 +484,141 @@ function decCoefficientToBinaryString(x) {
  *************************************/
 
 /**
- * Calculate the arc tangent of x
+ * Calculate the arc cosine of x
  *
- * arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
- *           = x - x^2*x^1/3 + x^2*x^3/5 - x^2*x^5/7 + x^2*x^7/9 - ...
+ * acos(x) = 2*atan(sqrt(1-x^2)/(1+x))
+ *
+ * @param {BigNumber} x
+ * @returns {BigNumber} arc cosine of x
+ */
+exports.arccos = function (x) {
+  var Big = x.constructor;
+  var precision = Big.precision;
+  if (x.abs().gt(Big.ONE)) {
+    throw new Error('acos() only has non-complex values for |x| <= 1.');
+  }
+  if (x.eq(-1)) {
+    return exports.pi(precision);
+  }
+
+  Big.config({precision: precision + 4});
+  var acos = exports.arctan(Big.ONE.minus(x.times(x)).sqrt()
+                                .div(x.plus(Big.ONE))).times(2);
+  Big.config({precision: precision});
+
+  return acos.toDP(precision - 1);
+};
+
+/**
+ * Calculate the arc sine of x
+ *
+ * @param {BigNumber} x
+ * @returns {BigNumber} arc sine of x
+ */
+exports.arcsin = function (x) {
+  var Big = x.constructor;
+  if (x.isNaN()) {
+    return new Big(NaN);
+  }
+  if (x.isZero()) {
+    return new Big(0);
+  }
+
+  var absX = x.abs();
+  if (absX.gt(1)) {
+    throw new Error('asin() only has non-complex values for |x| <= 1.');
+  }
+
+  var precision = Big.precision;
+
+  // Get x below 0.58
+  if (absX.gt(0.8)) {
+    Big.config({precision: precision + 4});
+
+    // arcsin(x) = sign(x)*(Pi/2 - arcsin(sqrt(1 - x^2)))
+    var sign = x.s;
+    var halfPi = exports.pi(precision + 4).div(2);
+    x = halfPi.minus(exports.arcsin(Big.ONE.minus(x.times(x)).sqrt()));
+    x.s = sign;
+
+    x.constructor = Big;
+    Big.config({precision: precision});
+    return x.toDP(precision - 1);
+  }
+  var wasReduced;
+  if (wasReduced = absX.gt(0.58)) {
+    Big.config({precision: precision + 8});
+
+    // arcsin(x) = 2*arcsin(x / (sqrt(2)*sqrt(sqrt(1 - x^2) + 1)))
+    x = x.div(new Big(2).sqrt().times(Big.ONE.minus(x.times(x)).sqrt()
+          .plus(Big.ONE).sqrt()));
+
+    Big.config({precision: precision});
+  }
+
+  // Avoid overhead of Newton's Method if feasible
+  var ret = (precision <= 60 || ((x.dp() <= Math.log(precision)) && x.lt(0.05)))
+    ? arcsin_taylor(x, precision)
+    : arcsin_newton(x, precision);
+
+  if (wasReduced) {
+    return ret.times(2);
+  }
+  return ret;
+};
+
+/**
+ * Calculate the arc tangent of x
  *
  * @param {BigNumber} x
  * @returns {BigNumber} arc tangent of x
  */
 exports.arctan = function (x) {
-  var y = x;
-  var yPrev = NaN;
-  var x2 = x.times(x);
-  var num = x;
-  var add = true;
+  var Big = x.constructor;
+  if (x.isNaN()) {
+    return new Big(NaN);
+  }
+  if (x.isZero()) {
+    return new Big(0);
+  }
+  var precision = Big.precision;
+  if (!x.isFinite()) {
+    var halfPi = exports.pi(precision + 2).div(2).toDP(precision - 1);
+    halfPi.s = x.s;
 
-  for (var k = 3; !y.equals(yPrev); k += 2) {
-    num = num.times(x2);
+    return halfPi;
+  }
 
-    yPrev = y;
-    add = !add;
-    y = (add) ? y.plus(num.div(k)) : y.minus(num.div(k));
+  var absX = x.abs();
+  if (absX.lte(0.875)) {
+    Big.config({precision: precision + 4});
+
+    var ret = arctan_taylor(x);
+
+    ret.constructor = Big;
+    Big.config({precision: precision});
+    return ret.toDP(Big.precision - 1);
   }
+  if (absX.gte(1.143)) {
+    Big.config({precision: precision + 4});
 
-  return y;
-}
+    // arctan(x) = sign(x)*((PI / 2) - arctan(1 / |x|))
+    var halfPi = exports.pi(precision + 4).div(2);
+    var ret = halfPi.minus(arctan_taylor(Big.ONE.div(absX)));
+    ret.s = x.s;
+
+    ret.constructor = Big;
+    Big.config({precision: precision});
+    return ret.toDP(Big.precision - 1);
+  }
+
+  // arctan(x) = arcsin(x / [sqrt(1 + x^2)])
+  Big.config({precision: precision + 4});
+  x = x.div(x.times(x).plus(1).sqrt());
+  Big.config({precision: precision});
+
+  return exports.arcsin(x);
+};
 
 /**
  * Calculate cosine/sine of x using the multiple angle identity:
@@ -519,30 +629,41 @@ exports.arctan = function (x) {
  * http://www.tc.umn.edu/~ringx004/sidebar.html
  *
  * @param {BigNumber} x
- * @param {Number} mode     sine function if 1, cosine function if 0
+ * @param {Number} precision    precision as defined in the config file
+ * @param {Number} mode         sine function if 1, cosine function if 0
  * @returns {BigNumber} sine or cosine of x
  */
 exports.cos_sin = function (x, precision, mode) {
+  var Big;
   if (x.isNaN() || !x.isFinite()) {
-    var Big = BigNumber.constructor({precision: precision});
+    Big = BigNumber.constructor({precision: precision});
     return new Big(NaN);
   }
 
+  // Avoid changing the original value
+  var y = BigNumberCopy(x);
+  Big = y.constructor;
+
   // sin(-x) == -sin(x), cos(-x) == cos(x)
-  var isNeg = x.isNegative();
+  var isNeg = y.isNegative();
   if (isNeg) {
-    x.s = -x.s;
+    y.s = -y.s;
   }
 
-  var y = reduceToPeriod(x, precision, mode);  // Make this destructive
+  // Apply ~log(precision) guard bits
+  var precPlusGuardDigits = precision + (Math.log(precision) | 0) + 3; 
+  Big.config({precision: precPlusGuardDigits});
+
+  y = reduceToPeriod(y, precPlusGuardDigits, mode);  // Make this destructive
   if (y[1]) {
+    Big.config({precision: precision});
     return y[0];
   }
 
+  var ret;
   y = y[0];
-  var Big = y.constructor;
   if (mode) {
-    var ret = cos_sin_taylor(y.div(3125), mode);
+    ret = cos_sin_taylor(y.div(3125), mode);
     Big.config({precision: Math.min(Big.precision, precision + 15)});
 
     var five = new Big(5);
@@ -570,7 +691,7 @@ exports.cos_sin = function (x, precision, mode) {
       loops = 4;
     }
 
-    var ret = cos_sin_taylor(y.div(div_factor), mode);
+    ret = cos_sin_taylor(y.div(div_factor), mode);
     Big.config({precision: Math.min(Big.precision, precision + 8)});
 
     var eight = new Big(8);
@@ -585,6 +706,228 @@ exports.cos_sin = function (x, precision, mode) {
   return ret.toDP(precision - 1);
 };
 
+/**
+ * Calculate the tangent of x
+ *
+ * tan(x) = sin(x) / cos(x)
+ *
+ * @param {BigNumber} x
+ * @param {Number} precision    precision as defined in the config file
+ * @returns {BigNumber} tangent of x
+ */
+exports.tan = function (x, precision) {
+  var Big = BigNumber.constructor({precision: precision});
+  if (x.isNaN()) {
+    return new Big(NaN);
+  }
+
+  var pi = exports.pi(precision + 2);
+  var halfPi = pi.div(2).toDP(precision - 1);
+  pi = pi.toDP(precision - 1);
+
+  var y = reduceToPeriod(x, precision, 1)[0];
+  if (y.abs().eq(pi)) {
+    return new Big(Infinity);
+  }
+
+  var sin = exports.cos_sin(y, precision + 2, 1);
+  var cos = sinToCos(sin);
+
+  sin.constructor.config({precision: precision});
+  sin = sin.toDP(precision);
+  cos = cos.toDP(precision);
+
+  // Make sure sign for cosine is correct
+  if (y.eq(x)) {
+    if (y.gt(halfPi)) {
+      cos.s = -cos.s;
+    }
+  } else if (pi.minus(y.abs()).gt(halfPi)) {
+    cos.s = -cos.s;
+  }
+
+  sin.constructor.config({precision: precision + 2});
+  var tan = sin.div(cos);
+
+  return new Big(tan.toPrecision(precision));
+};
+
+/**
+ * Calculate the hyperbolic cosine/sine of x
+ *
+ * cosh(x) = (exp(x) + exp(-x)) / 2
+ *         = (e^x + 1/e^x) / 2
+ *
+ * sinh(x) = (exp(x) - exp(-x)) / 2
+ *         = (e^x - 1/e^x) / 2
+ *
+ * @param {BigNumber} x
+ * @param {Number} mode     cosh function if 0, sinh function if 1
+ * @returns {BigNumber} cosh or sinh of x
+ */
+exports.cosh_sinh = function (x, mode) {
+  var Big = x.constructor;
+  if (x.isNaN()) {
+    return new Big(NaN);
+  }
+  if (!x.isFinite()) {
+    return new Big((mode) ? x : Infinity);
+  }
+
+  var precision = Big.precision;
+  Big.config({precision: precision + 4});
+
+  var y = x.exp();
+  y = (mode) ? y.minus(Big.ONE.div(y)) : y.plus(Big.ONE.div(y));
+  y = y.div(2);
+
+  Big.config({precision: precision});
+  return new Big(y.toPrecision(precision));
+};
+
+/**
+ * Calculate the hyperbolic tangent of x
+ *
+ * tanh(x) = (exp(x) + exp(-x)) / (exp(x) - exp(-x))
+ *         = (exp(2x) - 1) / (exp(2x) + 1)
+ *         = (e^x - 1/e^x) / (e^x + 1/e^x)
+ *
+ * @param {BigNumber} x
+ * @returns {BigNumber} tanh of x
+ */
+exports.tanh = function (x) {
+  var Big = x.constructor;
+  if (x.isNaN()) {
+    return new Big(NaN);
+  }
+  if (!x.isFinite()) {
+    return new Big(x.s);
+  }
+
+  var precision = Big.precision;
+  Big.config({precision: precision + 4});
+
+  var posExp = x.exp();
+  var negExp = Big.ONE.div(posExp);
+  var ret = posExp.minus(negExp);
+  ret = ret.div(posExp.plus(negExp));
+
+  Big.config({precision: precision});
+  return ret.toDP(precision - 1);
+};
+
+/**
+ * Calculate the arc sine of x using Newton's method
+ *
+ * f(x) = sin(x) = N  =>  f(x)  = sin(x) - N
+ *                        f'(x) = cos(x)
+ *
+ * Thus we solve each step as follows:
+ *     x_(i+1) = x_i - (sin(x_i) - N)/cos(x_i)
+ *
+ * @param {BigNumber} x
+ * @param {Number} oldPrecision
+ * @returns {BigNumber} arc sine of x
+ */
+function arcsin_newton(x, oldPrecision) {
+  // Calibration variables, adjusted from MAPM
+  var tolerance = -(oldPrecision + 4);
+  var maxp = oldPrecision + 8 - x.e;
+  var localPrecision = 25 - x.e;
+  var maxIter = Math.max(Math.log(oldPrecision + 2) * 1.442695 | 0 + 5, 5);
+
+  var Big = BigNumber.constructor({precision: oldPrecision});
+  var curr = new Big(Math.asin(x.toNumber()) + '');
+
+  var i = 0;
+  do {
+    var tmp0 = exports.cos_sin(curr, localPrecision, 1);
+    var tmp1 = sinToCos(tmp0);
+    if (!tmp0.isZero()) {
+      tmp0.s = curr.s;
+    }
+
+    var tmp2 = tmp0.minus(x).div(tmp1);
+    curr = curr.minus(tmp2);
+
+    localPrecision = Math.min(2*localPrecision, maxp);
+    curr.constructor.config({precision: localPrecision});
+  } while ((2*tmp2.e >= tolerance) && !tmp2.isZero() && (++i <= maxIter))
+
+  if (i == maxIter) {
+    throw new Error('asin() failed to converge to the requested accuracy.' +
+                    'Try with a higher precision.');
+  }
+
+  curr.constructor.config({precision: oldPrecision});
+  return curr.toDP(oldPrecision - 1);
+}
+
+/**
+ * Calculate the arc sine of x
+ *
+ * arcsin(x) = x + (1/2)*x^3/3 + (3/8)*x^5/5 + (15/48)*x^7/7 ...
+ *           = x + (1/2)*x^2*x^1/3 + [(1*3)/(2*4)]*x^2*x^3/5 + [(1*3*5)/(2*4*6)]*x^2*x^5/7 ...
+ *
+ * @param {BigNumber} x
+ * @param {Number} precision
+ * @returns {BigNumber} arc sine of x
+ */
+function arcsin_taylor(x, precision) {
+  var Big = x.constructor;
+  Big.config({precision: precision + Math.log(precision) | 0 + 4});
+
+  var one = new Big(1);
+  var y = x;
+  var yPrev = NaN;
+  var x2 = x.times(x);
+  var polyNum = x;
+  var constNum = new Big(one);
+  var constDen = new Big(one);
+
+  var bigK = new Big(one); 
+  for (var k = 3; !y.equals(yPrev); k += 2) {
+    polyNum = polyNum.times(x2);
+
+    constNum = constNum.times(bigK);
+    constDen = constDen.times(bigK.plus(one));
+
+    yPrev = y;
+    bigK = new Big(k);
+    y = y.plus(polyNum.times(constNum).div(bigK.times(constDen)));
+  }
+
+  Big.config({precision: precision});
+  return y.toDP(precision - 1);
+}
+
+/**
+ * Calculate the arc tangent of x using a Taylor expansion
+ *
+ * arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
+ *           = x - x^2*x^1/3 + x^2*x^3/5 - x^2*x^5/7 + x^2*x^7/9 - ...
+ *
+ * @param {BigNumber} x
+ * @returns {BigNumber} arc tangent of x
+ */
+function arctan_taylor(x) {
+  var y = x;
+  var yPrev = NaN;
+  var x2 = x.times(x);
+  var num = x;
+  var add = true;
+
+  for (var k = 3; !y.equals(yPrev); k += 2) {
+    num = num.times(x2);
+
+    yPrev = y;
+    add = !add;
+    y = (add) ? y.plus(num.div(k)) : y.minus(num.div(k));
+  }
+
+  return y;
+}
+
 /**
  * Calculate the cosine or sine of x using Taylor Series.
  *
@@ -629,22 +972,19 @@ function cos_sin_taylor(x, mode) {
  * @returns {Array} [Reduced x, is tau multiple?]
  */
 function reduceToPeriod(x, precision, mode) {
-  var Big = x.constructor;
-  var prec_plus_guard_digits = precision + (Math.log(precision) | 0) + 3;
-
-  Big.config({precision: prec_plus_guard_digits});
-  var pi = exports.pi(prec_plus_guard_digits + 2);
-  var tau = exports.tau(prec_plus_guard_digits);
+  var pi = exports.pi(precision + 2);
+  var tau = exports.tau(precision);
+  if (x.abs().lte(pi.toDP(x.dp()))) {
+    return [x, false];
+  }
 
+  var Big = x.constructor;
   // Catch if input is tau multiple using pi's precision
-  if (x.div(pi.toDP(x.dp(), 1)).toNumber() % 2 == 0) {
-    Big.config({precision: precision});
+  if (x.div(pi.toDP(x.dp())).toNumber() % 2 == 0) {
     return [new Big(mode ^ 1), true];
   }
 
   var y = x.mod(tau);
-  y.constructor = BigNumber.constructor({precision: prec_plus_guard_digits});
-  Big.config({precision: precision});
 
   // Catch if tau multiple with tau's precision
   if (y.toDP(x.dp(), 1).isZero()) {
@@ -662,9 +1002,29 @@ function reduceToPeriod(x, precision, mode) {
     }
   }
 
+  y.constructor = Big;
   return [y, false];
 }
 
+/**
+ * Convert from sine to cosine
+ *
+ * |cos(x)| = sqrt(1 - sin(x)^2)
+ *
+ * @param {BigNumber} sine of x
+ * @returns {BigNumber} sine as cosine
+ */
+function sinToCos(sinVal) {
+  var Big = sinVal.constructor;
+  var precision = Big.precision;
+  Big.config({precision: precision + 2});
+
+  var ret = Big.ONE.minus(sinVal.times(sinVal)).sqrt();
+
+  Big.config({precision: precision});
+  return ret.toDP(precision - 1);
+}
+
 
 /************************************
  *         Format functions         *
@@ -853,3 +1213,12 @@ exports.toFixed = function(value, precision) {
   // Note: the (precision || 0) is needed as the toFixed of BigNumber has an
   // undefined default precision instead of 0.
 };
+
+/* Helper functions */
+function BigNumberCopy(x) {
+  var Big = BigNumber.constructor({precision: x.constructor.precision});
+  var y = new Big(x);
+  y.constructor = Big;      // Not sure why I have to do this...
+
+  return y;
+}
diff --git a/package.json b/package.json
index d7c96da14..8abeffea0 100644
--- a/package.json
+++ b/package.json
@@ -27,9 +27,7 @@
     "Ryan Seys (https://github.com/ryanseys)",
     "Elijah Insua (https://github.com/tmpvar)",
     "Rogelio J. Baucells (https://github.com/rjbaucells)",
-    "Zach Zibrat (https://github.com/palimpsests)",
-    "Pete Corey (https://github.com/pcorey)",
-    "Kenan Yildirim (https://github.com/KenanY)"
+    "Zach Zibrat (https://github.com/palimpsests)"
   ],
   "homepage": "http://mathjs.org",
   "repository": {


Test patch:
diff --git a/test/expression/parse.test.js b/test/expression/parse.test.js
index f107a25e5..9ef5abaee 100644
--- a/test/expression/parse.test.js
+++ b/test/expression/parse.test.js
@@ -1300,33 +1300,36 @@ describe('parse', function() {
   });
 
   describe('functions', function () {
-    it('should evaluate function "mod"', function () {
-      approx.equal(parseAndEval('mod(8, 3)'), 2);
+    describe('functions', function () {
+      it('should evaluate function "mod"', function () {
+        approx.equal(parseAndEval('mod(8, 3)'), 2);
 
-    });
+      });
 
-    it('should evaluate function "to" ', function () {
-      approx.deepEqual(parseAndEval('to(5.08 cm * 1000, inch)'),
-          math.unit(2000, 'inch').to('inch'));
-    });
+      it('should evaluate function "to" ', function () {
+        approx.deepEqual(parseAndEval('to(5.08 cm * 1000, inch)'),
+            math.unit(2000, 'inch').to('inch'));
+      });
 
-    it('should evaluate function "sort" with a custom sort function', function () {
-      var scope = {};
-      parseAndEval('sortByLength(a, b) = size(a)[1] - size(b)[1]', scope);
-      assert.deepEqual(parseAndEval('sort(["Langdon", "Tom", "Sara"], sortByLength)', scope),
-          math.matrix(["Tom", "Sara", "Langdon"]));
-    });
+      it('should evaluate function "sort" with a custom sort function', function () {
+        var scope = {};
+        parseAndEval('sortByLength(a, b) = size(a)[1] - size(b)[1]', scope);
+        assert.deepEqual(parseAndEval('sort(["Langdon", "Tom", "Sara"], sortByLength)', scope),
+            math.matrix(["Tom", "Sara", "Langdon"]));
+      });
 
-    it('should evaluate function "filter" with a custom test function', function () {
-      var scope = {};
-      parseAndEval('isPositive(x) = x > 0', scope);
-      assert.deepEqual(parseAndEval('filter([6, -2, -1, 4, 3], isPositive)', scope),
-          math.matrix([6, 4, 3]));
-    });
+      it('should evaluate function "filter" with a custom test function', function () {
+        var scope = {};
+        parseAndEval('isPositive(x) = x > 0', scope);
+        assert.deepEqual(parseAndEval('filter([6, -2, -1, 4, 3], isPositive)', scope),
+            math.matrix([6, 4, 3]));
+      });
+
+      it('should evaluate function "filter" with a custom test equation', function () {
+        assert.deepEqual(parseAndEval('filter([6, -2, -1, 4, 3], x > 0)'),
+            math.matrix([6, 4, 3]));
+      });
 
-    it('should evaluate function "filter" with a custom test equation', function () {
-      assert.deepEqual(parseAndEval('filter([6, -2, -1, 4, 3], x > 0)'),
-          math.matrix([6, 4, 3]));
     });
 
   });
diff --git a/test/function/matrix/trace.test.js b/test/function/matrix/trace.test.js
deleted file mode 100644
index 23c28a9ca..000000000
--- a/test/function/matrix/trace.test.js
+++ /dev/null
@@ -1,114 +0,0 @@
-var assert = require('assert'),
-    error = require('../../../lib/error/index'),
-    approx = require('../../../tools/approx'),
-    math = require('../../../index');
-
-describe('trace', function() {
-
-  it('should calculate correctly the trace of a NxN matrix', function() {
-    assert.equal(math.trace([5]), 5);
-    assert.equal(math.trace([[1,2],[3,4]]), 5);
-    assert.equal(math.trace(math.matrix([[1,2],[3,4]])), 5);
-    approx.equal(math.trace([
-      [-2, 2,  3],
-      [-1, 1,  3],
-      [ 2, 0, -1]
-    ]), -2);
-    approx.equal(math.trace([
-      [ 1, 4,  7],
-      [ 3, 0,  5],
-      [-1, 9, 11]
-    ]), 12);
-    approx.equal(math.trace([
-      [1,7,4,3,7], 
-      [0,7,0,3,7], 
-      [0,7,4,3,0], 
-      [1,7,5,9,7], 
-      [2,7,4,3,7]
-    ]), 28);
-    approx.equal(math.trace(math.diag([4,-5,6])), 5);
-  });
-
-  it('should return N for the identity matrix',function() {
-    assert.equal(math.trace(math.eye(7)), 7);
-    assert.equal(math.trace(math.eye(2)), 2);
-    assert.equal(math.trace(math.eye(1)), 1);
-  });
-
-  it('should calculate the trace for a scalar',function() {
-    assert.equal(math.trace(7), 7);
-
-    var c1 = math.complex(2, 3);
-    var c2 = math.trace(c1);
-    assert.deepEqual(c1, c2);
-
-    // c2 should be a clone
-    c1.re = 0;
-    assert.equal(c1.re, 0);
-    assert.equal(c2.re, 2);
-  });
-
-  it('should calculate the trace for a 1x1 matrix',function() {
-    var c1 = math.complex(2, 3);
-    var c2 = math.trace([[c1]]);
-    assert.deepEqual(c1, c2);
-
-    // c2 should be a clone
-    c1.re = 0;
-    assert.equal(c1.re, 0);
-    assert.equal(c2.re, 2);
-  });
-
-  it('should calculate correctly the trace of a matrix with bignumbers', function() {
-    var bignumber = math.bignumber;
-
-    // 1x1
-    assert.deepEqual(math.trace([bignumber(5)]), bignumber(5));
-
-    // 2x2
-    assert.deepEqual(math.trace([
-      [bignumber(1), bignumber(2)],
-      [bignumber(3), bignumber(4)]
-    ]), bignumber(5));
-
-    // 3x3
-    assert.deepEqual(math.trace([
-      [bignumber(-2), bignumber(2), bignumber( 3)],
-      [bignumber(-1), bignumber(1), bignumber( 3)],
-      [bignumber( 2), bignumber(0), bignumber(-1)]
-    ]), bignumber(-2));
-
-    // the following would fail with regular Numbers due to a precision overflow
-    assert.deepEqual(math.trace([
-      [bignumber(1e10+1), bignumber(1e10)],
-      [bignumber(1e10), bignumber(-1e10)]
-    ]), bignumber(1));
-  });
-
-  it('should calculate the trace of a matrix with mixed numbers and bignumbers', function() {
-    var bignumber = math.bignumber;
-    assert.deepEqual(math.trace([
-      [bignumber(2), 1],
-      [bignumber(3), 4]
-    ]), bignumber(6));
-  });
-
-  it('should not change the value of the initial matrix', function() {
-    var m_test = [[1,2,3],[4,5,6],[7,8,9]];
-    math.trace(m_test);
-    assert.deepEqual(m_test, [[1,2,3],[4,5,6],[7,8,9]]);
-  });
-
-  it('should not accept a non-square matrix', function() {
-    assert.throws(function() { math.trace([1,2]); });
-    assert.throws(function() { math.trace([[1,2,3],[1,2,3]]); });
-    assert.throws(function() { math.trace([0,1],[0,1],[0,1]); });
-  });
-
-  it('should not accept arrays with dimensions higher than 2', function() {
-    assert.throws(function() { math.trace([[[1]]]); }, RangeError);
-    assert.throws(function() { math.trace(math.matrix([[[1]]])); }, RangeError);
-  });
-
-
-});
\ No newline at end of file
diff --git a/test/function/trigonometry/acos.test.js b/test/function/trigonometry/acos.test.js
index ced7c4fe5..c252ee3ea 100644
--- a/test/function/trigonometry/acos.test.js
+++ b/test/function/trigonometry/acos.test.js
@@ -7,7 +7,9 @@ var assert = require('assert'),
     cos = math.cos,
     complex = math.complex,
     matrix = math.matrix,
-    unit = math.unit;
+    unit = math.unit,
+    bigmath = math.create({number: 'bignumber', precision: 20}),
+    Big = bigmath.bignumber;
 
 describe('acos', function() {
   it('should return the arccos of a boolean', function () {
@@ -27,8 +29,21 @@ describe('acos', function() {
     approx.equal(acos(1) / pi, 0);
   });
 
-  it('should return the arccos of a bignumber (downgrades to number)', function() {
-    approx.equal(acos(math.bignumber(-1)), pi);
+  it('should return the arccos of a bignumber', function() {
+    var arg = Big(-1);
+    assert.deepEqual(acos(arg).toString(), bigmath.pi.toString());
+    assert.deepEqual(acos(Big(-0.5)), Big('2.0943951023931954923'));
+    assert.deepEqual(acos(Big(0)), Big('1.5707963267948966192'));
+    assert.deepEqual(acos(Big(0.5)), Big('1.0471975511965977462'));
+    assert.deepEqual(acos(Big(1)), Big(0));
+
+    // Hit Newton's method case
+    bigmath.config({precision: 61});
+    assert.deepEqual(acos(Big(0.00000001)).toString(), '1.570796316794896619' +
+                                                       '23132152497308477543' +
+                                                       '1910533020886243820359');
+    //Make sure arg was not changed
+    assert.deepEqual(arg, Big(-1));
   });
 
   it('should be the inverse function of cos', function() {
@@ -39,6 +54,15 @@ describe('acos', function() {
     approx.equal(acos(cos(2)), 2);
   });
 
+  it('should be the inverse function of bignumber cos', function() {
+    bigmath.config({precision: 20});
+    assert.deepEqual(acos(bigmath.cos(Big(-1))).toString(), '1');
+    assert.ok(acos(bigmath.cos(Big(0))).isZero());
+    assert.deepEqual(acos(bigmath.cos(Big(0.1))).toString(), '0.1');
+    assert.deepEqual(acos(bigmath.cos(Big(0.5))).toString(), '0.5');
+    assert.deepEqual(acos(bigmath.cos(Big(2))).toString(), '2');
+  });
+
   it('should return the arccos of a complex number', function() {
     approx.deepEqual(acos(complex('2+3i')), complex(1.00014354247380, -1.98338702991654));
     approx.deepEqual(acos(complex('2-3i')), complex(1.00014354247380, 1.98338702991654));
@@ -71,4 +95,4 @@ describe('acos', function() {
     assert.throws(function () {acos(1, 2)}, error.ArgumentsError);
   });
 
-});
\ No newline at end of file
+});
diff --git a/test/function/trigonometry/asin.test.js b/test/function/trigonometry/asin.test.js
index 6e225cf93..eb3e72d34 100644
--- a/test/function/trigonometry/asin.test.js
+++ b/test/function/trigonometry/asin.test.js
@@ -7,7 +7,9 @@ var assert = require('assert'),
     matrix = math.matrix,
     unit = math.unit,
     asin = math.asin,
-    sin = math.sin;
+    sin = math.sin,
+    bigmath = math.create({number: 'bignumber', precision: 20}),
+    Big = bigmath.bignumber;
 
 describe('asin', function() {
   it('should return the arcsin of a boolean', function () {
@@ -27,8 +29,30 @@ describe('asin', function() {
     approx.equal(asin(1) / pi, 0.5);
   });
 
-  it('should return the arcsin of a bignumber (downgrades to number)', function() {
-    approx.equal(asin(math.bignumber(-1)), -pi / 2);
+  it('should return the arcsin of a bignumber', function() {
+    var arg1 = Big(-1);
+    var arg2 = Big(-0.581);
+    var arg3 = Big(-0.5);
+
+    assert.deepEqual(asin(Big(-1)), Big('-1.5707963267948966192'));
+    assert.deepEqual(asin(Big(-0.581)), Big('-0.6199567994522537004'));
+    assert.deepEqual(asin(Big(-0.5)), Big('-0.5235987755982988731'));
+    assert.deepEqual(asin(Big(0)), Big(0));
+    assert.deepEqual(asin(Big(0.5)), Big('0.5235987755982988731'));
+    assert.deepEqual(asin(Big(0.581)), Big('0.6199567994522537004'));
+    assert.deepEqual(asin(Big(1)), Big('1.5707963267948966192'));
+
+    // Make sure args were not changed
+    assert.deepEqual(arg1, Big(-1));
+    assert.deepEqual(arg2, Big(-0.581));
+    assert.deepEqual(arg3, Big(-0.5));
+
+    // Hit Newton's method case
+    bigmath.config({precision: 61});
+
+    var arg4 = Big(0.00000001);
+    assert.deepEqual(asin(arg4).toString(), '1.0000000000000000166666666666666674166666666666667113e-8');
+    assert.deepEqual(arg4, Big(0.00000001));
   });
 
   it('should be the inverse function of sin', function() {
@@ -39,6 +63,27 @@ describe('asin', function() {
     approx.equal(asin(sin(2)), 1.14159265358979);
   });
 
+  it('should be the inverse function of bignumber sin', function() {
+    // More Newton's method test cases
+    assert.deepEqual(asin(bigmath.sin(Big(-2))).toString(), '-1.141592653589793238462643383279502884197169399375105820974945');
+    assert.deepEqual(asin(bigmath.sin(Big(-0.5))).toString(), '-0.5');
+    assert.deepEqual(asin(bigmath.sin(Big(-0.1))).toString(), '-0.1');
+    assert.deepEqual(asin(bigmath.sin(Big(0.1))).toString(), '0.1');
+    assert.deepEqual(asin(bigmath.sin(Big(0.5))).toString(), '0.5');
+    assert.deepEqual(asin(bigmath.sin(Big(2))).toString(), '1.141592653589793238462643383279502884197169399375105820974945');
+
+    bigmath.config({precision: 20});
+    // Full decimal Taylor test cases
+    /* sin(-1) =                      -0.8414709848078965067
+       asin(-0.8414709848078965067) = -1.00000000000000000008 => (rounding up) -1.0000000000000000001
+    assert.deepEqual(asin(bigmath.sin(Big(-1))), Big('-1')); */
+
+    assert.ok(asin(bigmath.sin(Big(0))).isZero());
+    assert.deepEqual(asin(bigmath.sin(Big(0.1))).toString(), '0.1');
+    assert.deepEqual(asin(bigmath.sin(Big(0.5))).toString(), '0.5');
+    assert.deepEqual(asin(bigmath.sin(Big(2))).toString(), '1.1415926535897932385');
+  });
+
   it('should return the arcsin of a complex number', function() {
     var re = 0.570652784321099;
     var im = 1.983387029916536;
@@ -76,4 +121,4 @@ describe('asin', function() {
     assert.throws(function () {asin(1, 2)}, error.ArgumentsError);
   });
 
-});
\ No newline at end of file
+});
diff --git a/test/function/trigonometry/atan.test.js b/test/function/trigonometry/atan.test.js
index 275f326bc..1997bd859 100644
--- a/test/function/trigonometry/atan.test.js
+++ b/test/function/trigonometry/atan.test.js
@@ -7,7 +7,9 @@ var assert = require('assert'),
     matrix = math.matrix,
     unit = math.unit,
     atan = math.atan,
-    tan = math.tan;
+    tan = math.tan,
+    bigmath = math.create({number: 'bignumber', precision: 20}),
+    Big = bigmath.bignumber;
 
 describe('atan', function() {
   it('should return the arctan of a boolean', function () {
@@ -27,8 +29,27 @@ describe('atan', function() {
     approx.equal(atan(1) / pi, 0.25);
   });
 
-  it('should return the arctan of a bignumber (downgrades to number)', function() {
-    approx.equal(atan(math.bignumber(1)), pi / 4);
+  it('should return the arctan of a bignumber', function() {
+    var arg1 = Big(-1);
+    var arg2 = Big(-0.5);
+    var arg3 = Big(0);
+    var arg6 = Big(2);
+    assert.deepEqual(atan(arg1), Big('-0.7853981633974483096'));
+    assert.deepEqual(atan(arg2), Big('-0.4636476090008061162'));
+    assert.deepEqual(atan(arg3), Big(0));
+    assert.deepEqual(atan(Big(0.5)), Big('0.4636476090008061162'));
+    assert.deepEqual(atan(Big(1)), Big('0.7853981633974483096'));
+    assert.deepEqual(atan(arg6), Big('1.107148717794090503'));
+
+    // Ensure the arguments where not changed
+    assert.deepEqual(arg1, Big(-1));
+    assert.deepEqual(arg2, Big(-0.5));
+    assert.deepEqual(arg3, Big(0));
+    assert.deepEqual(arg6, Big(2));
+
+    // Hit Newton's method case
+    bigmath.config({precision: 61});
+    assert.deepEqual(atan(Big(0.9)).toString(), '0.732815101786506591640792072734280251985755679358256086310506');
   });
 
   it('should be the inverse function of tan', function() {
@@ -39,6 +60,16 @@ describe('atan', function() {
     approx.equal(atan(tan(2)), -1.14159265358979);
   });
 
+  it('should be the inverse function of bignumber tan', function() {
+    bigmath.config({precision: 20});
+    assert.deepEqual(atan(bigmath.tan(Big(-1))).toString(), '-1');
+    assert.ok(atan(bigmath.tan(Big(0))).isZero());
+    assert.deepEqual(atan(bigmath.tan(Big(0.1))).toString(), '0.1');
+    assert.deepEqual(atan(bigmath.tan(Big(0.5))).toString(), '0.5');
+    assert.deepEqual(atan(bigmath.tan(Big(2))).toString(), '-1.1415926535897932385');
+    assert.deepEqual(atan(bigmath.tan(bigmath.pi.div(2))).toString(), '-1.5707963267948966192');
+  });
+
   it('should return the arctan of a complex number', function() {
     var re = 1.409921049596575,
         im = 0.229072682968539;
@@ -72,4 +103,4 @@ describe('atan', function() {
     assert.throws(function () {atan(1, 2)}, error.ArgumentsError);
   });
 
-});
\ No newline at end of file
+});
diff --git a/test/function/trigonometry/cos.test.js b/test/function/trigonometry/cos.test.js
index c530e948b..db6063efc 100644
--- a/test/function/trigonometry/cos.test.js
+++ b/test/function/trigonometry/cos.test.js
@@ -6,7 +6,10 @@ var assert = require('assert'),
     complex = math.complex,
     matrix = math.matrix,
     unit = math.unit,
-    cos = math.cos;
+    cos = math.cos,
+    bigmath = math.create({number: 'bignumber', precision: 15}),
+    biggermath = math.create({number: 'bignumber', precision: 238});
+
 
 describe('cos', function() {
   it('should return the cosine of a boolean', function () {
@@ -33,8 +36,7 @@ describe('cos', function() {
   });
 
   it('should return the cosine of a bignumber', function() {
-    var bigmath = math.create({number: 'bignumber', precision: 238});
-    assert.deepEqual(bigmath.cos(bigmath.bignumber(0)), bigmath.bignumber(1));
+    assert.deepEqual(bigmath.cos(biggermath.bignumber(0)).toString(), '1');
 
     // 103.64 % tau = 3.109... <- pretty close to the pi boundary
     var result_val = '-0.99947004918247698171247470962484532559534008595265991588' +
@@ -42,33 +44,31 @@ describe('cos', function() {
                         '80857263937361947475191126604630777331941809888320749410' +
                         '59494006339537812110786663367929884637840572887762249921' +
                         '8425619255481';
-    var cos_val = bigmath.cos(bigmath.bignumber(103.64));
+    var cos_val = biggermath.cos(biggermath.bignumber(103.64));
     assert.equal(cos_val.constructor.precision, 238);
     assert.deepEqual(cos_val.toString(), result_val);
 
-    cos_val = bigmath.cos(bigmath.bignumber(-103.64));
+    cos_val = biggermath.cos(biggermath.bignumber(-103.64));
     assert.equal(cos_val.constructor.precision, 238);
     assert.deepEqual(cos_val.toString(), result_val);
 
-    bigmath.config({precision: 15});
 
-    var bigPi = bigmath.pi;
-    result_val = bigmath.SQRT2.div(2).toString();
+    biggermath.config({precision: 16});
+    var bigPi = biggermath.pi;
 
+    result_val = bigmath.SQRT2.div(2).toString();
     assert.deepEqual(bigmath.cos(bigPi.div(4)).toString(), result_val);
     assert.ok(bigmath.cos(bigPi.div(2)).isZero());
     assert.deepEqual(bigmath.cos(bigPi).toString(), '-1');
     assert.ok(bigmath.cos(bigPi.times(3).div(2)).isZero());
     assert.deepEqual(bigmath.cos(bigPi.times(2)).toString(), '1');
-
     assert.deepEqual(bigmath.cos(bigmath.tau).toString(), '1');
     assert.deepEqual(bigmath.cos(bigmath.tau.times(2)).toString(), '1');
 
-    /* If we ever decide to pass in more digits of pi, uncomment these.
+    /* Pass in an extra digit of pi! Also tests whether pi was changed previously. */
     assert.deepEqual(bigmath.cos(bigPi.times(3).div(4)).toString(), '-'+result_val);
     assert.deepEqual(bigmath.cos(bigPi.times(5).div(4)).toString(), '-'+result_val);
     assert.deepEqual(bigmath.cos(bigPi.times(7).div(4)).toString(), result_val);
-    */
   });
 
   it('should return the cosine of a complex number', function() {
diff --git a/test/function/trigonometry/cosh.test.js b/test/function/trigonometry/cosh.test.js
index 94b90768a..054280e5d 100644
--- a/test/function/trigonometry/cosh.test.js
+++ b/test/function/trigonometry/cosh.test.js
@@ -26,8 +26,29 @@ describe('cosh', function() {
     approx.equal(cosh(3), 10.067661995778);
   });
 
-  it('should return the cosh of a bignumber (downgrades to number)', function() {
-    approx.equal(cosh(math.bignumber(1)), 1.5430806348152);
+  it('should return the cosh of a bignumber', function() {
+    var bigmath = math.create({number: 'bignumber', precision: 20});
+    var Big = bigmath.bignumber;
+    var bigInfinity = Big(Infinity);
+
+    var arg1 = Big(-3);
+    var arg9 = Big(Infinity);
+    var arg10 = Big(-Infinity);
+    assert.deepEqual(cosh(arg1), Big('10.067661995777765842'));
+    assert.deepEqual(cosh(Big(-2)), Big('3.7621956910836314596'));
+    assert.deepEqual(cosh(Big(-1)), Big('1.5430806348152437785'));
+    assert.deepEqual(cosh(Big(0)), Big(1));
+    assert.deepEqual(cosh(Big(1)), Big('1.5430806348152437785'));
+    assert.deepEqual(cosh(Big(2)), Big('3.7621956910836314596'));
+    assert.deepEqual(cosh(Big(3)), Big('10.067661995777765842'));
+    assert.deepEqual(cosh(bigmath.pi).toString(), '11.591953275521520628');
+    assert.deepEqual(cosh(arg9), bigInfinity);
+    assert.deepEqual(cosh(arg10), bigInfinity);
+
+    // Ensure args were not changed
+    assert.deepEqual(arg1, Big(-3));
+    assert.deepEqual(arg9, bigInfinity);
+    assert.deepEqual(arg10, Big(-Infinity));
   });
 
   it('should return the cosh of a complex number', function() {
diff --git a/test/function/trigonometry/csch.test.js b/test/function/trigonometry/csch.test.js
index 616fbcc82..8dbc2555f 100644
--- a/test/function/trigonometry/csch.test.js
+++ b/test/function/trigonometry/csch.test.js
@@ -24,6 +24,7 @@ describe('csch', function() {
     approx.equal(csch(1), 0.85091812823932);
     approx.equal(csch(2), 0.27572056477178);
     approx.equal(csch(3), 0.099821569668823);
+    approx.equal(csch(3), 0.099821569668823);
     approx.equal(csch(1e-22), Number.POSITIVE_INFINITY);
     approx.equal(csch(-1e-22), Number.NEGATIVE_INFINITY);
   });
diff --git a/test/function/trigonometry/sin.test.js b/test/function/trigonometry/sin.test.js
index 7a49bb6cc..52935ac4c 100644
--- a/test/function/trigonometry/sin.test.js
+++ b/test/function/trigonometry/sin.test.js
@@ -6,7 +6,8 @@ var assert = require('assert'),
     complex = math.complex,
     matrix = math.matrix,
     unit = math.unit,
-    sin = math.sin;
+    sin = math.sin,
+    bigmath = math.create({precision: 242});
 
 describe('sin', function() {
   it('should return the sine of a boolean', function () {
@@ -32,7 +33,6 @@ describe('sin', function() {
   });
 
   it('should return the sine of a bignumber', function() {
-    var bigmath = math.create({number: 'bignumber', precision: 242});
     assert.ok(bigmath.sin(bigmath.bignumber(0)).isZero());
 
     // 103.64 % tau = 3.109... <- pretty close to the pi boundary
@@ -42,13 +42,15 @@ describe('sin', function() {
                                               '714804383298805501395839512341888732261080924779366105855493575' +
                                               '835362891900420559398509489530577719840860106717522689249606121' +
                                               '2602629134186583352145117086874446046421403346033616');
-    result_val = bigmath.sin(bigmath.bignumber(-103.64));
+    var arg = bigmath.bignumber(-103.64);
+    result_val = bigmath.sin(arg);
+    assert.deepEqual(arg, bigmath.bignumber(-103.64));   // Make sure arg wasn't changed
     assert.equal(result_val.constructor.precision, 242);
     assert.deepEqual(result_val.toString(), '-0.0325518169566161584427313159942672130512044591216893328934710' +
                                                '3071480438329880550139583951234188873226108092477936610585549' +
                                                '3575835362891900420559398509489530577719840860106717522689249' +
                                                '6061212602629134186583352145117086874446046421403346033616');
-    bigmath.config({precision: 15});
+    bigmath.config({number: 'bignumber', precision: 15});
 
     var bigPi = bigmath.pi;
     result_val = bigmath.SQRT2.div(2).toString();
diff --git a/test/function/trigonometry/sinh.test.js b/test/function/trigonometry/sinh.test.js
index 7c10043c4..bb2d0d5f7 100644
--- a/test/function/trigonometry/sinh.test.js
+++ b/test/function/trigonometry/sinh.test.js
@@ -30,8 +30,28 @@ describe('sinh', function() {
     assert.equal(sinh(1e-50), 1e-50);
   })
 
-  it('should return the sinh of a bignumber (downgrades to number)', function() {
-    approx.equal(sinh(math.bignumber(1)), 1.1752011936438014);
+  it('should return the sinh of a bignumber', function() {
+    var bigmath = math.create({number: 'bignumber', precision: 20});
+    var Big = bigmath.bignumber;
+
+    var arg1 = Big(-1);
+    var arg6 = Big(Infinity);
+    var arg7 = Big(-Infinity);
+    assert.deepEqual(sinh(arg1), Big('-1.1752011936438014569'));
+    assert.deepEqual(sinh(Big(0)), Big(0));
+    assert.deepEqual(sinh(bigmath.pi).toString(), '11.548739357257748378');
+    assert.deepEqual(sinh(Big(1)), Big('1.1752011936438014569'));
+    assert.deepEqual(sinh(Big(-1e-10)), Big(-1e-10));
+    assert.deepEqual(sinh(arg6), Big(Infinity));
+    assert.deepEqual(sinh(arg7), Big(-Infinity));
+
+    // Ensure args were not changed
+    assert.deepEqual(arg1, Big(-1));
+    assert.deepEqual(arg6, Big(Infinity));
+    assert.deepEqual(arg7, Big(-Infinity));
+
+    bigmath.config({precision: 50});
+    assert.deepEqual(sinh(Big(1e-50)), Big(1e-50));
   });
 
   it('should return the sinh of a complex number', function() {
diff --git a/test/function/trigonometry/tan.test.js b/test/function/trigonometry/tan.test.js
index 6f70f41f9..0b7ba0113 100644
--- a/test/function/trigonometry/tan.test.js
+++ b/test/function/trigonometry/tan.test.js
@@ -8,7 +8,9 @@ var assert = require('assert'),
     unit = math.unit,
     sin = math.sin,
     cos = math.cos,
-    tan = math.tan;
+    tan = math.tan,
+    piBigmath = math.create({number: 'bignumber', precision: 21}),
+    bigmath = math.create({precision: 20});
 
 describe('tan', function() {
   it('should return the tangent of a boolean', function () {
@@ -33,8 +35,27 @@ describe('tan', function() {
     approx.equal(tan(pi*8/4), 0);
   });
 
-  it('should return the tangent of a bignumber (downgrades to number)', function() {
-    approx.equal(tan(math.bignumber(1)), 1.55740772465490);
+  it('should return the tangent of a bignumber', function() { 
+    var bigPi = piBigmath.pi;
+    var bigTau = piBigmath.tau;
+
+    assert.ok(bigmath.tan(bigmath.bignumber(0)).isZero());
+    assert.deepEqual(bigmath.tan(bigmath.bignumber(-1)).toString(), '-1.5574077246549022305');
+
+    assert.deepEqual(bigmath.tan(bigPi.div(8)).toString(), '0.4142135623730950488');
+    assert.deepEqual(bigmath.tan(bigPi.div(4)).toString(), '1');
+    assert.ok(!bigmath.tan(bigPi.div(2)).isFinite());
+    assert.ok(!bigmath.tan(bigPi.times(3).div(2)).isFinite());
+    assert.ok(bigmath.tan(bigPi.times(2)).isZero());
+    assert.ok(bigmath.tan(bigPi.times(4)).isZero());
+    assert.ok(bigmath.tan(bigTau).isZero());
+    assert.ok(bigmath.tan(bigTau.times(2)).isZero());
+
+    /* Passes with an extra digit of pi! */
+    assert.deepEqual(bigmath.tan(bigPi.times(3).div(4)).toString(), '-1');
+    assert.ok(bigmath.tan(bigPi).isZero());
+    assert.deepEqual(bigmath.tan(bigPi.times(5).div(4)).toString(), '1');
+    assert.deepEqual(bigmath.tan(bigPi.times(7).div(4)).toString(), '-1');
   });
 
   it('should return the tangent of a complex number', function() {
@@ -77,4 +98,4 @@ describe('tan', function() {
     assert.throws(function () {tan(1, 2)}, error.ArgumentsError);
   });
 
-});
\ No newline at end of file
+});
diff --git a/test/function/trigonometry/tanh.test.js b/test/function/trigonometry/tanh.test.js
index 0269bf94a..5056e463e 100644
--- a/test/function/trigonometry/tanh.test.js
+++ b/test/function/trigonometry/tanh.test.js
@@ -26,8 +26,28 @@ describe('tanh', function() {
     approx.equal(tanh(3), 0.99505475368673);
   });
 
-  it('should return the tanh of a bignumber (downgrades to number)', function() {
-    approx.equal(tanh(math.bignumber(1)), 0.76159415595576);
+  it('should return the tanh of a bignumber', function() {
+    var bigmath = math.create({number: 'bignumber', precision: 20});
+    var Big = bigmath.bignumber;
+
+    var arg1 = Big(-Infinity);
+    var arg2 = Big(-3);
+    var arg10 = Big(Infinity);
+    assert.deepEqual(tanh(arg1), Big(-1));
+    assert.deepEqual(tanh(arg2), Big('-0.9950547536867304513'));
+    assert.deepEqual(tanh(Big(-2)), Big('-0.9640275800758168839'));
+    assert.deepEqual(tanh(Big(-1)), Big('-0.7615941559557648881'));
+    assert.deepEqual(tanh(Big(0)), Big(0));
+    assert.deepEqual(tanh(Big(1)), Big('0.7615941559557648881'));
+    assert.deepEqual(tanh(Big(2)), Big('0.9640275800758168839'));
+    assert.deepEqual(tanh(Big(3)), Big('0.9950547536867304513'));
+    assert.deepEqual(tanh(bigmath.pi).toString(), '0.9962720762207499443');
+    assert.deepEqual(tanh(arg10), Big(1));
+
+    // Make sure args were not changed
+    assert.deepEqual(arg1, Big(-Infinity));
+    assert.deepEqual(arg2, Big(-3));
+    assert.deepEqual(arg10, Big(Infinity));
   });
 
   it('should return the tanh of a complex number', function() {