module.exports = function (math) {
  var util = require('../../util/index'),

      BigNumber = math.type.BigNumber,
      Complex = require('../../type/Complex'),
      collection = require('../../type/collection'),

      isNumber = util.number.isNumber,
      isBoolean = util['boolean'].isBoolean,
      isComplex = Complex.isComplex,
      isCollection = collection.isCollection;

  /**
   * Calculate the square root of a value.
   *
   * For matrices, the function is evaluated element wise.
   *
   * Syntax:
   *
   *    math.sqrt(x)
   *
   * Examples:
   *
   *    math.sqrt(25);                // returns 5
   *    math.square(5);               // returns 25
   *    math.sqrt(-4);                // returns Complex -2i
   *
   * See also:
   *
   *    square, multiply
   *
   * @param {Number | Boolean | Complex | Array | Matrix} x
   *            Value for which to calculate the square root.
   * @return {Number | Complex | Array | Matrix}
   *            Returns the square root of `x`
   */
  math.sqrt = function sqrt (x) {
    if (arguments.length != 1) {
      throw new math.error.ArgumentsError('sqrt', arguments.length, 1);
    }

    if (isNumber(x)) {
      if (x >= 0) {
        return Math.sqrt(x);
      }
      else {
        return sqrt(new Complex(x, 0));
      }
    }

    if (isComplex(x)) {
      var r = Math.sqrt(x.re * x.re + x.im * x.im);
      if (x.im >= 0) {
        return new Complex(
            0.5 * Math.sqrt(2.0 * (r + x.re)),
            0.5 * Math.sqrt(2.0 * (r - x.re))
        );
      }
      else {
        return new Complex(
            0.5 * Math.sqrt(2.0 * (r + x.re)),
            -0.5 * Math.sqrt(2.0 * (r - x.re))
        );
      }
    }

    if (x instanceof BigNumber) {
      if (x.isNegative()) {
        // negative value -> downgrade to number to do complex value computation
        return sqrt(x.toNumber());
      }
      else {
        return x.sqrt();
      }
    }

    if (isCollection(x)) {
      return collection.deepMap(x, sqrt);
    }

    if (isBoolean(x)) {
      return sqrt(+x);
    }

    throw new math.error.UnsupportedTypeError('sqrt', math['typeof'](x));
  };
};
