Skip to content

Commit

Permalink
feat: support all arithmetic operators and Math functions including s…
Browse files Browse the repository at this point in the history
…tatic versions

Closes #7
  • Loading branch information
targos committed Sep 8, 2015
1 parent d8f7f75 commit 521e4fe
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 201 deletions.
280 changes: 111 additions & 169 deletions src/matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,175 +343,6 @@ class Matrix extends Array {
return this.mulS(-1);
}

/**
* Adds a scalar or values from another matrix (in place)
* @param {number|Matrix} value
* @returns {Matrix} this
*/
add(value) {
if (typeof value === 'number') return this.addS(value);
return this.addM(value);
}

/**
* Adds a scalar to each element of the matrix (in place)
* @param {number} value
* @returns {Matrix} this
*/
addS(value) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] += value;
}
}
return this;
}

/**
* Adds the value of each element of matrix to the corresponding element of this (in place)
* @param {Matrix} matrix
* @returns {Matrix} this
*/
addM(matrix) {
checkDimensions(this, matrix);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] += matrix[i][j];
}
}
return this;
}

/**
* Subtracts a scalar or values from another matrix (in place)
* @param {number|Matrix} value
* @returns {Matrix} this
*/
sub(value) {
if (typeof value === 'number') return this.subS(value);
return this.subM(value);
}

/**
* Subtracts a scalar from each element of the matrix (in place)
* @param {number} value
* @returns {Matrix} this
*/
subS(value) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] -= value;
}
}
return this;
}

/**
* Subtracts the value of each element of matrix from the corresponding element of this (in place)
* @param {Matrix} matrix
* @returns {Matrix} this
*/
subM(matrix) {
checkDimensions(this, matrix);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] -= matrix[i][j];
}
}
return this;
}

/**
* Multiplies a scalar or values from another matrix (in place)
* @param {number|Matrix} value
* @returns {Matrix} this
*/
mul(value) {
if (typeof value === 'number') return this.mulS(value);
return this.mulM(value);
}

/**
* Multiplies a scalar with each element of the matrix (in place)
* @param {number} value
* @returns {Matrix} this
*/
mulS(value) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] *= value;
}
}
return this;
}

/**
* Multiplies the value of each element of matrix with the corresponding element of this (in place)
* @param {Matrix} matrix
* @returns {Matrix} this
*/
mulM(matrix) {
checkDimensions(this, matrix);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] *= matrix[i][j];
}
}
return this;
}

/**
* Divides by a scalar or values from another matrix (in place)
* @param {number|Matrix} value
* @returns {Matrix} this
*/
div(value) {
if (typeof value === 'number') return this.divS(value);
return this.divM(value);
}

/**
* Divides each element of the matrix by a scalar (in place)
* @param {number} value
* @returns {Matrix} this
*/
divS(value) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] /= value;
}
}
return this;
}

/**
* Divides each element of this by the corresponding element of matrix (in place)
* @param {Matrix} matrix
* @returns {Matrix} this
*/
divM(matrix) {
checkDimensions(this, matrix);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] /= matrix[i][j];
}
}
return this;
}

/**
* Sets each element of the matrix to its absolute value (in place)
* @returns {Matrix} this
*/
abs() {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] = Math.abs(this[i][j]);
}
}
return this;
}

/**
* Returns a new array from the given row index
* @param {number} index - Row index
Expand Down Expand Up @@ -1365,3 +1196,114 @@ function checkDimensions(matrix, otherMatrix) {
function compareNumbers(a, b) {
return a - b;
}

/*
Add dynamically instance and static methods for mathematical operations
*/

const inplaceOperator = `
(function %name%(value) {
if (typeof value === 'number') return this.%name%S(value);
return this.%name%M(value);
})
`;

const inplaceOperatorScalar = `
(function %name%S(value) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] = this[i][j] %op% value;
}
}
return this;
})
`;

const inplaceOperatorMatrix = `
(function %name%M(matrix) {
checkDimensions(this, matrix);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] = this[i][j] %op% matrix[i][j];
}
}
return this;
})
`;

const staticOperator = `
(function %name%(matrix, value) {
const newMatrix = new Matrix(matrix);
return newMatrix.%name%(value);
})
`;

const inplaceMethod = `
(function %name%() {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this[i][j] = %method%(this[i][j]);
}
}
return this;
})
`;

