1
1
import { fill , padStart , unzip } from "lodash-es" ;
2
2
import { inv } from "mathjs" ;
3
3
4
- type Matrix = {
5
- __value : number | ( number | number [ ] ) [ ] ;
4
+ interface IMatrix {
6
5
countRows : ( ) => number ;
7
6
countColumns : ( ) => number ;
8
7
addable : ( y : Matrix ) => boolean ;
@@ -12,30 +11,43 @@ type Matrix = {
12
11
transpose : ( ) => Matrix ;
13
12
invert : ( ) => Matrix ;
14
13
map : ( x : any ) => Matrix ;
15
- valueOf : ( ) => number | ( number | number [ ] ) [ ] ;
16
- } ;
14
+ }
15
+
16
+ interface Matrix1D extends IMatrix {
17
+ __value : number [ ] ;
18
+ valueOf : ( ) => number [ ] ;
19
+ }
20
+
21
+ interface Matrix2D extends IMatrix {
22
+ __value : number [ ] [ ] ;
23
+ valueOf : ( ) => number [ ] [ ] ;
24
+ }
25
+
26
+ type Matrix = Matrix1D | Matrix2D ;
27
+
28
+ function isMatrix1D ( matrix : Matrix ) : matrix is Matrix1D {
29
+ return matrix . countRows ( ) === 1 ;
30
+ }
17
31
18
32
/**
19
33
* Creates a Matrix
20
34
* @constructor
21
35
* @alias module:matrix
22
- * @param {number|(number | number[]) [] } x - Values to store in matrix
36
+ * @param {number[] | number[][] } x - Values to store in matrix
23
37
* @throws {TypeError } Argument x must be a number or number array
24
38
* @return {Matrix } Single or multi dimensional matrix
25
39
*/
26
- function Matrix ( x : number | ( number | number [ ] ) [ ] ) : Matrix {
40
+ function Matrix ( x : number [ ] | number [ ] [ ] ) : Matrix {
27
41
// extra nesting
28
- if ( Array . isArray ( x ) && Array . isArray ( x [ 0 ] ) && x . length === 1 ) {
42
+ if ( Array . isArray ( x [ 0 ] ) && x . length === 1 ) {
29
43
throw new TypeError ( "Matrix must be a number or array of numbers" ) ;
30
44
}
31
45
32
46
// uneven rows
47
+ const firstRowLength = Array . isArray ( x [ 0 ] ) ? x [ 0 ] . length : 0 ;
33
48
if (
34
- Array . isArray ( x ) &&
35
- Array . isArray ( x [ 0 ] ) &&
36
- x . some (
37
- ( row ) => Array . isArray ( row ) && row . length !== ( x [ 0 ] as number [ ] ) . length
38
- )
49
+ firstRowLength > 0 &&
50
+ x . some ( ( row ) => Array . isArray ( row ) && row . length !== firstRowLength )
39
51
) {
40
52
throw new TypeError ( "Matrix must be a number or array of numbers" ) ;
41
53
}
@@ -70,10 +82,9 @@ Matrix.addable = function (x: Matrix, y: Matrix): boolean {
70
82
Matrix . add = function ( x : Matrix , y : Matrix ) : Matrix {
71
83
if ( ! Matrix . addable ( x , y ) ) throw new TypeError ( "Matrices are not addable" ) ;
72
84
return x . map ( ( row : number [ ] , i : number ) : number [ ] =>
73
- row . map (
74
- ( column : number , j : number ) : number =>
75
- column + ( y . __value as number [ ] [ ] ) [ i ] [ j ]
76
- )
85
+ row . map ( ( column : number , j : number ) : number => {
86
+ return column + ( Array . isArray ( y . __value [ i ] ) ? y . __value [ i ] [ j ] : 0 ) ;
87
+ } )
77
88
) ;
78
89
} ;
79
90
@@ -82,7 +93,7 @@ Matrix.add = function (x: Matrix, y: Matrix): Matrix {
82
93
* @alias module:matrix.multipliable
83
94
* @param {Matrix } x - Matrix to check
84
95
* @param {Matrix } y - Matrix to check
85
- * @return {boolean } Whether two matrices can be summed (using matrix multiplication)
96
+ * @return {boolean } Whether two matrices can be multiplied (using matrix multiplication)
86
97
*/
87
98
Matrix . multipliable = function ( x : Matrix , y : Matrix ) : boolean {
88
99
return x . countColumns ( ) === y . countRows ( ) ;
@@ -95,16 +106,18 @@ Matrix.multipliable = function (x: Matrix, y: Matrix): boolean {
95
106
* @param {number } i - Column in matrix y to multiply
96
107
* @return {number } Inner product of matrices
97
108
*/
98
- function innerproduct ( x : Matrix , y : Matrix , i : number ) : number {
99
- const _x : number [ ] = x . __value as number [ ] ;
100
- const _y : number [ ] =
101
- Array . isArray ( unzip < number > ( y . __value as number [ ] [ ] ) ) &&
102
- unzip < number > ( y . __value as number [ ] [ ] ) . length === 0
103
- ? unzip ( [ y . __value as number [ ] ] ) [ i ]
104
- : unzip ( y . __value as number [ ] [ ] ) [ i ] ;
105
- return ( [ ] as number [ ] )
106
- . concat ( _x )
107
- . reduce ( ( z : number , _z : number , j : number ) : number => z + _z * _y [ j ] , 0 ) ;
109
+ function innerproduct ( x : Matrix1D , y : Matrix , i : number ) : number {
110
+ const _x = x . __value ;
111
+ let _y ;
112
+ if ( isMatrix1D ( y ) ) {
113
+ _y = unzip ( [ y . __value ] ) [ i ] ;
114
+ } else {
115
+ _y = unzip ( y . __value ) [ i ] ;
116
+ }
117
+ return [ ..._x ] . reduce (
118
+ ( z : number , _z : number , j : number ) : number => z + _z * _y [ j ] ,
119
+ 0
120
+ ) ;
108
121
}
109
122
110
123
/**
@@ -119,23 +132,21 @@ Matrix.multiply = function (x: Matrix, y: Matrix): Matrix {
119
132
throw new TypeError ( "Matrices are not multipliable" ) ;
120
133
}
121
134
122
- if ( x . countColumns ( ) === 0 && y . countRows ( ) === 0 ) {
123
- return Matrix ( ( x . __value as number ) * ( y . __value as number ) ) ;
124
- }
125
-
126
135
/* New matrix with the dot product */
127
- const z : Matrix = Matrix (
128
- fill (
129
- Array ( x . countRows ( ) ) ,
130
- x . countRows ( ) !== 1 ? fill ( Array ( y . countColumns ( ) ) , 0 ) : 0
131
- )
132
- ) ;
133
- return z . map ( ( _z : number | number [ ] , i : number ) : number | number [ ] => {
134
- if ( typeof _z === "number" ) return innerproduct ( x , y , i ) ;
135
- return _z . map ( ( _ , j ) =>
136
- innerproduct ( Matrix ( ( x . __value as number [ ] ) [ i ] ) , y , j )
136
+ if ( isMatrix1D ( x ) ) {
137
+ return Matrix ( [ 0 ] ) . map ( ( _z : number , i : number ) : number =>
138
+ innerproduct ( x , y , i )
137
139
) ;
138
- } ) ;
140
+ } else {
141
+ return Matrix (
142
+ fill ( Array ( x . countRows ( ) ) , fill ( Array ( y . countColumns ( ) ) , 0 ) )
143
+ ) . map ( ( _z : number [ ] , i : number ) => {
144
+ const _x = Matrix ( x . __value [ i ] ) ;
145
+ if ( isMatrix1D ( _x ) ) {
146
+ return _z . map ( ( _ , j ) : number => innerproduct ( _x , y , j ) ) ;
147
+ }
148
+ } ) ;
149
+ }
139
150
} ;
140
151
141
152
/**
@@ -154,7 +165,6 @@ Matrix.invert = function (x: Matrix): Matrix {
154
165
* @return {number } Number of rows
155
166
*/
156
167
Matrix . prototype . countRows = function ( this : Matrix ) : number {
157
- if ( typeof this . __value === "number" ) return 0 ;
158
168
if ( typeof this . __value [ 0 ] === "number" ) return 1 ;
159
169
return this . __value . length ;
160
170
} ;
@@ -165,7 +175,6 @@ Matrix.prototype.countRows = function (this: Matrix): number {
165
175
* @return {number } Number of columns
166
176
*/
167
177
Matrix . prototype . countColumns = function ( this : Matrix ) : number {
168
- if ( typeof this . __value === "number" ) return 0 ;
169
178
if ( typeof this . __value [ 0 ] === "number" ) return this . __value . length ;
170
179
return this . __value [ 0 ] . length ;
171
180
} ;
@@ -216,13 +225,10 @@ Matrix.prototype.multiply = function (this: Matrix, y: Matrix): Matrix {
216
225
* @return {Matrix } New matrix with the transpose
217
226
*/
218
227
Matrix . prototype . transpose = function ( this : Matrix ) : Matrix {
219
- switch ( this . countRows ( ) ) {
220
- case 0 :
221
- return Matrix ( this . __value as number ) ;
222
- case 1 :
223
- return Matrix ( unzip ( [ this . __value as number [ ] ] ) ) ;
224
- default :
225
- return Matrix ( unzip ( this . __value as number [ ] [ ] ) ) ;
228
+ if ( isMatrix1D ( this ) ) {
229
+ return Matrix ( unzip ( [ this . __value ] ) ) ;
230
+ } else {
231
+ return Matrix ( unzip ( this . __value ) ) ;
226
232
}
227
233
} ;
228
234
@@ -240,19 +246,23 @@ Matrix.prototype.invert = function (this: Matrix): Matrix {
240
246
* @alias module:matrix#map
241
247
* @return {Matrix } Matrix inverse
242
248
*/
243
- Matrix . prototype . map = function ( this : Matrix , x : any ) : Matrix {
244
- if ( typeof this . __value === "number" ) return Matrix ( x ( this . __value ) ) ;
245
- return Matrix ( this . __value . map ( x ) ) ;
249
+ Matrix . prototype . map = function (
250
+ this : Matrix ,
251
+ x : < T extends number | number [ ] > ( value : T , index : number , array : T [ ] ) => T
252
+ ) : Matrix {
253
+ if ( isMatrix1D ( this ) ) {
254
+ return Matrix ( this . __value . map ( x < number > ) ) ;
255
+ } else {
256
+ return Matrix ( this . __value . map ( x < number [ ] > ) ) ;
257
+ }
246
258
} ;
247
259
248
260
/**
249
261
* Returns the number or number array value
250
262
* @alias module:matrix#valueOf
251
- * @return {number|number[] } Number of number array value
263
+ * @return {number[] |number[] [] } Number of number array value
252
264
*/
253
- Matrix . prototype . valueOf = function (
254
- this : Matrix
255
- ) : number | ( number | number [ ] ) [ ] {
265
+ Matrix . prototype . valueOf = function ( this : Matrix ) : number [ ] | number [ ] [ ] {
256
266
return this . __value ;
257
267
} ;
258
268
@@ -264,26 +274,22 @@ Matrix.prototype.valueOf = function (
264
274
Matrix . prototype [ Symbol . for ( "nodejs.util.inspect.custom" ) ] = function (
265
275
this : Matrix
266
276
) : string {
267
- switch ( this . countRows ( ) ) {
268
- case 0 :
269
- return `${ this . __value } ` ;
270
- case 1 :
271
- return `[ ${ ( this . __value as number [ ] ) . join ( " " ) } ]` ;
272
- default :
273
- /* Output array filled with zeroes */
274
- const padding : number [ ] = unzip ( this . __value as number [ ] [ ] ) . map (
275
- ( column : number [ ] ) =>
276
- column . reduce ( ( length , x ) => Math . max ( `${ x } ` . length , length ) , 0 )
277
- ) ;
278
- return ( this . __value as number [ ] [ ] )
279
- . reduce (
280
- ( output , row ) =>
281
- `${ output } [ ${ row
282
- . map ( ( x , i ) => padStart ( `${ x } ` , padding [ i ] ) )
283
- . join ( " " ) } ]`,
284
- ""
285
- )
286
- . replace ( / ] \[ / g, "]\n[" ) ;
277
+ if ( isMatrix1D ( this ) ) {
278
+ return `[ ${ this . __value . join ( " " ) } ]` ;
279
+ } else {
280
+ /* Output array filled with zeroes */
281
+ const padding : number [ ] = unzip ( this . __value ) . map ( ( column : number [ ] ) =>
282
+ column . reduce ( ( length , x ) => Math . max ( `${ x } ` . length , length ) , 0 )
283
+ ) ;
284
+ return this . __value
285
+ . reduce (
286
+ ( output , row ) =>
287
+ `${ output } [ ${ row
288
+ . map ( ( x , i ) => padStart ( `${ x } ` , padding [ i ] ) )
289
+ . join ( " " ) } ]`,
290
+ ""
291
+ )
292
+ . replace ( / ] \[ / g, "]\n[" ) ;
287
293
}
288
294
} ;
289
295
0 commit comments