-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix.js
135 lines (123 loc) · 3.19 KB
/
matrix.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const MATRIX = Symbol("matrix")
const isArr = Array.isArray
export const shape = m => {
const lens = [];
let temp = m;
while (temp && isArr(temp)) {
lens.push(temp.length);
temp = (temp.length && [...temp][0]) || null;
}
return lens;
}
const validateNumber = n => {
if (typeof n !== "number" || n % 1 !== 0 || n < 0)
throw new Error("Invalid type. Required a integer. Over zero")
}
const validateType = m => {
if (!m || !isArr(m) || !isArr(m[0]))
throw new Error("Invalid matrix format")
}
const validate2D = m => {
validateType(m)
if (shape(m) !== 2)
throw new Error("Matrix is not of 2D shape")
}
/**
* Validates that matrices are of the same shape.
*
* @param {Matrix} a
* @param {Matrix} b
* @throws {Error}
*/
export const validateSameShape = (a, b) => {
validateType(a);
validateType(b);
const aShape = shape(a);
const bShape = shape(b);
if (aShape.length !== bShape.length)
throw new Error('Matrices have different dimensions')
while (aShape.length && bShape.length)
if (aShape.pop() !== bShape.pop())
throw new Error('Matrices have different shapes')
};
const generate = (mShape, fill) => {
const genRec = (rShape, recI) => {
if (rShape.length === 1) {
return new Array(rShape[0])
.fill(null)
.map((cellValue, cellIndex) => fill([...recI, cellIndex]));
}
const m = [];
for (let i = 0; i < rShape[0]; i++)
m.push(genRec(rShape.slice(1), [...recI, i]));
return m;
};
return genRec(mShape, []);
};
const validateLen = (a, b) => {
if (a.length !== b.length)
throw new Error("Invalid shape")
let i = 0
const len = a.length
while (i < len) {
const val = a[i]
if (val > b[i] || val % 1 !== 0 || val < 0)
throw new Error("Invalid index of shape")
i++
}
}
export class Matrix {
#shape
constructor(x, y, z, fill) {
const args = [...arguments]
fill = () => null
if (typeof args[args.length - 1] === "function")
fill = args.pop()
if (args.length === 0) args[0] = 1
args.forEach(validateNumber)
this[MATRIX] = generate(this.#shape = args, fill)
}
getShape() { return [...this.#shape] }
get lenght() {
let len = 0
for (const n of this.#shape) len += n
return len
}
get matrix() { return this[MATRIX] }
get(x, y, z) {
const args = [...arguments]
validateLen(args, this.#shape)
const len = args.length
let i = 0
let m = this[MATRIX]
while (i < len) m = m[args[i++]]
return m
}
set(value, x, y, z) {
const args = [...arguments].slice(1)
validateLen(args, this.#shape)
const len = args.length - 1
let i = 0
let m = this[MATRIX]
while (i < len)
m = m[args[i++]]
m[args.pop()] = value
}
each(fn) {
const m = this[MATRIX]
const slen = this.#shape.length
const recWalk = (recM, cellIndices, deep) => {
if (deep === slen) {
for (let i = 0; i < recM.length; i += 1)
fn(recM[i], [...cellIndices, i])
}
for (let i = 0; i < recM.length; i += 1)
recWalk(recM[i], [...cellIndices, i], deep + 1)
}
recWalk(m, [], 1);
}
static generate(...mShape) {
if (mShape.length === 0) mShape[0] = 1
return new Matrix(...mShape)
}
}