text: 'use strict';
  
  module.exports = function (math) {
    var util = require('../../util/index'),
  
        BigNumber = math.type.BigNumber,
        Complex = require('../../type/Complex'),
        Matrix = math.type.Matrix,
        Unit = require('../../type/Unit'),
        collection = math.collection,
  
        isBoolean = util['boolean'].isBoolean,
        isNumber = util.number.isNumber,
        isComplex = Complex.isComplex,
        isUnit = Unit.isUnit,
        isCollection = collection.isCollection;
  
    /**
     * Subtract two values, `x - y`.
     * For matrices, the function is evaluated element wise.
     *
     * Syntax:
     *
     *    math.subtract(x, y)
     *
     * Examples:
     *
     *    math.subtract(5.3, 2);        // returns Number 3.3
     *
     *    var a = math.complex(2, 3);
     *    var b = math.complex(4, 1);
     *    math.subtract(a, b);          // returns Complex -2 + 2i
     *
     *    math.subtract([5, 7, 4], 4);  // returns Array [1, 3, 0]
     *
     *    var c = math.unit('2.1 km');
     *    var d = math.unit('500m');
     *    math.subtract(c, d);          // returns Unit 1.6 km
     *
     * See also:
     *
     *    add
     *
     * @param  {Number | BigNumber | Boolean | Complex | Unit | Array | Matrix | null} x
     *            Initial value
     * @param  {Number | BigNumber | Boolean | Complex | Unit | Array | Matrix | null} y
     *            Value to subtract from `x`
     * @return {Number | BigNumber | Complex | Unit | Array | Matrix}
     *            Subtraction of `x` and `y`
     */
    math.subtract = function subtract(x, y) {
      if (arguments.length != 2) {
        throw new math.error.ArgumentsError('subtract', arguments.length, 2);
      }
  
      if (isNumber(x)) {
        if (isNumber(y)) {
          // number - number
          return x - y;
        }
        else if (isComplex(y)) {
          // number - complex
          return new Complex (
              x - y.re,
              - y.im
          );
        }
      }
      else if (isComplex(x)) {
        if (isNumber(y)) {
          // complex - number
          return new Complex (
              x.re - y,
              x.im
          )
        }
        else if (isComplex(y)) {
          // complex - complex
          return new Complex (
              x.re - y.re,
              x.im - y.im
          )
        }
      }
  
      if (x instanceof BigNumber) {
        // try to convert to big number
        if (isNumber(y)) {
          y = BigNumber.convert(y);
        }
        else if (isBoolean(y) || y === null) {
          y = new BigNumber(y ? 1 : 0);
        }
  
        if (y instanceof BigNumber) {
          return x.minus(y);
        }
  
        // downgrade to Number
        return subtract(x.toNumber(), y);
      }
      if (y instanceof BigNumber) {
        // try to convert to big number
        if (isNumber(x)) {
          x = BigNumber.convert(x);
        }
        else if (isBoolean(x) || x === null) {
          x = new BigNumber(x ? 1 : 0);
        }
  
        if (x instanceof BigNumber) {
          return x.minus(y)
        }
  
        // downgrade to Number
        return subtract(x, y.toNumber());
      }
  
      if (isUnit(x)) {
        if (isUnit(y)) {
          if (x.value == null) {
            throw new Error('Parameter x contains a unit with undefined value');
          }
  
          if (y.value == null) {
            throw new Error('Parameter y contains a unit with undefined value');
          }
  
          if (!x.equalBase(y)) {
            throw new Error('Units do not match');
          }
  
          var res = x.clone();
          res.value -= y.value;
          res.fixPrefix = false;
  
          return res;
        }
      }
  
      if (isCollection(x) || isCollection(y)) {
        return collection.deepMap2(x, y, subtract);
      }
  
      if (isBoolean(x) || x === null) {
        return subtract(+x, y);
      }
      if (isBoolean(y) || y === null) {
        return subtract(x, +y);
      }
  
      throw new math.error.UnsupportedTypeError('subtract', math['typeof'](x), math['typeof'](y));
    };
  };
  