Skip to content

Commit 521e4fe

Browse files
committed
feat: support all arithmetic operators and Math functions including static versions
Closes #7
1 parent d8f7f75 commit 521e4fe

File tree

4 files changed

+245
-201
lines changed

4 files changed

+245
-201
lines changed

src/matrix.js

Lines changed: 111 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -343,175 +343,6 @@ class Matrix extends Array {
343343
return this.mulS(-1);
344344
}
345345

346-
/**
347-
* Adds a scalar or values from another matrix (in place)
348-
* @param {number|Matrix} value
349-
* @returns {Matrix} this
350-
*/
351-
add(value) {
352-
if (typeof value === 'number') return this.addS(value);
353-
return this.addM(value);
354-
}
355-
356-
/**
357-
* Adds a scalar to each element of the matrix (in place)
358-
* @param {number} value
359-
* @returns {Matrix} this
360-
*/
361-
addS(value) {
362-
for (var i = 0; i < this.rows; i++) {
363-
for (var j = 0; j < this.columns; j++) {
364-
this[i][j] += value;
365-
}
366-
}
367-
return this;
368-
}
369-
370-
/**
371-
* Adds the value of each element of matrix to the corresponding element of this (in place)
372-
* @param {Matrix} matrix
373-
* @returns {Matrix} this
374-
*/
375-
addM(matrix) {
376-
checkDimensions(this, matrix);
377-
for (var i = 0; i < this.rows; i++) {
378-
for (var j = 0; j < this.columns; j++) {
379-
this[i][j] += matrix[i][j];
380-
}
381-
}
382-
return this;
383-
}
384-
385-
/**
386-
* Subtracts a scalar or values from another matrix (in place)
387-
* @param {number|Matrix} value
388-
* @returns {Matrix} this
389-
*/
390-
sub(value) {
391-
if (typeof value === 'number') return this.subS(value);
392-
return this.subM(value);
393-
}
394-
395-
/**
396-
* Subtracts a scalar from each element of the matrix (in place)
397-
* @param {number} value
398-
* @returns {Matrix} this
399-
*/
400-
subS(value) {
401-
for (var i = 0; i < this.rows; i++) {
402-
for (var j = 0; j < this.columns; j++) {
403-
this[i][j] -= value;
404-
}
405-
}
406-
return this;
407-
}
408-
409-
/**
410-
* Subtracts the value of each element of matrix from the corresponding element of this (in place)
411-
* @param {Matrix} matrix
412-
* @returns {Matrix} this
413-
*/
414-
subM(matrix) {
415-
checkDimensions(this, matrix);
416-
for (var i = 0; i < this.rows; i++) {
417-
for (var j = 0; j < this.columns; j++) {
418-
this[i][j] -= matrix[i][j];
419-
}
420-
}
421-
return this;
422-
}
423-
424-
/**
425-
* Multiplies a scalar or values from another matrix (in place)
426-
* @param {number|Matrix} value
427-
* @returns {Matrix} this
428-
*/
429-
mul(value) {
430-
if (typeof value === 'number') return this.mulS(value);
431-
return this.mulM(value);
432-
}
433-
434-
/**
435-
* Multiplies a scalar with each element of the matrix (in place)
436-
* @param {number} value
437-
* @returns {Matrix} this
438-
*/
439-
mulS(value) {
440-
for (var i = 0; i < this.rows; i++) {
441-
for (var j = 0; j < this.columns; j++) {
442-
this[i][j] *= value;
443-
}
444-
}
445-
return this;
446-
}
447-
448-
/**
449-
* Multiplies the value of each element of matrix with the corresponding element of this (in place)
450-
* @param {Matrix} matrix
451-
* @returns {Matrix} this
452-
*/
453-
mulM(matrix) {
454-
checkDimensions(this, matrix);
455-
for (var i = 0; i < this.rows; i++) {
456-
for (var j = 0; j < this.columns; j++) {
457-
this[i][j] *= matrix[i][j];
458-
}
459-
}
460-
return this;
461-
}
462-
463-
/**
464-
* Divides by a scalar or values from another matrix (in place)
465-
* @param {number|Matrix} value
466-
* @returns {Matrix} this
467-
*/
468-
div(value) {
469-
if (typeof value === 'number') return this.divS(value);
470-
return this.divM(value);
471-
}
472-
473-
/**
474-
* Divides each element of the matrix by a scalar (in place)
475-
* @param {number} value
476-
* @returns {Matrix} this
477-
*/
478-
divS(value) {
479-
for (var i = 0; i < this.rows; i++) {
480-
for (var j = 0; j < this.columns; j++) {
481-
this[i][j] /= value;
482-
}
483-
}
484-
return this;
485-
}
486-
487-
/**
488-
* Divides each element of this by the corresponding element of matrix (in place)
489-
* @param {Matrix} matrix
490-
* @returns {Matrix} this
491-
*/
492-
divM(matrix) {
493-
checkDimensions(this, matrix);
494-
for (var i = 0; i < this.rows; i++) {
495-
for (var j = 0; j < this.columns; j++) {
496-
this[i][j] /= matrix[i][j];
497-
}
498-
}
499-
return this;
500-
}
501-
502-
/**
503-
* Sets each element of the matrix to its absolute value (in place)
504-
* @returns {Matrix} this
505-
*/
506-
abs() {
507-
for (var i = 0; i < this.rows; i++) {
508-
for (var j = 0; j < this.columns; j++) {
509-
this[i][j] = Math.abs(this[i][j]);
510-
}
511-
}
512-
return this;
513-
}
514-
515346
/**
516347
* Returns a new array from the given row index
517348
* @param {number} index - Row index
@@ -1365,3 +1196,114 @@ function checkDimensions(matrix, otherMatrix) {
13651196
function compareNumbers(a, b) {
13661197
return a - b;
13671198
}
1199+
1200+
/*
1201+
Add dynamically instance and static methods for mathematical operations
1202+
*/
1203+
1204+
const inplaceOperator = `
1205+
(function %name%(value) {
1206+
if (typeof value === 'number') return this.%name%S(value);
1207+
return this.%name%M(value);
1208+
})
1209+
`;
1210+
1211+
const inplaceOperatorScalar = `
1212+
(function %name%S(value) {
1213+
for (var i = 0; i < this.rows; i++) {
1214+
for (var j = 0; j < this.columns; j++) {
1215+
this[i][j] = this[i][j] %op% value;
1216+
}
1217+
}
1218+
return this;
1219+
})
1220+
`;
1221+
1222+
const inplaceOperatorMatrix = `
1223+
(function %name%M(matrix) {
1224+
checkDimensions(this, matrix);
1225+
for (var i = 0; i < this.rows; i++) {
1226+
for (var j = 0; j < this.columns; j++) {
1227+
this[i][j] = this[i][j] %op% matrix[i][j];
1228+
}
1229+
}
1230+
return this;
1231+
})
1232+
`;
1233+
1234+
const staticOperator = `
1235+
(function %name%(matrix, value) {
1236+
const newMatrix = new Matrix(matrix);
1237+
return newMatrix.%name%(value);
1238+
})
1239+
`;
1240+
1241+
const inplaceMethod = `
1242+
(function %name%() {
1243+
for (var i = 0; i < this.rows; i++) {
1244+
for (var j = 0; j < this.columns; j++) {
1245+
this[i][j] = %method%(this[i][j]);
1246+
}
1247+
}
1248+
return this;
1249+
})
1250+
`;
1251+
1252+
const staticMethod = `
1253+
(function %name%(matrix) {
1254+
const newMatrix = new Matrix(matrix);
1255+
return newMatrix.%name%();
1256+
})
1257+
`;
1258+
1259+
const operators = [
1260+
// Arithmetic operators
1261+
['+', 'add'],
1262+
['-', 'sub', 'subtract'],
1263+
['*', 'mul', 'multiply'],
1264+
['/', 'div', 'divide'],
1265+
['%', 'mod', 'modulus'],
1266+
// Bitwise operators
1267+
['&', 'and'],
1268+
['|', 'or'],
1269+
['^', 'xor'],
1270+
['<<', 'leftShift'],
1271+
['>>', 'rightShift'],
1272+
['>>>', 'zeroRightShift']
1273+
];
1274+
1275+
for (let operator of operators) {
1276+
for (let i = 1; i < operator.length; i++) {
1277+
Matrix.prototype[operator[i]] = eval(fillTemplateFunction(inplaceOperator, {name: operator[i], op: operator[0]}));
1278+
Matrix.prototype[operator[i] + 'S'] = eval(fillTemplateFunction(inplaceOperatorScalar, {name: operator[i] + 'S', op: operator[0]}));
1279+
Matrix.prototype[operator[i] + 'M'] = eval(fillTemplateFunction(inplaceOperatorMatrix, {name: operator[i] + 'M', op: operator[0]}));
1280+
1281+
Matrix[operator[i]] = eval(fillTemplateFunction(staticOperator, {name: operator[i]}));
1282+
}
1283+
}
1284+
1285+
const methods = [
1286+
['~', 'not']
1287+
];
1288+
1289+
[
1290+
'abs', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cbrt', 'ceil',
1291+
'clz32', 'cos', 'cosh', 'exp', 'expm1', 'floor', 'fround', 'log', 'log1p',
1292+
'log10', 'log2', 'round', 'sign', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'
1293+
].forEach(function (mathMethod) {
1294+
methods.push(['Math.' + mathMethod, mathMethod]);
1295+
});
1296+
1297+
for (let method of methods) {
1298+
for (let i = 1; i < method.length; i++) {
1299+
Matrix.prototype[method[i]] = eval(fillTemplateFunction(inplaceMethod, {name: method[i], method: method[0]}));
1300+
Matrix[method[i]] = eval(fillTemplateFunction(staticMethod, {name: method[i]}));
1301+
}
1302+
}
1303+
1304+
function fillTemplateFunction(template, values) {
1305+
for (var i in values) {
1306+
template = template.replace(new RegExp('%' + i + '%', 'g'), values[i]);
1307+
}
1308+
return template;
1309+
}

