11import { fill , padStart , unzip } from "lodash-es" ;
22import { inv } from "mathjs" ;
33
4- type Matrix = {
5- __value : number | ( number | number [ ] ) [ ] ;
4+ interface IMatrix {
65 countRows : ( ) => number ;
76 countColumns : ( ) => number ;
87 addable : ( y : Matrix ) => boolean ;
@@ -12,30 +11,43 @@ type Matrix = {
1211 transpose : ( ) => Matrix ;
1312 invert : ( ) => Matrix ;
1413 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+ }
1731
1832/**
1933 * Creates a Matrix
2034 * @constructor
2135 * @alias module:matrix
22- * @param {number|(number | number[]) [] } x - Values to store in matrix
36+ * @param {number[] | number[][] } x - Values to store in matrix
2337 * @throws {TypeError } Argument x must be a number or number array
2438 * @return {Matrix } Single or multi dimensional matrix
2539 */
26- function Matrix ( x : number | ( number | number [ ] ) [ ] ) : Matrix {
40+ function Matrix ( x : number [ ] | number [ ] [ ] ) : Matrix {
2741 // extra nesting
28- if ( Array . isArray ( x ) && Array . isArray ( x [ 0 ] ) && x . length === 1 ) {
42+ if ( Array . isArray ( x [ 0 ] ) && x . length === 1 ) {
2943 throw new TypeError ( "Matrix must be a number or array of numbers" ) ;
3044 }
3145
3246 // uneven rows
47+ const firstRowLength = Array . isArray ( x [ 0 ] ) ? x [ 0 ] . length : 0 ;
3348 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 )
3951 ) {
4052 throw new TypeError ( "Matrix must be a number or array of numbers" ) ;
4153 }
@@ -70,10 +82,9 @@ Matrix.addable = function (x: Matrix, y: Matrix): boolean {
7082Matrix . add = function ( x : Matrix , y : Matrix ) : Matrix {
7183 if ( ! Matrix . addable ( x , y ) ) throw new TypeError ( "Matrices are not addable" ) ;
7284 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+ } )
7788 ) ;
7889} ;
7990
@@ -82,7 +93,7 @@ Matrix.add = function (x: Matrix, y: Matrix): Matrix {
8293 * @alias module:matrix.multipliable
8394 * @param {Matrix } x - Matrix to check
8495 * @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)
8697 */
8798Matrix . multipliable = function ( x : Matrix , y : Matrix ) : boolean {
8899 return x . countColumns ( ) === y . countRows ( ) ;
@@ -95,16 +106,18 @@ Matrix.multipliable = function (x: Matrix, y: Matrix): boolean {
95106 * @param {number } i - Column in matrix y to multiply
96107 * @return {number } Inner product of matrices
97108 */
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+ ) ;
108121}
109122
110123/**
@@ -119,23 +132,21 @@ Matrix.multiply = function (x: Matrix, y: Matrix): Matrix {
119132 throw new TypeError ( "Matrices are not multipliable" ) ;
120133 }
121134
122- if ( x . countColumns ( ) === 0 && y . countRows ( ) === 0 ) {
123- return Matrix ( ( x . __value as number ) * ( y . __value as number ) ) ;
124- }
125-
126135 /* 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 )
137139 ) ;
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+ }
139150} ;
140151
141152/**
@@ -154,7 +165,6 @@ Matrix.invert = function (x: Matrix): Matrix {
154165 * @return {number } Number of rows
155166 */
156167Matrix . prototype . countRows = function ( this : Matrix ) : number {
157- if ( typeof this . __value === "number" ) return 0 ;
158168 if ( typeof this . __value [ 0 ] === "number" ) return 1 ;
159169 return this . __value . length ;
160170} ;
@@ -165,7 +175,6 @@ Matrix.prototype.countRows = function (this: Matrix): number {
165175 * @return {number } Number of columns
166176 */
167177Matrix . prototype . countColumns = function ( this : Matrix ) : number {
168- if ( typeof this . __value === "number" ) return 0 ;
169178 if ( typeof this . __value [ 0 ] === "number" ) return this . __value . length ;
170179 return this . __value [ 0 ] . length ;
171180} ;
@@ -216,13 +225,10 @@ Matrix.prototype.multiply = function (this: Matrix, y: Matrix): Matrix {
216225 * @return {Matrix } New matrix with the transpose
217226 */
218227Matrix . 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 ) ) ;
226232 }
227233} ;
228234
@@ -240,19 +246,23 @@ Matrix.prototype.invert = function (this: Matrix): Matrix {
240246 * @alias module:matrix#map
241247 * @return {Matrix } Matrix inverse
242248 */
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+ }
246258} ;
247259
248260/**
249261 * Returns the number or number array value
250262 * @alias module:matrix#valueOf
251- * @return {number|number[] } Number of number array value
263+ * @return {number[] |number[] [] } Number of number array value
252264 */
253- Matrix . prototype . valueOf = function (
254- this : Matrix
255- ) : number | ( number | number [ ] ) [ ] {
265+ Matrix . prototype . valueOf = function ( this : Matrix ) : number [ ] | number [ ] [ ] {
256266 return this . __value ;
257267} ;
258268
@@ -264,26 +274,22 @@ Matrix.prototype.valueOf = function (
264274Matrix . prototype [ Symbol . for ( "nodejs.util.inspect.custom" ) ] = function (
265275 this : Matrix
266276) : 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[" ) ;
287293 }
288294} ;
289295
0 commit comments