text: "use strict";
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.arraySize = arraySize;
  exports.validate = validate;
  exports.validateIndex = validateIndex;
  exports.resize = resize;
  exports.reshape = reshape;
  exports.squeeze = squeeze;
  exports.unsqueeze = unsqueeze;
  exports.flatten = flatten;
  exports.map = map;
  exports.forEach = forEach;
  exports.filter = filter;
  exports.filterRegExp = filterRegExp;
  exports.join = join;
  exports.identify = identify;
  exports.generalize = generalize;
  exports.getArrayDataType = getArrayDataType;
  exports.last = last;
  exports.initial = initial;
  exports.contains = contains;
  
  var _number = require("./number");
  
  var _is = require("./is");
  
  var _string = require("./string");
  
  var _DimensionError = require("../error/DimensionError");
  
  var _IndexError = require("../error/IndexError");
  
  /**
   * Calculate the size of a multi dimensional array.
   * This function checks the size of the first entry, it does not validate
   * whether all dimensions match. (use function `validate` for that)
   * @param {Array} x
   * @Return {Number[]} size
   */
  function arraySize(x) {
    var s = [];
  
    while (Array.isArray(x)) {
      s.push(x.length);
      x = x[0];
    }
  
    return s;
  }
  /**
   * Recursively validate whether each element in a multi dimensional array
   * has a size corresponding to the provided size array.
   * @param {Array} array    Array to be validated
   * @param {number[]} size  Array with the size of each dimension
   * @param {number} dim   Current dimension
   * @throws DimensionError
   * @private
   */
  
  
  function _validate(array, size, dim) {
    var i;
    var len = array.length;
  
    if (len !== size[dim]) {
      throw new _DimensionError.DimensionError(len, size[dim]);
    }
  
    if (dim < size.length - 1) {
      // recursively validate each child array
      var dimNext = dim + 1;
  
      for (i = 0; i < len; i++) {
        var child = array[i];
  
        if (!Array.isArray(child)) {
          throw new _DimensionError.DimensionError(size.length - 1, size.length, '<');
        }
  
        _validate(array[i], size, dimNext);
      }
    } else {
      // last dimension. none of the childs may be an array
      for (i = 0; i < len; i++) {
        if (Array.isArray(array[i])) {
          throw new _DimensionError.DimensionError(size.length + 1, size.length, '>');
        }
      }
    }
  }
  /**
   * Validate whether each element in a multi dimensional array has
   * a size corresponding to the provided size array.
   * @param {Array} array    Array to be validated
   * @param {number[]} size  Array with the size of each dimension
   * @throws DimensionError
   */
  
  
  function validate(array, size) {
    var isScalar = size.length === 0;
  
    if (isScalar) {
      // scalar
      if (Array.isArray(array)) {
        throw new _DimensionError.DimensionError(array.length, 0);
      }
    } else {
      // array
      _validate(array, size, 0);
    }
  }
  /**
   * Test whether index is an integer number with index >= 0 and index < length
   * when length is provided
   * @param {number} index    Zero-based index
   * @param {number} [length] Length of the array
   */
  
  
  function validateIndex(index, length) {
    if (!(0, _is.isNumber)(index) || !(0, _number.isInteger)(index)) {
      throw new TypeError('Index must be an integer (value: ' + index + ')');
    }
  
    if (index < 0 || typeof length === 'number' && index >= length) {
      throw new _IndexError.IndexError(index, length);
    }
  }
  /**
   * Resize a multi dimensional array. The resized array is returned.
   * @param {Array} array         Array to be resized
   * @param {Array.<number>} size Array with the size of each dimension
   * @param {*} [defaultValue=0]  Value to be filled in in new entries,
   *                              zero by default. Specify for example `null`,
   *                              to clearly see entries that are not explicitly
   *                              set.
   * @return {Array} array         The resized array
   */
  
  
  function resize(array, size, defaultValue) {
    // TODO: add support for scalars, having size=[] ?
    // check the type of the arguments
    if (!Array.isArray(array) || !Array.isArray(size)) {
      throw new TypeError('Array expected');
    }
  
    if (size.length === 0) {
      throw new Error('Resizing to scalar is not supported');
    } // check whether size contains positive integers
  
  
    size.forEach(function (value) {
      if (!(0, _is.isNumber)(value) || !(0, _number.isInteger)(value) || value < 0) {
        throw new TypeError('Invalid size, must contain positive integers ' + '(size: ' + (0, _string.format)(size) + ')');
      }
    }); // recursively resize the array
  
    var _defaultValue = defaultValue !== undefined ? defaultValue : 0;
  
    _resize(array, size, 0, _defaultValue);
  
    return array;
  }
  /**
   * Recursively resize a multi dimensional array
   * @param {Array} array         Array to be resized
   * @param {number[]} size       Array with the size of each dimension
   * @param {number} dim          Current dimension
   * @param {*} [defaultValue]    Value to be filled in in new entries,
   *                              undefined by default.
   * @private
   */
  
  
  function _resize(array, size, dim, defaultValue) {
    var i;
    var elem;
    var oldLen = array.length;
    var newLen = size[dim];
    var minLen = Math.min(oldLen, newLen); // apply new length
  
    array.length = newLen;
  
    if (dim < size.length - 1) {
      // non-last dimension
      var dimNext = dim + 1; // resize existing child arrays
  
      for (i = 0; i < minLen; i++) {
        // resize child array
        elem = array[i];
  
        if (!Array.isArray(elem)) {
          elem = [elem]; // add a dimension
  
          array[i] = elem;
        }
  
        _resize(elem, size, dimNext, defaultValue);
      } // create new child arrays
  
  
      for (i = minLen; i < newLen; i++) {
        // get child array
        elem = [];
        array[i] = elem; // resize new child array
  
        _resize(elem, size, dimNext, defaultValue);
      }
    } else {
      // last dimension
      // remove dimensions of existing values
      for (i = 0; i < minLen; i++) {
        while (Array.isArray(array[i])) {
          array[i] = array[i][0];
        }
      } // fill new elements with the default value
  
  
      for (i = minLen; i < newLen; i++) {
        array[i] = defaultValue;
      }
    }
  }
  /**
   * Re-shape a multi dimensional array to fit the specified dimensions
   * @param {Array} array           Array to be reshaped
   * @param {Array.<number>} sizes  List of sizes for each dimension
   * @returns {Array}               Array whose data has been formatted to fit the
   *                                specified dimensions
   *
   * @throws {DimensionError}       If the product of the new dimension sizes does
   *                                not equal that of the old ones
   */
  
  
  function reshape(array, sizes) {
    var flatArray = flatten(array);
    var newArray;
  
    function product(arr) {
      return arr.reduce(function (prev, curr) {
        return prev * curr;
      });
    }
  
    if (!Array.isArray(array) || !Array.isArray(sizes)) {
      throw new TypeError('Array expected');
    }
  
    if (sizes.length === 0) {
      throw new _DimensionError.DimensionError(0, product(arraySize(array)), '!=');
    }
  
    var totalSize = 1;
  
    for (var sizeIndex = 0; sizeIndex < sizes.length; sizeIndex++) {
      totalSize *= sizes[sizeIndex];
    }
  
    if (flatArray.length !== totalSize) {
      throw new _DimensionError.DimensionError(product(sizes), product(arraySize(array)), '!=');
    }
  
    try {
      newArray = _reshape(flatArray, sizes);
    } catch (e) {
      if (e instanceof _DimensionError.DimensionError) {
        throw new _DimensionError.DimensionError(product(sizes), product(arraySize(array)), '!=');
      }
  
      throw e;
    }
  
    return newArray;
  }
  /**
   * Iteratively re-shape a multi dimensional array to fit the specified dimensions
   * @param {Array} array           Array to be reshaped
   * @param {Array.<number>} sizes  List of sizes for each dimension
   * @returns {Array}               Array whose data has been formatted to fit the
   *                                specified dimensions
   */
  
  
  function _reshape(array, sizes) {
    // testing if there are enough elements for the requested shape
    var tmpArray = array;
    var tmpArray2; // for each dimensions starting by the last one and ignoring the first one
  
    for (var sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) {
      var size = sizes[sizeIndex];
      tmpArray2 = []; // aggregate the elements of the current tmpArray in elements of the requested size
  
      var length = tmpArray.length / size;
  
      for (var i = 0; i < length; i++) {
        tmpArray2.push(tmpArray.slice(i * size, (i + 1) * size));
      } // set it as the new tmpArray for the next loop turn or for return
  
  
      tmpArray = tmpArray2;
    }
  
    return tmpArray;
  }
  /**
   * Squeeze a multi dimensional array
   * @param {Array} array
   * @param {Array} [size]
   * @returns {Array} returns the array itself
   */
  
  
  function squeeze(array, size) {
    var s = size || arraySize(array); // squeeze outer dimensions
  
    while (Array.isArray(array) && array.length === 1) {
      array = array[0];
      s.shift();
    } // find the first dimension to be squeezed
  
  
    var dims = s.length;
  
    while (s[dims - 1] === 1) {
      dims--;
    } // squeeze inner dimensions
  
  
    if (dims < s.length) {
      array = _squeeze(array, dims, 0);
      s.length = dims;
    }
  
    return array;
  }
  /**
   * Recursively squeeze a multi dimensional array
   * @param {Array} array
   * @param {number} dims Required number of dimensions
   * @param {number} dim  Current dimension
   * @returns {Array | *} Returns the squeezed array
   * @private
   */
  
  
  function _squeeze(array, dims, dim) {
    var i, ii;
  
    if (dim < dims) {
      var next = dim + 1;
  
      for (i = 0, ii = array.length; i < ii; i++) {
        array[i] = _squeeze(array[i], dims, next);
      }
    } else {
      while (Array.isArray(array)) {
        array = array[0];
      }
    }
  
    return array;
  }
  /**
   * Unsqueeze a multi dimensional array: add dimensions when missing
   *
   * Paramter `size` will be mutated to match the new, unqueezed matrix size.
   *
   * @param {Array} array
   * @param {number} dims       Desired number of dimensions of the array
   * @param {number} [outer]    Number of outer dimensions to be added
   * @param {Array} [size] Current size of array.
   * @returns {Array} returns the array itself
   * @private
   */
  
  
  function unsqueeze(array, dims, outer, size) {
    var s = size || arraySize(array); // unsqueeze outer dimensions
  
    if (outer) {
      for (var i = 0; i < outer; i++) {
        array = [array];
        s.unshift(1);
      }
    } // unsqueeze inner dimensions
  
  
    array = _unsqueeze(array, dims, 0);
  
    while (s.length < dims) {
      s.push(1);
    }
  
    return array;
  }
  /**
   * Recursively unsqueeze a multi dimensional array
   * @param {Array} array
   * @param {number} dims Required number of dimensions
   * @param {number} dim  Current dimension
   * @returns {Array | *} Returns the squeezed array
   * @private
   */
  
  
  function _unsqueeze(array, dims, dim) {
    var i, ii;
  
    if (Array.isArray(array)) {
      var next = dim + 1;
  
      for (i = 0, ii = array.length; i < ii; i++) {
        array[i] = _unsqueeze(array[i], dims, next);
      }
    } else {
      for (var d = dim; d < dims; d++) {
        array = [array];
      }
    }
  
    return array;
  }
  /**
   * Flatten a multi dimensional array, put all elements in a one dimensional
   * array
   * @param {Array} array   A multi dimensional array
   * @return {Array}        The flattened array (1 dimensional)
   */
  
  
  function flatten(array) {
    if (!Array.isArray(array)) {
      // if not an array, return as is
      return array;
    }
  
    var flat = [];
    array.forEach(function callback(value) {
      if (Array.isArray(value)) {
        value.forEach(callback); // traverse through sub-arrays recursively
      } else {
        flat.push(value);
      }
    });
    return flat;
  }
  /**
   * A safe map
   * @param {Array} array
   * @param {function} callback
   */
  
  
  function map(array, callback) {
    return Array.prototype.map.call(array, callback);
  }
  /**
   * A safe forEach
   * @param {Array} array
   * @param {function} callback
   */
  
  
  function forEach(array, callback) {
    Array.prototype.forEach.call(array, callback);
  }
  /**
   * A safe filter
   * @param {Array} array
   * @param {function} callback
   */
  
  
  function filter(array, callback) {
    if (arraySize(array).length !== 1) {
      throw new Error('Only one dimensional matrices supported');
    }
  
    return Array.prototype.filter.call(array, callback);
  }
  /**
   * Filter values in a callback given a regular expression
   * @param {Array} array
   * @param {RegExp} regexp
   * @return {Array} Returns the filtered array
   * @private
   */
  
  
  function filterRegExp(array, regexp) {
    if (arraySize(array).length !== 1) {
      throw new Error('Only one dimensional matrices supported');
    }
  
    return Array.prototype.filter.call(array, function (entry) {
      return regexp.test(entry);
    });
  }
  /**
   * A safe join
   * @param {Array} array
   * @param {string} separator
   */
  
  
  function join(array, separator) {
    return Array.prototype.join.call(array, separator);
  }
  /**
   * Assign a numeric identifier to every element of a sorted array
   * @param {Array} a  An array
   * @return {Array} An array of objects containing the original value and its identifier
   */
  
  
  function identify(a) {
    if (!Array.isArray(a)) {
      throw new TypeError('Array input expected');
    }
  
    if (a.length === 0) {
      return a;
    }
  
    var b = [];
    var count = 0;
    b[0] = {
      value: a[0],
      identifier: 0
    };
  
    for (var i = 1; i < a.length; i++) {
      if (a[i] === a[i - 1]) {
        count++;
      } else {
        count = 0;
      }
  
      b.push({
        value: a[i],
        identifier: count
      });
    }
  
    return b;
  }
  /**
   * Remove the numeric identifier from the elements
   * @param {array} a  An array
   * @return {array} An array of values without identifiers
   */
  
  
  function generalize(a) {
    if (!Array.isArray(a)) {
      throw new TypeError('Array input expected');
    }
  
    if (a.length === 0) {
      return a;
    }
  
    var b = [];
  
    for (var i = 0; i < a.length; i++) {
      b.push(a[i].value);
    }
  
    return b;
  }
  /**
   * Check the datatype of a given object
   * This is a low level implementation that should only be used by
   * parent Matrix classes such as SparseMatrix or DenseMatrix
   * This method does not validate Array Matrix shape
   * @param {Array} array
   * @param {function} typeOf   Callback function to use to determine the type of a value
   * @return string
   */
  
  
  function getArrayDataType(array, typeOf) {
    var type; // to hold type info
  
    var length = 0; // to hold length value to ensure it has consistent sizes
  
    for (var i = 0; i < array.length; i++) {
      var item = array[i];
      var isArray = Array.isArray(item); // Saving the target matrix row size
  
      if (i === 0 && isArray) {
        length = item.length;
      } // If the current item is an array but the length does not equal the targetVectorSize
  
  
      if (isArray && item.length !== length) {
        return undefined;
      }
  
      var itemType = isArray ? getArrayDataType(item, typeOf) // recurse into a nested array
      : typeOf(item);
  
      if (type === undefined) {
        type = itemType; // first item
      } else if (type !== itemType) {
        return 'mixed';
      } else {// we're good, everything has the same type so far
      }
    }
  
    return type;
  }
  /**
   * Return the last item from an array
   * @param array
   * @returns {*}
   */
  
  
  function last(array) {
    return array[array.length - 1];
  }
  /**
   * Get all but the last element of array.
   */
  
  
  function initial(array) {
    return array.slice(0, array.length - 1);
  }
  /**
   * Test whether an array or string contains an item
   * @param {Array | string} array
   * @param {*} item
   * @return {boolean}
   */
  
  
  function contains(array, item) {
    return array.indexOf(item) !== -1;
  }