test/matrix/abs.js

Lines changed: 0 additions & 32 deletions
This file was deleted.

test/matrix/dynamicMethods.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict';
2+
3+
var Matrix = require('../..');
4+
5+
describe('Dynamic methods on matrices', function () {
6+
7+
var matrix;
8+
9+
beforeEach(function () {
10+
matrix = new Matrix([
11+
[0, 1, 2],
12+
[3, -4, -5],
13+
[-6, -7, -8],
14+
[4.39, -0.61, -12.7]
15+
]);
16+
});
17+
18+
describe('inplace', function () {
19+
it('should return instance', function () {
20+
matrix.abs().should.equal(matrix);
21+
matrix.sqrt().should.equal(matrix);
22+
});
23+
it('abs', function () {
24+
matrix.abs();
25+
matrix.to2DArray().should.eql([
26+
[0, 1, 2],
27+
[3, 4, 5],
28+
[6, 7, 8],
29+
[4.39, 0.61, 12.7]
30+
]);
31+
});
32+
it('cbrt', function () {
33+
matrix.fill(27);
34+
matrix.cbrt();
35+
matrix.to2DArray().should.eql([
36+
[3, 3, 3],
37+
[3, 3, 3],
38+
[3, 3, 3],
39+
[3, 3, 3]
40+
]);
41+
});
42+
});
43+
44+
describe('static', function () {
45+
it('should return a new Matrix', function () {
46+
Matrix.abs(matrix).should.not.equal(matrix);
47+
var abs1 = Matrix.abs(matrix);
48+
var abs2 = Matrix.abs(matrix);
49+
abs1.should.not.equal(abs2);
50+
});
51+
it('should accept 2D array input', function () {
52+
var result = Matrix.abs([[-6]]);
53+
result[0][0].should.equal(6);
54+
});
55+
it('should return a Matrix instance', function () {
56+
var result = Matrix.abs([[-6]]);
57+
result.should.be.instanceOf(Matrix);
58+
});
59+
it('cbrt', function () {
60+
matrix.fill(27);
61+
Matrix.cbrt(matrix).to2DArray().should.eql([
62+
[3, 3, 3],
63+
[3, 3, 3],
64+
[3, 3, 3],
65+
[3, 3, 3]
66+
]);
67+
});
68+
});
69+
70+
});

0 commit comments

Comments
 (0)