mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser and offers an integrated solution to work with numbers, big numbers, complex numbers, units, and matrices.
250 lines (230 loc) • 6.08 kB
JavaScript
// utility methods for arrays and matrices
var util = require('../util/index'),
DimensionError = require('../error/DimensionError'),
Matrix = require('./Matrix'),
isArray = util.array.isArray,
isString = util.string.isString;
/**
* Convert function arguments to an array. Arguments can have the following
* signature:
* fn()
* fn(n)
* fn(m, n, p, ...)
* fn([m, n, p, ...])
* @param {...Number | Array | Matrix} args
* @returns {Array} array
*/
exports.argsToArray = function argsToArray(args) {
var array;
if (args.length == 0) {
// fn()
array = [];
}
else if (args.length == 1) {
// fn(n)
// fn([m, n, p, ...])
array = args[0];
if (array instanceof Matrix) {
array = array.valueOf();
}
if (!isArray(array)) {
array = [array];
}
}
else {
// fn(m, n, p, ...)
array = Array.prototype.slice.apply(args);
}
return array;
};
/**
* Test whether a value is a collection: an Array or Matrix
* @param {*} x
* @returns {boolean} isCollection
*/
exports.isCollection = function isCollection (x) {
return (isArray(x) || (x instanceof Matrix));
};
/**
* Execute the callback function element wise for each element in array and any
* nested array
* Returns an array with the results
* @param {Array | Matrix} array
* @param {function} callback The callback is called with two parameters:
* value1 and value2, which contain the current
* element of both arrays.
* @return {Array | Matrix} res
*/
exports.deepMap = function deepMap(array, callback) {
if (array && (typeof array.map === 'function')) {
return array.map(function (x) {
return deepMap(x, callback);
});
}
else {
return callback(array);
}
};
/**
* Execute the callback function element wise for each entry in two given arrays,
* and for any nested array. Objects can also be scalar objects.
* Returns an array with the results.
* @param {Array | Matrix | Object} array1
* @param {Array | Matrix | Object} array2
* @param {function} callback The callback is called with two parameters:
* value1 and value2, which contain the current
* element of both arrays.
* @return {Array | Matrix} res
*/
exports.deepMap2 = function deepMap2(array1, array2, callback) {
var res, len, i;
if (isArray(array1)) {
if (isArray(array2)) {
// callback(array, array)
if (array1.length != array2.length) {
throw new DimensionError(array1.length, array2.length);
}
res = [];
len = array1.length;
for (i = 0; i < len; i++) {
res[i] = deepMap2(array1[i], array2[i], callback);
}
}
else if (array2 instanceof Matrix) {
// callback(array, matrix)
res = deepMap2(array1, array2.valueOf(), callback);
return new Matrix(res);
}
else {
// callback(array, object)
res = [];
len = array1.length;
for (i = 0; i < len; i++) {
res[i] = deepMap2(array1[i], array2, callback);
}
}
}
else if (array1 instanceof Matrix) {
if (array2 instanceof Matrix) {
// callback(matrix, matrix)
res = deepMap2(array1.valueOf(), array2.valueOf(), callback);
return new Matrix(res);
}
else {
// callback(matrix, array)
// callback(matrix, object)
res = deepMap2(array1.valueOf(), array2, callback);
return new Matrix(res);
}
}
else {
if (isArray(array2)) {
// callback(object, array)
res = [];
len = array2.length;
for (i = 0; i < len; i++) {
res[i] = deepMap2(array1, array2[i], callback);
}
}
else if (array2 instanceof Matrix) {
// callback(object, matrix)
res = deepMap2(array1, array2.valueOf(), callback);
return new Matrix(res);
}
else {
// callback(object, object)
res = callback(array1, array2);
}
}
return res;
};
/**
* Reduce a given matrix or array to a new matrix or
* array with one less dimension, applying the given
* callback in the selected dimension.
* @param {Array | Matrix} mat
* @param {Number} dim
* @param {function} callback
* @return {Array | Matrix} res
*/
exports.reduce = function reduce (mat, dim, callback) {
if (mat instanceof Matrix) {
return new Matrix(_reduce(mat.valueOf(), dim, callback));
}else {
return _reduce(mat, dim, callback);
}
};
/**
* Recursively reduce a matrix
* @param {Array} mat
* @param {Number} dim
* @param {Function} callback
* @returns {Array} ret
* @private
*/
function _reduce(mat, dim, callback){
var i, ret, val, tran;
if(dim<=0){
if( !isArray(mat[0]) ){
val = mat[0];
for(i=1; i<mat.length; i++){
val = callback(val, mat[i]);
}
return val;
}else{
tran = _switch(mat);
ret = [];
for(i=0; i<tran.length; i++){
ret[i] = _reduce(tran[i], dim-1, callback);
}
return ret
}
}else{
ret = [];
for(i=0; i<mat.length; i++){
ret[i] = _reduce(mat[i], dim-1, callback);
}
return ret;
}
}
/**
* Transpose a matrix
* @param {Array} mat
* @returns {Array} ret
* @private
*/
function _switch(mat){
var I = mat.length;
var J = mat[0].length;
var i, j;
var ret = [];
for( j=0; j<J; j++) {
var tmp = [];
for( i=0; i<I; i++) {
tmp.push(mat[i][j]);
}
ret.push(tmp);
}
return ret;
}
/**
* Recursively loop over all elements in a given multi dimensional array
* and invoke the callback on each of the elements.
* @param {Array | Matrix} array
* @param {function} callback The callback method is invoked with one
* parameter: the current element in the array
*/
exports.deepForEach = function deepForEach (array, callback) {
if (array instanceof Matrix) {
array = array.valueOf();
}
for (var i = 0, ii = array.length; i < ii; i++) {
var value = array[i];
if (isArray(value)) {
deepForEach(value, callback);
}
else {
callback(value);
}
}
};