'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createNorm = void 0;

var _factory = require("../../utils/factory");

var name = 'norm';
var dependencies = ['typed', 'abs', 'add', 'pow', 'conj', 'sqrt', 'multiply', 'equalScalar', 'larger', 'smaller', 'matrix'];
var createNorm =
/* #__PURE__ */
(0, _factory.factory)(name, dependencies, function (_ref) {
  var typed = _ref.typed,
      abs = _ref.abs,
      add = _ref.add,
      pow = _ref.pow,
      conj = _ref.conj,
      sqrt = _ref.sqrt,
      multiply = _ref.multiply,
      equalScalar = _ref.equalScalar,
      larger = _ref.larger,
      smaller = _ref.smaller,
      matrix = _ref.matrix;

  /**
   * Calculate the norm of a number, vector or matrix.
   *
   * The second parameter p is optional. If not provided, it defaults to 2.
   *
   * Syntax:
   *
   *    math.norm(x)
   *    math.norm(x, p)
   *
   * Examples:
   *
   *    math.abs(-3.5)                         // returns 3.5
   *    math.norm(-3.5)                        // returns 3.5
   *
   *    math.norm(math.complex(3, -4))         // returns 5
   *
   *    math.norm([1, 2, -3], Infinity)        // returns 3
   *    math.norm([1, 2, -3], -Infinity)       // returns 1
   *
   *    math.norm([3, 4], 2)                   // returns 5
   *
   *    math.norm([[1, 2], [3, 4]], 1)          // returns 6
   *    math.norm([[1, 2], [3, 4]], 'inf')     // returns 7
   *    math.norm([[1, 2], [3, 4]], 'fro')     // returns 5.477225575051661
   *
   * See also:
   *
   *    abs, hypot
   *
   * @param  {number | BigNumber | Complex | Array | Matrix} x
   *            Value for which to calculate the norm
   * @param  {number | BigNumber | string} [p=2]
   *            Vector space.
   *            Supported numbers include Infinity and -Infinity.
   *            Supported strings are: 'inf', '-inf', and 'fro' (The Frobenius norm)
   * @return {number | BigNumber} the p-norm
   */
  var norm = typed(name, {
    'number': Math.abs,
    'Complex': function Complex(x) {
      return x.abs();
    },
    'BigNumber': function BigNumber(x) {
      // norm(x) = abs(x)
      return x.abs();
    },
    'boolean': function boolean(x) {
      // norm(x) = abs(x)
      return Math.abs(x);
    },
    'Array': function Array(x) {
      return _norm(matrix(x), 2);
    },
    'Matrix': function Matrix(x) {
      return _norm(x, 2);
    },
    'number | Complex | BigNumber | boolean, number | BigNumber | string': function numberComplexBigNumberBooleanNumberBigNumberString(x) {
      // ignore second parameter, TODO: remove the option of second parameter for these types
      return norm(x);
    },
    'Array, number | BigNumber | string': function ArrayNumberBigNumberString(x, p) {
      return _norm(matrix(x), p);
    },
    'Matrix, number | BigNumber | string': function MatrixNumberBigNumberString(x, p) {
      return _norm(x, p);
    }
  });
  /**
   * Calculate the norm for an array
   * @param {Matrix} x
   * @param {number | string} p
   * @returns {number} Returns the norm
   * @private
   */

  function _norm(x, p) {
    // size
    var sizeX = x.size(); // check if it is a vector

    if (sizeX.length === 1) {
      // check p
      if (p === Number.POSITIVE_INFINITY || p === 'inf') {
        // norm(x, Infinity) = max(abs(x))
        var pinf = 0; // skip zeros since abs(0) === 0

        x.forEach(function (value) {
          var v = abs(value);

          if (larger(v, pinf)) {
            pinf = v;
          }
        }, true);
        return pinf;
      }

      if (p === Number.NEGATIVE_INFINITY || p === '-inf') {
        // norm(x, -Infinity) = min(abs(x))
        var ninf; // skip zeros since abs(0) === 0

        x.forEach(function (value) {
          var v = abs(value);

          if (!ninf || smaller(v, ninf)) {
            ninf = v;
          }
        }, true);
        return ninf || 0;
      }

      if (p === 'fro') {
        return _norm(x, 2);
      }

      if (typeof p === 'number' && !isNaN(p)) {
        // check p != 0
        if (!equalScalar(p, 0)) {
          // norm(x, p) = sum(abs(xi) ^ p) ^ 1/p
          var n = 0; // skip zeros since abs(0) === 0

          x.forEach(function (value) {
            n = add(pow(abs(value), p), n);
          }, true);
          return pow(n, 1 / p);
        }

        return Number.POSITIVE_INFINITY;
      } // invalid parameter value


      throw new Error('Unsupported parameter value');
    } // MxN matrix


    if (sizeX.length === 2) {
      // check p
      if (p === 1) {
        // norm(x) = the largest column sum
        var c = []; // result

        var maxc = 0; // skip zeros since abs(0) == 0

        x.forEach(function (value, index) {
          var j = index[1];
          var cj = add(c[j] || 0, abs(value));

          if (larger(cj, maxc)) {
            maxc = cj;
          }

          c[j] = cj;
        }, true);
        return maxc;
      }

      if (p === Number.POSITIVE_INFINITY || p === 'inf') {
        // norm(x) = the largest row sum
        var r = []; // result

        var maxr = 0; // skip zeros since abs(0) == 0

        x.forEach(function (value, index) {
          var i = index[0];
          var ri = add(r[i] || 0, abs(value));

          if (larger(ri, maxr)) {
            maxr = ri;
          }

          r[i] = ri;
        }, true);
        return maxr;
      }

      if (p === 'fro') {
        // norm(x) = sqrt(sum(diag(x'x)))
        var fro = 0;
        x.forEach(function (value, index) {
          fro = add(fro, multiply(value, conj(value)));
        });
        return abs(sqrt(fro));
      }

      if (p === 2) {
        // not implemented
        throw new Error('Unsupported parameter value, missing implementation of matrix singular value decomposition');
      } // invalid parameter value


      throw new Error('Unsupported parameter value');
    }
  }

  return norm;
});
exports.createNorm = createNorm;