const staticMethod = `
(function %name%(matrix) {
const newMatrix = new Matrix(matrix);
return newMatrix.%name%();
})
`;

const operators = [
// Arithmetic operators
['+', 'add'],
['-', 'sub', 'subtract'],
['*', 'mul', 'multiply'],
['/', 'div', 'divide'],
['%', 'mod', 'modulus'],
// Bitwise operators
['&', 'and'],
['|', 'or'],
['^', 'xor'],
['<<', 'leftShift'],
['>>', 'rightShift'],
['>>>', 'zeroRightShift']
];

for (let operator of operators) {
for (let i = 1; i < operator.length; i++) {
Matrix.prototype[operator[i]] = eval(fillTemplateFunction(inplaceOperator, {name: operator[i], op: operator[0]}));
Matrix.prototype[operator[i] + 'S'] = eval(fillTemplateFunction(inplaceOperatorScalar, {name: operator[i] + 'S', op: operator[0]}));
Matrix.prototype[operator[i] + 'M'] = eval(fillTemplateFunction(inplaceOperatorMatrix, {name: operator[i] + 'M', op: operator[0]}));

Matrix[operator[i]] = eval(fillTemplateFunction(staticOperator, {name: operator[i]}));
}
}

const methods = [
['~', 'not']
];

[
'abs', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cbrt', 'ceil',
'clz32', 'cos', 'cosh', 'exp', 'expm1', 'floor', 'fround', 'log', 'log1p',
'log10', 'log2', 'round', 'sign', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'
].forEach(function (mathMethod) {
methods.push(['Math.' + mathMethod, mathMethod]);
});

for (let method of methods) {
for (let i = 1; i < method.length; i++) {
Matrix.prototype[method[i]] = eval(fillTemplateFunction(inplaceMethod, {name: method[i], method: method[0]}));
Matrix[method[i]] = eval(fillTemplateFunction(staticMethod, {name: method[i]}));
}
}

function fillTemplateFunction(template, values) {
for (var i in values) {
template = template.replace(new RegExp('%' + i + '%', 'g'), values[i]);
}
return template;
}
32 changes: 0 additions & 32 deletions test/matrix/abs.js

This file was deleted.

70 changes: 70 additions & 0 deletions test/matrix/dynamicMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';

var Matrix = require('../..');

describe('Dynamic methods on matrices', function () {

var matrix;

beforeEach(function () {
matrix = new Matrix([
[0, 1, 2],
[3, -4, -5],
[-6, -7, -8],
[4.39, -0.61, -12.7]
]);
});

describe('inplace', function () {
it('should return instance', function () {
matrix.abs().should.equal(matrix);
matrix.sqrt().should.equal(matrix);
});
it('abs', function () {
matrix.abs();
matrix.to2DArray().should.eql([
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[4.39, 0.61, 12.7]
]);
});
it('cbrt', function () {
matrix.fill(27);
matrix.cbrt();
matrix.to2DArray().should.eql([
[3, 3, 3],
[3, 3, 3],
[3, 3, 3],
[3, 3, 3]
]);
});
});

describe('static', function () {
it('should return a new Matrix', function () {
Matrix.abs(matrix).should.not.equal(matrix);
var abs1 = Matrix.abs(matrix);
var abs2 = Matrix.abs(matrix);
abs1.should.not.equal(abs2);
});
it('should accept 2D array input', function () {
var result = Matrix.abs([[-6]]);
result[0][0].should.equal(6);
});
it('should return a Matrix instance', function () {
var result = Matrix.abs([[-6]]);
result.should.be.instanceOf(Matrix);
});
it('cbrt', function () {
matrix.fill(27);
Matrix.cbrt(matrix).to2DArray().should.eql([
[3, 3, 3],
[3, 3, 3],
[3, 3, 3],
[3, 3, 3]
]);
});
});

});
Loading

2 comments on commit 521e4fe

@lpatiny
Copy link
Member

@lpatiny lpatiny commented on 521e4fe Sep 8, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we allow combination of 2 matrix that has not the same size ? Maybe the checkDimensions should be an options. Could be by default true.

@targos
Copy link
Member Author

@targos targos commented on 521e4fe Sep 8, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we talked about it but I don't want to do it now. It can be supported later

Please sign in to comment.