feat:node-modules
This commit is contained in:
872
node_modules/mathjs/lib/cjs/utils/array.js
generated
vendored
Normal file
872
node_modules/mathjs/lib/cjs/utils/array.js
generated
vendored
Normal file
@@ -0,0 +1,872 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.arraySize = arraySize;
|
||||
exports.broadcastArrays = broadcastArrays;
|
||||
exports.broadcastSizes = broadcastSizes;
|
||||
exports.broadcastTo = broadcastTo;
|
||||
exports.checkBroadcastingRules = checkBroadcastingRules;
|
||||
exports.clone = clone;
|
||||
exports.concat = concat;
|
||||
exports.filter = filter;
|
||||
exports.filterRegExp = filterRegExp;
|
||||
exports.flatten = flatten;
|
||||
exports.forEach = forEach;
|
||||
exports.generalize = generalize;
|
||||
exports.get = get;
|
||||
exports.getArrayDataType = getArrayDataType;
|
||||
exports.identify = identify;
|
||||
exports.initial = initial;
|
||||
exports.isEmptyIndex = isEmptyIndex;
|
||||
exports.join = join;
|
||||
exports.last = last;
|
||||
exports.map = map;
|
||||
exports.processSizesWildcard = processSizesWildcard;
|
||||
exports.recurse = recurse;
|
||||
exports.reshape = reshape;
|
||||
exports.resize = resize;
|
||||
exports.squeeze = squeeze;
|
||||
exports.stretch = stretch;
|
||||
exports.unsqueeze = unsqueeze;
|
||||
exports.validate = validate;
|
||||
exports.validateIndex = validateIndex;
|
||||
exports.validateIndexSourceSize = validateIndexSourceSize;
|
||||
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||
var _number = require("./number.js");
|
||||
var _is = require("./is.js");
|
||||
var _string = require("./string.js");
|
||||
var _DimensionError = require("../error/DimensionError.js");
|
||||
var _IndexError = require("../error/IndexError.js");
|
||||
var _object = require("./object.js");
|
||||
/**
|
||||
* 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) {
|
||||
const 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) {
|
||||
let i;
|
||||
const len = array.length;
|
||||
if (len !== size[dim]) {
|
||||
throw new _DimensionError.DimensionError(len, size[dim]);
|
||||
}
|
||||
if (dim < size.length - 1) {
|
||||
// recursively validate each child array
|
||||
const dimNext = dim + 1;
|
||||
for (i = 0; i < len; i++) {
|
||||
const 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) {
|
||||
const isScalar = size.length === 0;
|
||||
if (isScalar) {
|
||||
// scalar
|
||||
if (Array.isArray(array)) {
|
||||
throw new _DimensionError.DimensionError(array.length, 0);
|
||||
}
|
||||
} else {
|
||||
// array
|
||||
_validate(array, size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate whether the source of the index matches the size of the Array
|
||||
* @param {Array | Matrix} array Array to be validated
|
||||
* @param {Index} index Index with the source information to validate
|
||||
* @throws DimensionError
|
||||
*/
|
||||
function validateIndexSourceSize(value, index) {
|
||||
const valueSize = value.isMatrix ? value._size : arraySize(value);
|
||||
const sourceSize = index._sourceSize;
|
||||
// checks if the source size is not null and matches the valueSize
|
||||
sourceSize.forEach((sourceDim, i) => {
|
||||
if (sourceDim !== null && sourceDim !== valueSize[i]) {
|
||||
throw new _DimensionError.DimensionError(sourceDim, valueSize[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (index !== undefined) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if and index has empty values
|
||||
* @param {number} index Zero-based index
|
||||
*/
|
||||
function isEmptyIndex(index) {
|
||||
for (let i = 0; i < index._dimensions.length; ++i) {
|
||||
const dimension = index._dimensions[i];
|
||||
if (dimension._data && (0, _is.isArray)(dimension._data)) {
|
||||
if (dimension._size[0] === 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (dimension.isRange) {
|
||||
if (dimension.start === dimension.end) {
|
||||
return true;
|
||||
}
|
||||
} else if ((0, _is.isString)(dimension)) {
|
||||
if (dimension.length === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize a multi dimensional array. The resized array is returned.
|
||||
* @param {Array | number} array Array to be resized
|
||||
* @param {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) {
|
||||
// check the type of the arguments
|
||||
if (!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) + ')');
|
||||
}
|
||||
});
|
||||
|
||||
// convert number to an array
|
||||
if ((0, _is.isNumber)(array) || (0, _is.isBigNumber)(array)) {
|
||||
array = [array];
|
||||
}
|
||||
|
||||
// recursively resize the array
|
||||
const _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) {
|
||||
let i;
|
||||
let elem;
|
||||
const oldLen = array.length;
|
||||
const newLen = size[dim];
|
||||
const minLen = Math.min(oldLen, newLen);
|
||||
|
||||
// apply new length
|
||||
array.length = newLen;
|
||||
if (dim < size.length - 1) {
|
||||
// non-last dimension
|
||||
const 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 {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) {
|
||||
const flatArray = flatten(array);
|
||||
const currentLength = flatArray.length;
|
||||
if (!Array.isArray(array) || !Array.isArray(sizes)) {
|
||||
throw new TypeError('Array expected');
|
||||
}
|
||||
if (sizes.length === 0) {
|
||||
throw new _DimensionError.DimensionError(0, currentLength, '!=');
|
||||
}
|
||||
sizes = processSizesWildcard(sizes, currentLength);
|
||||
const newLength = product(sizes);
|
||||
if (currentLength !== newLength) {
|
||||
throw new _DimensionError.DimensionError(newLength, currentLength, '!=');
|
||||
}
|
||||
try {
|
||||
return _reshape(flatArray, sizes);
|
||||
} catch (e) {
|
||||
if (e instanceof _DimensionError.DimensionError) {
|
||||
throw new _DimensionError.DimensionError(newLength, currentLength, '!=');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the wildcard -1 in the sizes array.
|
||||
* @param {number[]} sizes List of sizes for each dimension. At most on wildcard.
|
||||
* @param {number} currentLength Number of elements in the array.
|
||||
* @throws {Error} If more than one wildcard or unable to replace it.
|
||||
* @returns {number[]} The sizes array with wildcard replaced.
|
||||
*/
|
||||
function processSizesWildcard(sizes, currentLength) {
|
||||
const newLength = product(sizes);
|
||||
const processedSizes = sizes.slice();
|
||||
const WILDCARD = -1;
|
||||
const wildCardIndex = sizes.indexOf(WILDCARD);
|
||||
const isMoreThanOneWildcard = sizes.indexOf(WILDCARD, wildCardIndex + 1) >= 0;
|
||||
if (isMoreThanOneWildcard) {
|
||||
throw new Error('More than one wildcard in sizes');
|
||||
}
|
||||
const hasWildcard = wildCardIndex >= 0;
|
||||
const canReplaceWildcard = currentLength % newLength === 0;
|
||||
if (hasWildcard) {
|
||||
if (canReplaceWildcard) {
|
||||
processedSizes[wildCardIndex] = -currentLength / newLength;
|
||||
} else {
|
||||
throw new Error('Could not replace wildcard, since ' + currentLength + ' is no multiple of ' + -newLength);
|
||||
}
|
||||
}
|
||||
return processedSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the product of all array elements.
|
||||
* @param {number[]} array Array of factors
|
||||
* @returns {number} Product of all elements
|
||||
*/
|
||||
function product(array) {
|
||||
return array.reduce((prev, curr) => prev * curr, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iteratively re-shape a multi dimensional array to fit the specified dimensions
|
||||
* @param {Array} array Array to be reshaped
|
||||
* @param {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
|
||||
let tmpArray = array;
|
||||
let tmpArray2;
|
||||
// for each dimensions starting by the last one and ignoring the first one
|
||||
for (let sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) {
|
||||
const size = sizes[sizeIndex];
|
||||
tmpArray2 = [];
|
||||
|
||||
// aggregate the elements of the current tmpArray in elements of the requested size
|
||||
const length = tmpArray.length / size;
|
||||
for (let 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) {
|
||||
const 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
|
||||
let 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) {
|
||||
let i, ii;
|
||||
if (dim < dims) {
|
||||
const 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) {
|
||||
const s = size || arraySize(array);
|
||||
|
||||
// unsqueeze outer dimensions
|
||||
if (outer) {
|
||||
for (let 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) {
|
||||
let i, ii;
|
||||
if (Array.isArray(array)) {
|
||||
const next = dim + 1;
|
||||
for (i = 0, ii = array.length; i < ii; i++) {
|
||||
array[i] = _unsqueeze(array[i], dims, next);
|
||||
}
|
||||
} else {
|
||||
for (let 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;
|
||||
}
|
||||
const 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, entry => 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;
|
||||
}
|
||||
const b = [];
|
||||
let count = 0;
|
||||
b[0] = {
|
||||
value: a[0],
|
||||
identifier: 0
|
||||
};
|
||||
for (let 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;
|
||||
}
|
||||
const b = [];
|
||||
for (let 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) {
|
||||
let type; // to hold type info
|
||||
let length = 0; // to hold length value to ensure it has consistent sizes
|
||||
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
const 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;
|
||||
}
|
||||
const 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.
|
||||
* @param {array}
|
||||
* @returns {*}
|
||||
*/
|
||||
function initial(array) {
|
||||
return array.slice(0, array.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively concatenate two matrices.
|
||||
* The contents of the matrices is not cloned.
|
||||
* @param {Array} a Multi dimensional array
|
||||
* @param {Array} b Multi dimensional array
|
||||
* @param {number} concatDim The dimension on which to concatenate (zero-based)
|
||||
* @param {number} dim The current dim (zero-based)
|
||||
* @return {Array} c The concatenated matrix
|
||||
* @private
|
||||
*/
|
||||
function concatRecursive(a, b, concatDim, dim) {
|
||||
if (dim < concatDim) {
|
||||
// recurse into next dimension
|
||||
if (a.length !== b.length) {
|
||||
throw new _DimensionError.DimensionError(a.length, b.length);
|
||||
}
|
||||
const c = [];
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
c[i] = concatRecursive(a[i], b[i], concatDim, dim + 1);
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
// concatenate this dimension
|
||||
return a.concat(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates many arrays in the specified direction
|
||||
* @param {...Array} arrays All the arrays to concatenate
|
||||
* @param {number} concatDim The dimension on which to concatenate (zero-based)
|
||||
* @returns
|
||||
*/
|
||||
function concat() {
|
||||
const arrays = Array.prototype.slice.call(arguments, 0, -1);
|
||||
const concatDim = Array.prototype.slice.call(arguments, -1);
|
||||
if (arrays.length === 1) {
|
||||
return arrays[0];
|
||||
}
|
||||
if (arrays.length > 1) {
|
||||
return arrays.slice(1).reduce(function (A, B) {
|
||||
return concatRecursive(A, B, concatDim, 0);
|
||||
}, arrays[0]);
|
||||
} else {
|
||||
throw new Error('Wrong number of arguments in function concat');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives two or more sizes and get's the broadcasted size for both.
|
||||
* @param {...number[]} sizes Sizes to broadcast together
|
||||
* @returns
|
||||
*/
|
||||
function broadcastSizes() {
|
||||
for (var _len = arguments.length, sizes = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
sizes[_key] = arguments[_key];
|
||||
}
|
||||
const dimensions = sizes.map(s => s.length);
|
||||
const N = Math.max(...dimensions);
|
||||
const sizeMax = new Array(N).fill(null);
|
||||
// check for every size
|
||||
for (let i = 0; i < sizes.length; i++) {
|
||||
const size = sizes[i];
|
||||
const dim = dimensions[i];
|
||||
for (let j = 0; j < dim; j++) {
|
||||
const n = N - dim + j;
|
||||
if (size[j] > sizeMax[n]) {
|
||||
sizeMax[n] = size[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < sizes.length; i++) {
|
||||
checkBroadcastingRules(sizes[i], sizeMax);
|
||||
}
|
||||
return sizeMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it's possible to broadcast a size to another size
|
||||
* @param {number[]} size The size of the array to check
|
||||
* @param {number[]} toSize The size of the array to validate if it can be broadcasted to
|
||||
*/
|
||||
function checkBroadcastingRules(size, toSize) {
|
||||
const N = toSize.length;
|
||||
const dim = size.length;
|
||||
for (let j = 0; j < dim; j++) {
|
||||
const n = N - dim + j;
|
||||
if (size[j] < toSize[n] && size[j] > 1 || size[j] > toSize[n]) {
|
||||
throw new Error(`shape missmatch: missmatch is found in arg with shape (${size}) not possible to broadcast dimension ${dim} with size ${size[j]} to size ${toSize[n]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a single array to a certain size
|
||||
* @param {array} array Array to be broadcasted
|
||||
* @param {number[]} toSize Size to broadcast the array
|
||||
* @returns The broadcasted array
|
||||
*/
|
||||
function broadcastTo(array, toSize) {
|
||||
let Asize = arraySize(array);
|
||||
if ((0, _object.deepStrictEqual)(Asize, toSize)) {
|
||||
return array;
|
||||
}
|
||||
checkBroadcastingRules(Asize, toSize);
|
||||
const broadcastedSize = broadcastSizes(Asize, toSize);
|
||||
const N = broadcastedSize.length;
|
||||
const paddedSize = [...Array(N - Asize.length).fill(1), ...Asize];
|
||||
let A = clone(array);
|
||||
// reshape A if needed to make it ready for concat
|
||||
if (Asize.length < N) {
|
||||
A = reshape(A, paddedSize);
|
||||
Asize = arraySize(A);
|
||||
}
|
||||
|
||||
// stretches the array on each dimension to make it the same size as index
|
||||
for (let dim = 0; dim < N; dim++) {
|
||||
if (Asize[dim] < broadcastedSize[dim]) {
|
||||
A = stretch(A, broadcastedSize[dim], dim);
|
||||
Asize = arraySize(A);
|
||||
}
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts arrays and returns the broadcasted arrays in an array
|
||||
* @param {...Array | any} arrays
|
||||
* @returns
|
||||
*/
|
||||
function broadcastArrays() {
|
||||
for (var _len2 = arguments.length, arrays = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
arrays[_key2] = arguments[_key2];
|
||||
}
|
||||
if (arrays.length === 0) {
|
||||
throw new Error('Insuficient number of argumnets in function broadcastArrays');
|
||||
}
|
||||
if (arrays.length === 1) {
|
||||
return arrays[0];
|
||||
}
|
||||
const sizes = arrays.map(function (array) {
|
||||
return arraySize(array);
|
||||
});
|
||||
const broadcastedSize = broadcastSizes(...sizes);
|
||||
const broadcastedArrays = [];
|
||||
arrays.forEach(function (array) {
|
||||
broadcastedArrays.push(broadcastTo(array, broadcastedSize));
|
||||
});
|
||||
return broadcastedArrays;
|
||||
}
|
||||
|
||||
/**
|
||||
* stretches a matrix up to a certain size in a certain dimension
|
||||
* @param {Array} arrayToStretch
|
||||
* @param {number[]} sizeToStretch
|
||||
* @param {number} dimToStretch
|
||||
* @returns
|
||||
*/
|
||||
function stretch(arrayToStretch, sizeToStretch, dimToStretch) {
|
||||
return concat(...Array(sizeToStretch).fill(arrayToStretch), dimToStretch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single element from an array given an index.
|
||||
*
|
||||
* @param {Array} array - The array from which to retrieve the value.
|
||||
* @param {Array<number>} idx - An array of indices specifying the position of the desired element in each dimension.
|
||||
* @returns {*} - The value at the specified position in the array.
|
||||
*
|
||||
* @example
|
||||
* const arr = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];
|
||||
* const index = [1, 0, 1];
|
||||
* console.log(getValue(arr, index)); // 6
|
||||
*/
|
||||
function get(array, index) {
|
||||
if (!Array.isArray(array)) {
|
||||
throw new Error('Array expected');
|
||||
}
|
||||
const size = arraySize(array);
|
||||
if (index.length !== size.length) {
|
||||
throw new _DimensionError.DimensionError(index.length, size.length);
|
||||
}
|
||||
for (let x = 0; x < index.length; x++) {
|
||||
validateIndex(index[x], size[x]);
|
||||
}
|
||||
return index.reduce((acc, curr) => acc[curr], array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function to map a multi-dimensional array.
|
||||
*
|
||||
* @param {*} value - The current value being processed in the array.
|
||||
* @param {Array} index - The index of the current value being processed in the array.
|
||||
* @param {Array} array - The array being processed.
|
||||
* @param {Function} callback - Function that produces the element of the new Array, taking three arguments: the value of the element, the index of the element, and the Array being processed.
|
||||
* @returns {*} The new array with each element being the result of the callback function.
|
||||
*/
|
||||
function recurse(value, index, array, callback) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(function (child, i) {
|
||||
// we create a copy of the index array and append the new index value
|
||||
return recurse(child, index.concat(i), array, callback);
|
||||
});
|
||||
} else {
|
||||
// invoke the callback function with the right number of arguments
|
||||
return callback(value, index, array);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep clones a multidimensional array
|
||||
* @param {Array} array
|
||||
* @returns cloned array
|
||||
*/
|
||||
function clone(array) {
|
||||
return (0, _extends2.default)([], array);
|
||||
}
|
||||
Reference in New Issue
Block a user