Skip to content

Commit 2c81deb

Browse files
committedDec 19, 2020
Add Matrices section with basic Matrix operations (multiplication, transposition, etc.)
1 parent e220450 commit 2c81deb

File tree

5 files changed

+852
-21
lines changed

5 files changed

+852
-21
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ a set of rules that precisely define a sequence of operations.
7777
* `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion
7878
* `B` [Fast Powering](src/algorithms/math/fast-powering)
7979
* `B` [Horner's method](src/algorithms/math/horner-method) - polynomial evaluation
80+
* `B` [Matrices](src/algorithms/math/matrix) - matrices and basic matrix operations (multiplication, transposition, etc.)
8081
* `A` [Integer Partition](src/algorithms/math/integer-partition)
8182
* `A` [Square Root](src/algorithms/math/square-root) - Newton's method
8283
* `A` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons

‎src/algorithms/cryptography/hill-cipher/hillCipher.js

+24-21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as mtrx from '../../math/matrix/Matrix';
2+
13
// The code of an 'A' character (equals to 65).
24
const alphabetCodeShift = 'A'.codePointAt(0);
35
const englishAlphabetSize = 26;
@@ -15,33 +17,36 @@ const generateKeyMatrix = (keyString) => {
1517
'Invalid key string length. The square root of the key string must be an integer',
1618
);
1719
}
18-
const keyMatrix = [];
1920
let keyStringIndex = 0;
20-
for (let i = 0; i < matrixSize; i += 1) {
21-
const keyMatrixRow = [];
22-
for (let j = 0; j < matrixSize; j += 1) {
21+
return mtrx.generate(
22+
[matrixSize, matrixSize],
23+
// Callback to get a value of each matrix cell.
24+
// The order the matrix is being filled in is from left to right, from top to bottom.
25+
() => {
2326
// A → 0, B → 1, ..., a → 32, b → 33, ...
2427
const charCodeShifted = (keyString.codePointAt(keyStringIndex)) % alphabetCodeShift;
25-
keyMatrixRow.push(charCodeShifted);
2628
keyStringIndex += 1;
27-
}
28-
keyMatrix.push(keyMatrixRow);
29-
}
30-
return keyMatrix;
29+
return charCodeShifted;
30+
},
31+
);
3132
};
3233

3334
/**
3435
* Generates a message vector from a given message.
3536
*
3637
* @param {string} message - the message to encrypt.
37-
* @return {number[]} messageVector
38+
* @return {number[][]} messageVector
3839
*/
3940
const generateMessageVector = (message) => {
40-
const messageVector = [];
41-
for (let i = 0; i < message.length; i += 1) {
42-
messageVector.push(message.codePointAt(i) % alphabetCodeShift);
43-
}
44-
return messageVector;
41+
return mtrx.generate(
42+
[message.length, 1],
43+
// Callback to get a value of each matrix cell.
44+
// The order the matrix is being filled in is from left to right, from top to bottom.
45+
(cellIndices) => {
46+
const rowIndex = cellIndices[0];
47+
return message.codePointAt(rowIndex) % alphabetCodeShift;
48+
},
49+
);
4550
};
4651

4752
/**
@@ -59,19 +64,17 @@ export function hillCipherEncrypt(message, keyString) {
5964
}
6065

6166
const keyMatrix = generateKeyMatrix(keyString);
67+
const messageVector = generateMessageVector(message);
6268

6369
// keyString.length must equal to square of message.length
6470
if (keyMatrix.length !== message.length) {
6571
throw new Error('Invalid key string length. The key length must be a square of message length');
6672
}
6773

68-
const messageVector = generateMessageVector(message);
74+
const cipherVector = mtrx.dot(keyMatrix, messageVector);
6975
let cipherString = '';
70-
for (let row = 0; row < keyMatrix.length; row += 1) {
71-
let item = 0;
72-
for (let column = 0; column < keyMatrix.length; column += 1) {
73-
item += keyMatrix[row][column] * messageVector[column];
74-
}
76+
for (let row = 0; row < cipherVector.length; row += 1) {
77+
const item = cipherVector[row];
7578
cipherString += String.fromCharCode((item % englishAlphabetSize) + alphabetCodeShift);
7679
}
7780

‎src/algorithms/math/matrix/Matrix.js

+309
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/**
2+
* @typedef {number} Cell
3+
* @typedef {Cell[][]|Cell[][][]} Matrix
4+
* @typedef {number[]} Shape
5+
* @typedef {number[]} CellIndices
6+
*/
7+
8+
/**
9+
* Gets the matrix's shape.
10+
*
11+
* @param {Matrix} m
12+
* @returns {Shape}
13+
*/
14+
export const shape = (m) => {
15+
const shapes = [];
16+
let dimension = m;
17+
while (dimension && Array.isArray(dimension)) {
18+
shapes.push(dimension.length);
19+
dimension = (dimension.length && [...dimension][0]) || null;
20+
}
21+
return shapes;
22+
};
23+
24+
/**
25+
* Checks if matrix has a correct type.
26+
*
27+
* @param {Matrix} m
28+
* @throws {Error}
29+
*/
30+
const validateType = (m) => {
31+
if (
32+
!m
33+
|| !Array.isArray(m)
34+
|| !Array.isArray(m[0])
35+
) {
36+
throw new Error('Invalid matrix format');
37+
}
38+
};
39+
40+
/**
41+
* Checks if matrix is two dimensional.
42+
*
43+
* @param {Matrix} m
44+
* @throws {Error}
45+
*/
46+
const validate2D = (m) => {
47+
validateType(m);
48+
const aShape = shape(m);
49+
if (aShape.length !== 2) {
50+
throw new Error('Matrix is not of 2D shape');
51+
}
52+
};
53+
54+
/**
55+
* Validates that matrices are of the same shape.
56+
*
57+
* @param {Matrix} a
58+
* @param {Matrix} b
59+
* @trows {Error}
60+
*/
61+
const validateSameShape = (a, b) => {
62+
validateType(a);
63+
validateType(b);
64+
65+
const aShape = shape(a);
66+
const bShape = shape(b);
67+
68+
if (aShape.length !== bShape.length) {
69+
throw new Error('Matrices have different dimensions');
70+
}
71+
72+
while (aShape.length && bShape.length) {
73+
if (aShape.pop() !== bShape.pop()) {
74+
throw new Error('Matrices have different shapes');
75+
}
76+
}
77+
};
78+
79+
/**
80+
* Generates the matrix of specific shape with specific values.
81+
*
82+
* @param {Shape} mShape - the shape of the matrix to generate
83+
* @param {function({CellIndex}): Cell} fill - cell values of a generated matrix.
84+
* @returns {Matrix}
85+
*/
86+
export const generate = (mShape, fill) => {
87+
/**
88+
* Generates the matrix recursively.
89+
*
90+
* @param {Shape} recShape - the shape of the matrix to generate
91+
* @param {CellIndices} recIndices
92+
* @returns {Matrix}
93+
*/
94+
const generateRecursively = (recShape, recIndices) => {
95+
if (recShape.length === 1) {
96+
return Array(recShape[0])
97+
.fill(null)
98+
.map((cellValue, cellIndex) => fill([...recIndices, cellIndex]));
99+
}
100+
const m = [];
101+
for (let i = 0; i < recShape[0]; i += 1) {
102+
m.push(generateRecursively(recShape.slice(1), [...recIndices, i]));
103+
}
104+
return m;
105+
};
106+
107+
return generateRecursively(mShape, []);
108+
};
109+
110+
/**
111+
* Generates the matrix of zeros of specified shape.
112+
*
113+
* @param {Shape} mShape - shape of the matrix
114+
* @returns {Matrix}
115+
*/
116+
export const zeros = (mShape) => {
117+
return generate(mShape, () => 0);
118+
};
119+
120+
/**
121+
* @param {Matrix} a
122+
* @param {Matrix} b
123+
* @return Matrix
124+
* @throws {Error}
125+
*/
126+
export const dot = (a, b) => {
127+
// Validate inputs.
128+
validate2D(a);
129+
validate2D(b);
130+
131+
// Check dimensions.
132+
const aShape = shape(a);
133+
const bShape = shape(b);
134+
if (aShape[1] !== bShape[0]) {
135+
throw new Error('Matrices have incompatible shape for multiplication');
136+
}
137+
138+
// Perform matrix multiplication.
139+
const outputShape = [aShape[0], bShape[1]];
140+
const c = zeros(outputShape);
141+
142+
for (let bCol = 0; bCol < b[0].length; bCol += 1) {
143+
for (let aRow = 0; aRow < a.length; aRow += 1) {
144+
let cellSum = 0;
145+
for (let aCol = 0; aCol < a[aRow].length; aCol += 1) {
146+
cellSum += a[aRow][aCol] * b[aCol][bCol];
147+
}
148+
c[aRow][bCol] = cellSum;
149+
}
150+
}
151+
152+
return c;
153+
};
154+
155+
/**
156+
* Transposes the matrix.
157+
*
158+
* @param {Matrix} m
159+
* @returns Matrix
160+
* @throws {Error}
161+
*/
162+
export const t = (m) => {
163+
validate2D(m);
164+
const mShape = shape(m);
165+
const transposed = zeros([mShape[1], mShape[0]]);
166+
for (let row = 0; row < m.length; row += 1) {
167+
for (let col = 0; col < m[0].length; col += 1) {
168+
transposed[col][row] = m[row][col];
169+
}
170+
}
171+
return transposed;
172+
};
173+
174+
/**
175+
* Traverses the matrix.
176+
*
177+
* @param {Matrix} m
178+
* @param {function(indices: CellIndices, c: Cell)} visit
179+
*/
180+
const walk = (m, visit) => {
181+
/**
182+
* Traverses the matrix recursively.
183+
*
184+
* @param {Matrix} recM
185+
* @param {CellIndices} cellIndices
186+
* @return {Matrix}
187+
*/
188+
const recWalk = (recM, cellIndices) => {
189+
const recMShape = shape(recM);
190+
191+
if (recMShape.length === 1) {
192+
for (let i = 0; i < recM.length; i += 1) {
193+
visit([...cellIndices, i], recM[i]);
194+
}
195+
}
196+
for (let i = 0; i < recM.length; i += 1) {
197+
recWalk(recM[i], [...cellIndices, i]);
198+
}
199+
};
200+
201+
recWalk(m, []);
202+
};
203+
204+
/**
205+
* Gets the matrix cell value at specific index.
206+
*
207+
* @param {Matrix} m - Matrix that contains the cell that needs to be updated
208+
* @param {CellIndices} cellIndices - Array of cell indices
209+
* @return {Cell}
210+
*/
211+
const getCellAtIndex = (m, cellIndices) => {
212+
// We start from the row at specific index.
213+
let cell = m[cellIndices[0]];
214+
// Going deeper into the next dimensions but not to the last one to preserve
215+
// the pointer to the last dimension array.
216+
for (let dimIdx = 1; dimIdx < cellIndices.length - 1; dimIdx += 1) {
217+
cell = cell[cellIndices[dimIdx]];
218+
}
219+
// At this moment the cell variable points to the array at the last needed dimension.
220+
return cell[cellIndices[cellIndices.length - 1]];
221+
};
222+
223+
/**
224+
* Update the matrix cell at specific index.
225+
*
226+
* @param {Matrix} m - Matrix that contains the cell that needs to be updated
227+
* @param {CellIndices} cellIndices - Array of cell indices
228+
* @param {Cell} cellValue - New cell value
229+
*/
230+
const updateCellAtIndex = (m, cellIndices, cellValue) => {
231+
// We start from the row at specific index.
232+
let cell = m[cellIndices[0]];
233+
// Going deeper into the next dimensions but not to the last one to preserve
234+
// the pointer to the last dimension array.
235+
for (let dimIdx = 1; dimIdx < cellIndices.length - 1; dimIdx += 1) {
236+
cell = cell[cellIndices[dimIdx]];
237+
}
238+
// At this moment the cell variable points to the array at the last needed dimension.
239+
cell[cellIndices[cellIndices.length - 1]] = cellValue;
240+
};
241+
242+
/**
243+
* Adds two matrices element-wise.
244+
*
245+
* @param {Matrix} a
246+
* @param {Matrix} b
247+
* @return {Matrix}
248+
*/
249+
export const add = (a, b) => {
250+
validateSameShape(a, b);
251+
const result = zeros(shape(a));
252+
253+
walk(a, (cellIndices, cellValue) => {
254+
updateCellAtIndex(result, cellIndices, cellValue);
255+
});
256+
257+
walk(b, (cellIndices, cellValue) => {
258+
const currentCellValue = getCellAtIndex(result, cellIndices);
259+
updateCellAtIndex(result, cellIndices, currentCellValue + cellValue);
260+
});
261+
262+
return result;
263+
};
264+
265+
/**
266+
* Multiplies two matrices element-wise.
267+
*
268+
* @param {Matrix} a
269+
* @param {Matrix} b
270+
* @return {Matrix}
271+
*/
272+
export const mul = (a, b) => {
273+
validateSameShape(a, b);
274+
const result = zeros(shape(a));
275+
276+
walk(a, (cellIndices, cellValue) => {
277+
updateCellAtIndex(result, cellIndices, cellValue);
278+
});
279+
280+
walk(b, (cellIndices, cellValue) => {
281+
const currentCellValue = getCellAtIndex(result, cellIndices);
282+
updateCellAtIndex(result, cellIndices, currentCellValue * cellValue);
283+
});
284+
285+
return result;
286+
};
287+
288+
/**
289+
* Subtract two matrices element-wise.
290+
*
291+
* @param {Matrix} a
292+
* @param {Matrix} b
293+
* @return {Matrix}
294+
*/
295+
export const sub = (a, b) => {
296+
validateSameShape(a, b);
297+
const result = zeros(shape(a));
298+
299+
walk(a, (cellIndices, cellValue) => {
300+
updateCellAtIndex(result, cellIndices, cellValue);
301+
});
302+
303+
walk(b, (cellIndices, cellValue) => {
304+
const currentCellValue = getCellAtIndex(result, cellIndices);
305+
updateCellAtIndex(result, cellIndices, currentCellValue - cellValue);
306+
});
307+
308+
return result;
309+
};

‎src/algorithms/math/matrix/README.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Matrices
2+
3+
In mathematics, a **matrix** (plural **matrices**) is a rectangular array or table of numbers, symbols, or expressions, arranged in rows and columns. For example, the dimension of the matrix below is `2 × 3` (read "two by three"), because there are two rows and three columns:
4+
5+
```
6+
| 1 9 -13 |
7+
| 20 5 -6 |
8+
```
9+
10+
![An `m × n` matrix](https://upload.wikimedia.org/wikipedia/commons/b/bf/Matris.png)
11+
12+
An `m × n` matrix: the `m` rows are horizontal, and the `n` columns are vertical. Each element of a matrix is often denoted by a variable with two subscripts. For example, <i>a<sub>2,1</sub></i> represents the element at the second row and first column of the matrix
13+
14+
## Operations on matrices
15+
16+
### Addition
17+
18+
To add two matrices: add the numbers in the matching positions:
19+
20+
![Matrices addition](https://www.mathsisfun.com/algebra/images/matrix-addition.gif)
21+
22+
The two matrices must be the same size, i.e. the rows must match in size, and the columns must match in size.
23+
24+
### Subtracting
25+
26+
To subtract two matrices: subtract the numbers in the matching positions:
27+
28+
![Matrices subtraction](https://www.mathsisfun.com/algebra/images/matrix-subtraction.gif)
29+
30+
### Multiply by a Constant
31+
32+
We can multiply a matrix by a constant (the value 2 in this case):
33+
34+
![Matrices multiplication be a constant](https://www.mathsisfun.com/algebra/images/matrix-multiply-constant.gif)
35+
36+
### Multiplying by Another Matrix
37+
38+
To multiply a matrix by another matrix we need to do the [dot product](https://www.mathsisfun.com/algebra/vectors-dot-product.html) of rows and columns.
39+
40+
To work out the answer for the **1st row** and **1st column**:
41+
42+
![Matrices multiplication - 1st step](https://www.mathsisfun.com/algebra/images/matrix-multiply-a.svg)
43+
44+
Here it is for the 1st row and 2nd column:
45+
46+
![Matrices multiplication - 2st step](https://www.mathsisfun.com/algebra/images/matrix-multiply-b.svg)
47+
48+
If we'll do the same for the rest of the rows and columns we'll get the following resulting matrix:
49+
50+
![Matrices multiplication - Result](https://www.mathsisfun.com/algebra/images/matrix-multiply-c.svg)
51+
52+
### Transposing
53+
54+
To "transpose" a matrix, swap the rows and columns.
55+
56+
We put a "T" in the top right-hand corner to mean transpose:
57+
58+
![Transposing](https://www.mathsisfun.com/algebra/images/matrix-transpose.gif)
59+
60+
## References
61+
62+
- [Matrices on MathIsFun](https://www.mathsisfun.com/algebra/matrix-introduction.html)
63+
- [Matrix on Wikipedia](https://en.wikipedia.org/wiki/Matrix_(mathematics))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,455 @@
1+
import * as mtrx from '../Matrix';
2+
3+
describe('Matrix', () => {
4+
it('should throw when trying to add matrices of invalid shapes', () => {
5+
expect(
6+
() => mtrx.dot([0], [1]),
7+
).toThrowError('Invalid matrix format');
8+
expect(
9+
() => mtrx.dot([[0]], [1]),
10+
).toThrowError('Invalid matrix format');
11+
expect(
12+
() => mtrx.dot([[[0]]], [[1]]),
13+
).toThrowError('Matrix is not of 2D shape');
14+
expect(
15+
() => mtrx.dot([[0]], [[1], [2]]),
16+
).toThrowError('Matrices have incompatible shape for multiplication');
17+
});
18+
19+
it('should calculate matrices dimensions', () => {
20+
expect(mtrx.shape([])).toEqual([0]);
21+
22+
expect(mtrx.shape([
23+
[],
24+
])).toEqual([1, 0]);
25+
26+
expect(mtrx.shape([
27+
[0],
28+
])).toEqual([1, 1]);
29+
30+
expect(mtrx.shape([
31+
[0, 0],
32+
])).toEqual([1, 2]);
33+
34+
expect(mtrx.shape([
35+
[0, 0],
36+
[0, 0],
37+
])).toEqual([2, 2]);
38+
39+
expect(mtrx.shape([
40+
[0, 0, 0],
41+
[0, 0, 0],
42+
])).toEqual([2, 3]);
43+
44+
expect(mtrx.shape([
45+
[0, 0],
46+
[0, 0],
47+
[0, 0],
48+
])).toEqual([3, 2]);
49+
50+
expect(mtrx.shape([
51+
[0, 0, 0],
52+
[0, 0, 0],
53+
[0, 0, 0],
54+
])).toEqual([3, 3]);
55+
56+
expect(mtrx.shape([
57+
[0],
58+
[0],
59+
[0],
60+
])).toEqual([3, 1]);
61+
62+
expect(mtrx.shape([
63+
[[0], [0], [0]],
64+
[[0], [0], [0]],
65+
[[0], [0], [0]],
66+
])).toEqual([3, 3, 1]);
67+
68+
expect(mtrx.shape([
69+
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
70+
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
71+
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
72+
])).toEqual([3, 3, 3]);
73+
});
74+
75+
it('should generate the matrix of zeros', () => {
76+
expect(mtrx.zeros([1, 0])).toEqual([
77+
[],
78+
]);
79+
80+
expect(mtrx.zeros([1, 1])).toEqual([
81+
[0],
82+
]);
83+
84+
expect(mtrx.zeros([1, 3])).toEqual([
85+
[0, 0, 0],
86+
]);
87+
88+
expect(mtrx.zeros([3, 3])).toEqual([
89+
[0, 0, 0],
90+
[0, 0, 0],
91+
[0, 0, 0],
92+
]);
93+
94+
expect(mtrx.zeros([3, 3, 1])).toEqual([
95+
[[0], [0], [0]],
96+
[[0], [0], [0]],
97+
[[0], [0], [0]],
98+
]);
99+
});
100+
101+
it('should generate the matrix with custom values', () => {
102+
expect(mtrx.generate([1, 0], () => 1)).toEqual([
103+
[],
104+
]);
105+
106+
expect(mtrx.generate([1, 1], () => 1)).toEqual([
107+
[1],
108+
]);
109+
110+
expect(mtrx.generate([1, 3], () => 1)).toEqual([
111+
[1, 1, 1],
112+
]);
113+
114+
expect(mtrx.generate([3, 3], () => 1)).toEqual([
115+
[1, 1, 1],
116+
[1, 1, 1],
117+
[1, 1, 1],
118+
]);
119+
120+
expect(mtrx.generate([3, 3, 1], () => 1)).toEqual([
121+
[[1], [1], [1]],
122+
[[1], [1], [1]],
123+
[[1], [1], [1]],
124+
]);
125+
});
126+
127+
it('should generate a custom matrix based on specific cell indices', () => {
128+
const indicesCallback = jest.fn((indices) => {
129+
return indices[0] * 10 + indices[1];
130+
});
131+
const m = mtrx.generate([3, 3], indicesCallback);
132+
133+
expect(indicesCallback).toHaveBeenCalledTimes(3 * 3);
134+
expect(indicesCallback.mock.calls[0][0]).toEqual([0, 0]);
135+
expect(indicesCallback.mock.calls[1][0]).toEqual([0, 1]);
136+
expect(indicesCallback.mock.calls[2][0]).toEqual([0, 2]);
137+
expect(indicesCallback.mock.calls[3][0]).toEqual([1, 0]);
138+
expect(indicesCallback.mock.calls[4][0]).toEqual([1, 1]);
139+
expect(indicesCallback.mock.calls[5][0]).toEqual([1, 2]);
140+
expect(indicesCallback.mock.calls[6][0]).toEqual([2, 0]);
141+
expect(indicesCallback.mock.calls[7][0]).toEqual([2, 1]);
142+
expect(indicesCallback.mock.calls[8][0]).toEqual([2, 2]);
143+
expect(m).toEqual([
144+
[0, 1, 2],
145+
[10, 11, 12],
146+
[20, 21, 22],
147+
]);
148+
});
149+
150+
it('should multiply two matrices', () => {
151+
let c;
152+
c = mtrx.dot(
153+
[
154+
[1, 2],
155+
[3, 4],
156+
],
157+
[
158+
[5, 6],
159+
[7, 8],
160+
],
161+
);
162+
expect(mtrx.shape(c)).toEqual([2, 2]);
163+
expect(c).toEqual([
164+
[19, 22],
165+
[43, 50],
166+
]);
167+
168+
c = mtrx.dot(
169+
[
170+
[1, 2],
171+
[3, 4],
172+
],
173+
[
174+
[5],
175+
[6],
176+
],
177+
);
178+
expect(mtrx.shape(c)).toEqual([2, 1]);
179+
expect(c).toEqual([
180+
[17],
181+
[39],
182+
]);
183+
184+
c = mtrx.dot(
185+
[
186+
[1, 2, 3],
187+
[4, 5, 6],
188+
],
189+
[
190+
[7, 8],
191+
[9, 10],
192+
[11, 12],
193+
],
194+
);
195+
expect(mtrx.shape(c)).toEqual([2, 2]);
196+
expect(c).toEqual([
197+
[58, 64],
198+
[139, 154],
199+
]);
200+
201+
c = mtrx.dot(
202+
[
203+
[3, 4, 2],
204+
],
205+
[
206+
[13, 9, 7, 5],
207+
[8, 7, 4, 6],
208+
[6, 4, 0, 3],
209+
],
210+
);
211+
expect(mtrx.shape(c)).toEqual([1, 4]);
212+
expect(c).toEqual([
213+
[83, 63, 37, 45],
214+
]);
215+
});
216+
217+
it('should transpose matrices', () => {
218+
expect(mtrx.t([[1, 2, 3]])).toEqual([
219+
[1],
220+
[2],
221+
[3],
222+
]);
223+
224+
expect(mtrx.t([
225+
[1],
226+
[2],
227+
[3],
228+
])).toEqual([
229+
[1, 2, 3],
230+
]);
231+
232+
expect(mtrx.t([
233+
[1, 2, 3],
234+
[4, 5, 6],
235+
])).toEqual([
236+
[1, 4],
237+
[2, 5],
238+
[3, 6],
239+
]);
240+
241+
expect(mtrx.t([
242+
[1, 2, 3],
243+
[4, 5, 6],
244+
[7, 8, 9],
245+
])).toEqual([
246+
[1, 4, 7],
247+
[2, 5, 8],
248+
[3, 6, 9],
249+
]);
250+
});
251+
252+
it('should throw when trying to transpose non 2D matrix', () => {
253+
expect(() => {
254+
mtrx.t([[[1]]]);
255+
}).toThrowError('Matrix is not of 2D shape');
256+
});
257+
258+
it('should add two matrices', () => {
259+
expect(mtrx.add([[1]], [[2]])).toEqual([[3]]);
260+
261+
expect(mtrx.add(
262+
[[1, 2, 3]],
263+
[[4, 5, 6]],
264+
))
265+
.toEqual(
266+
[[5, 7, 9]],
267+
);
268+
269+
expect(mtrx.add(
270+
[[1], [2], [3]],
271+
[[4], [5], [6]],
272+
))
273+
.toEqual(
274+
[[5], [7], [9]],
275+
);
276+
277+
expect(mtrx.add(
278+
[
279+
[1, 2, 3],
280+
[4, 5, 6],
281+
[7, 8, 9],
282+
],
283+
[
284+
[10, 11, 12],
285+
[13, 14, 15],
286+
[16, 17, 18],
287+
],
288+
))
289+
.toEqual(
290+
[
291+
[11, 13, 15],
292+
[17, 19, 21],
293+
[23, 25, 27],
294+
],
295+
);
296+
297+
expect(mtrx.add(
298+
[
299+
[[1], [2], [3]],
300+
[[4], [5], [6]],
301+
[[7], [8], [9]],
302+
],
303+
[
304+
[[10], [11], [12]],
305+
[[13], [14], [15]],
306+
[[16], [17], [18]],
307+
],
308+
))
309+
.toEqual(
310+
[
311+
[[11], [13], [15]],
312+
[[17], [19], [21]],
313+
[[23], [25], [27]],
314+
],
315+
);
316+
});
317+
318+
it('should throw when trying to add matrices of different shape', () => {
319+
expect(() => mtrx.add([[0]], [[[0]]])).toThrowError(
320+
'Matrices have different dimensions',
321+
);
322+
323+
expect(() => mtrx.add([[0]], [[0, 0]])).toThrowError(
324+
'Matrices have different shapes',
325+
);
326+
});
327+
328+
it('should do element wise multiplication two matrices', () => {
329+
expect(mtrx.mul([[2]], [[3]])).toEqual([[6]]);
330+
331+
expect(mtrx.mul(
332+
[[1, 2, 3]],
333+
[[4, 5, 6]],
334+
))
335+
.toEqual(
336+
[[4, 10, 18]],
337+
);
338+
339+
expect(mtrx.mul(
340+
[[1], [2], [3]],
341+
[[4], [5], [6]],
342+
))
343+
.toEqual(
344+
[[4], [10], [18]],
345+
);
346+
347+
expect(mtrx.mul(
348+
[
349+
[1, 2],
350+
[3, 4],
351+
],
352+
[
353+
[5, 6],
354+
[7, 8],
355+
],
356+
))
357+
.toEqual(
358+
[
359+
[5, 12],
360+
[21, 32],
361+
],
362+
);
363+
364+
expect(mtrx.mul(
365+
[
366+
[[1], [2]],
367+
[[3], [4]],
368+
],
369+
[
370+
[[5], [6]],
371+
[[7], [8]],
372+
],
373+
))
374+
.toEqual(
375+
[
376+
[[5], [12]],
377+
[[21], [32]],
378+
],
379+
);
380+
});
381+
382+
it('should throw when trying to multiply matrices element-wise of different shape', () => {
383+
expect(() => mtrx.mul([[0]], [[[0]]])).toThrowError(
384+
'Matrices have different dimensions',
385+
);
386+
387+
expect(() => mtrx.mul([[0]], [[0, 0]])).toThrowError(
388+
'Matrices have different shapes',
389+
);
390+
});
391+
392+
it('should do element wise subtraction two matrices', () => {
393+
expect(mtrx.sub([[3]], [[2]])).toEqual([[1]]);
394+
395+
expect(mtrx.sub(
396+
[[10, 12, 14]],
397+
[[4, 5, 6]],
398+
))
399+
.toEqual(
400+
[[6, 7, 8]],
401+
);
402+
403+
expect(mtrx.sub(
404+
[[[10], [12], [14]]],
405+
[[[4], [5], [6]]],
406+
))
407+
.toEqual(
408+
[[[6], [7], [8]]],
409+
);
410+
411+
expect(mtrx.sub(
412+
[
413+
[10, 20],
414+
[30, 40],
415+
],
416+
[
417+
[5, 6],
418+
[7, 8],
419+
],
420+
))
421+
.toEqual(
422+
[
423+
[5, 14],
424+
[23, 32],
425+
],
426+
);
427+
428+
expect(mtrx.sub(
429+
[
430+
[[10], [20]],
431+
[[30], [40]],
432+
],
433+
[
434+
[[5], [6]],
435+
[[7], [8]],
436+
],
437+
))
438+
.toEqual(
439+
[
440+
[[5], [14]],
441+
[[23], [32]],
442+
],
443+
);
444+
});
445+
446+
it('should throw when trying to subtract matrices element-wise of different shape', () => {
447+
expect(() => mtrx.sub([[0]], [[[0]]])).toThrowError(
448+
'Matrices have different dimensions',
449+
);
450+
451+
expect(() => mtrx.sub([[0]], [[0, 0]])).toThrowError(
452+
'Matrices have different shapes',
453+
);
454+
});
455+
});

0 commit comments

Comments
 (0)
Please sign in to comment.