mathjs/lib/type/Matrix.js
2015-03-05 22:06:31 -05:00

294 lines
9.1 KiB
JavaScript

'use strict';
var util = require('../util/index');
var DimensionError = require('../error/DimensionError');
var Index = require('./Index');
var string = util.string;
var array = util.array;
var object = util.object;
var isArray = Array.isArray;
var validateIndex = array.validateIndex;
var isString = string.isString;
module.exports = function (config) {
/**
* @constructor Matrix
*
* A Matrix is a wrapper around an Array. A matrix can hold a multi dimensional
* array. A matrix can be constructed as:
* var matrix = new Matrix(data)
*
* Matrix contains the functions to resize, get and set values, get the size,
* clone the matrix and to convert the matrix to a vector, array, or scalar.
* Furthermore, one can iterate over the matrix using map and forEach.
* The internal Array of the Matrix can be accessed using the function valueOf.
*
* Example usage:
* var matrix = new Matrix([[1, 2], [3, 4]]);
* matix.size(); // [2, 2]
* matrix.resize([3, 2], 5);
* matrix.valueOf(); // [[1, 2], [3, 4], [5, 5]]
* matrix.subset([1,2]) // 3 (indexes are zero-based)
*
* @param {Array | Matrix} [data] A multi dimensional array
* @param {string} [format] The Matrix storage format, defaults to 'dense'
*/
function Matrix(data, format) {
if (!(this instanceof Matrix)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if (data instanceof Matrix) {
// check we need to change storage format
if (!format || format === data._storage.format()) {
// clone storage
this._storage = data._storage.clone();
}
else {
// change storage format, TODO: find a better solution
this._storage = _createStorage(data._storage.toArray(), format);
}
}
else if (isArray(data)) {
// create storage
this._storage = _createStorage(data, format);
}
else if (typeof data === 'object' && data.mathjs === 'Matrix' && data.storage) {
// create Matrix from json representation
this._storage = _createStorage(data.storage);
}
else if (data != null) {
// unsupported type
throw new TypeError('Unsupported type of data (' + util.types.type(data) + ')');
}
else {
// create storage
this._storage = _createStorage([], format);
}
}
var _createStorage = function (data, format) {
// check data is an array
if (isArray(data)) {
// format to use
format = format || 'default';
// check format
if (!isString(format))
throw new TypeError('format must be a string value');
// get format constructor
var f = Matrix.format[format];
if (!f)
throw new SyntaxError('Unsupported Matrix Storage Format: ' + format);
// create instance
return new f(data);
}
// check it is a storage json representation
if (typeof data === 'object' && isString(data.format)) {
// get format constructor
var f = Matrix.format[data.format];
if (!f)
throw new SyntaxError('Unsupported Matrix Storage Format: ' + data.format);
// deserialize json
return f.fromJSON(data);
}
throw new SyntaxError('Unsupported data structure');
};
/**
* Test whether an object is a Matrix
* @param {*} object
* @return {Boolean} isMatrix
*/
Matrix.isMatrix = function (object) {
return (object instanceof Matrix);
};
/**
* Get a subset of the matrix, or replace a subset of the matrix.
*
* Usage:
* var subset = matrix.subset(index) // retrieve subset
* var value = matrix.subset(index, replacement) // replace subset
*
* @param {Index} index
* @param {Array | Matrix | *} [replacement]
* @param {*} [defaultValue=0] Default value, filled in on new entries when
* the matrix is resized. If not provided,
* new matrix elements will be filled with zeros.
*/
Matrix.prototype.subset = function (index, replacement, defaultValue) {
if (arguments.length < 1 || arguments.length > 3)
throw new SyntaxError('Wrong number of arguments');
// delegate to storage implementation
var result = this._storage.subset(index, replacement, defaultValue);
// check we need to wrap result in a Matrix
if (result instanceof this._storage.constructor) {
return new Matrix({
mathjs: 'Matrix',
storage: result.toJSON()
});
}
return result;
};
/**
* Get a single element from the matrix.
* @param {Number[]} index Zero-based index
* @return {*} value
*/
Matrix.prototype.get = function (index) {
// delegate to storage implementation
return this._storage.get(index);
};
/**
* Replace a single element in the matrix.
* @param {Number[]} index Zero-based index
* @param {*} value
* @param {*} [defaultValue] Default value, filled in on new entries when
* the matrix is resized. If not provided,
* new matrix elements will be left undefined.
* @return {Matrix} self
*/
Matrix.prototype.set = function (index, value, defaultValue) {
// delegate to storage implementation
this._storage.set(index, value, defaultValue);
// return the matrix itself
return this;
};
/**
* Resize the matrix
* @param {Number[]} size
* @param {*} [defaultValue=0] Default value, filled in on new entries.
* If not provided, the matrix elements will
* be filled with zeros.
* @return {Matrix} self The matrix itself is returned
*/
Matrix.prototype.resize = function (size, defaultValue) {
// delegate to storage implementation
this._storage.resize(size, defaultValue);
// return the matrix itself
return this;
};
/**
* Create a clone of the matrix
* @return {Matrix} clone
*/
Matrix.prototype.clone = function () {
return new Matrix(this);
};
/**
* Retrieve the size of the matrix.
* @returns {Number[]} size
*/
Matrix.prototype.size = function() {
// delegate to storage implementation
return this._storage.size();
};
/**
* Create a new matrix with the results of the callback function executed on
* each entry of the matrix.
* @param {function} callback The callback function is invoked with three
* parameters: the value of the element, the index
* of the element, and the Matrix being traversed.
* @return {Matrix} matrix
*/
Matrix.prototype.map = function (callback) {
// current instance
var me = this;
// create matrix from storage map
return new Matrix({
mathjs: 'Matrix',
storage: this._storage.map(callback, me).toJSON()
});
};
/**
* Execute a callback function on each entry of the matrix.
* @param {function} callback The callback function is invoked with three
* parameters: the value of the element, the index
* of the element, and the Matrix being traversed.
*/
Matrix.prototype.forEach = function (callback) {
// current instance
var me = this;
// delegate to storage implementation
this._storage.forEach(callback, me);
};
/**
* Create an Array with a copy of the data of the Matrix
* @returns {Array} array
*/
Matrix.prototype.toArray = function () {
// delegate to storage implementation
return this._storage.toArray();
};
/**
* Get the primitive value of the Matrix: a multidimensional array
* @returns {Array} array
*/
Matrix.prototype.valueOf = function () {
// delegate to storage implementation
return this._storage.valueOf();
};
/**
* Get a string representation of the matrix, with optional formatting options.
* @param {Object | Number | Function} [options] Formatting options. See
* lib/util/number:format for a
* description of the available
* options.
* @returns {String} str
*/
Matrix.prototype.format = function (options) {
// TODO: find better implementation for sparse matrix formats
return string.format(this._storage.toArray(), options);
};
/**
* Get a string representation of the matrix
* @returns {String} str
*/
Matrix.prototype.toString = function () {
// delegate to storage implementation
return this._storage.toString();
};
/**
* Get a JSON representation of the matrix
* @returns {Object}
*/
Matrix.prototype.toJSON = function () {
return {
mathjs: 'Matrix',
storage: this._storage.toJSON()
}
};
/**
* Generate a matrix from a JSON object
* @param {Object} json An object structured like
* `{"mathjs": "Matrix", data: []}`,
* where mathjs is optional
* @returns {Matrix}
*/
Matrix.fromJSON = function (json) {
return new Matrix(json);
};
// exports
return Matrix;
};