'use strict' ; import { Vector } from './vector' ; let _col , _diagonal , _get , _identity , _index , _jacobi , _multiply , _row , _set , _stringify , _transpose ; param data { Array < Number > } param m { Number } param n { Number } param col { Number } throws return { Array < Number > } _col = function ( data , m , n , col ) : Array < number > { let row , values ; if ( col < 0 || col >= n ) { throw new Error ( 'column ' + col + ' out of range [0,' + n + ')' ) ; } if ( n === 1 ) { return data ; } values = [ ] ; for ( row = 0 ; row < m ; row ++ ) { values . push ( data [ _index ( m , n , row , col ) ] ) ; } return values ; } ; param data { Array < Number > } param m { Number } param n { Number } return { Array < Number > } _diagonal = function ( data , m , n ) : Array < number > { let len , diag ; ( len = Math . min ( m , n ) ) , ( diag = [ ] ) ; for ( let i = 0 ; i < len ; i ++ ) { diag . push ( data [ _index ( m , n , i , i ) ] ) ; } return diag ; } ; param data { Array < Number > } param m { Number } param n { Number } param row { Number } param col { Number } throws return { Number } _get = function ( data , m , n , row , col ) : number { return data [ _index ( m , n , row , col ) ] ; } ; param n { Number } return _identity = function ( n ) : Array < number > { let values , row , col ; values = [ ] ; for ( row = 0 ; row < n ; row ++ ) { for ( col = 0 ; col < n ; col ++ ) { values . push ( row === col ? 1 : 0 ) ; } } return values ; } ; param data { Array < Number > } param m { Number } param n { Number } param row { Number } param col { Number } return { Number } _index = function ( m , n , row , col ) : number { return n * row + col ; } ; param data { Array < Number > } param m { Number } param n { Number } param maxRotations { Number } return { Array < any > } _jacobi = function ( data , m , n , maxRotations ) : Array < any > { let a , aip , aiq , api , app , app1 , apq , aqi , aqq , aqq1 , c , changed , e , i , ip , iq , p , phi , pi , q , qi , rotations , s , v , vector , vectors , vip , viq ; if ( m !== n ) { throw new Error ( 'Jacobi only works on symmetric, square matrices' ) ; } maxRotations = maxRotations ; a = data . slice ( 0 ) ; e = _diagonal ( data , m , n ) ; v = _identity ( n ) ; rotations = 0 ; do { changed = false ; for ( p = 0 ; p < n ; p ++ ) { for ( q = p + 1 ; q < n ; q ++ ) { app = e [ p ] ; aqq = e [ q ] ; apq = a [ n * p + q ] ; phi = 0.5 * Math . atan2 ( 2 * apq , aqq - app ) ; c = Math . cos ( phi ) ; s = Math . sin ( phi ) ; app1 = c * c * app - 2 * s * c * apq + s * s * aqq ; aqq1 = s * s * app + 2 * s * c * apq + c * c * aqq ; if ( app1 !== app || aqq1 !== aqq ) { changed = true ; rotations ++ ; e [ p ] = app1 ; e [ q ] = aqq1 ; a [ n * p + q ] = 0 ; for ( i = 0 ; i < p ; i ++ ) { ip = n * i + p ; iq = n * i + q ; aip = a [ ip ] ; aiq = a [ iq ] ; a [ ip ] = c * aip - s * aiq ; a [ iq ] = c * aiq + s * aip ; } for ( i = p + 1 ; i < q ; i ++ ) { pi = n * p + i ; iq = n * i + q ; api = a [ pi ] ; aiq = a [ iq ] ; a [ pi ] = c * api - s * aiq ; a [ iq ] = c * aiq + s * api ; } for ( i = q + 1 ; i < n ; i ++ ) { pi = n * p + i ; qi = n * q + i ; api = a [ pi ] ; aqi = a [ qi ] ; a [ pi ] = c * api - s * aqi ; a [ qi ] = c * aqi + s * api ; } for ( i = 0 ; i < n ; i ++ ) { ip = n * i + p ; iq = n * i + q ; vip = v [ ip ] ; viq = v [ iq ] ; v [ ip ] = c * vip - s * viq ; v [ iq ] = c * viq + s * vip ; } } } } } while ( changed && rotations < maxRotations ) ; if ( changed ) { throw new Error ( 'failed to converge' ) ; } vectors = [ ] ; for ( i = 0 ; i < n ; i ++ ) { vector = new Vector ( _col ( v , m , n , i ) ) ; vectors . push ( { eigenvalue : e [ i ] , vector : vector } ) ; } return vectors ; } ; param data1 { Array < Number > } param m1 { Number } param n1 { Number } param data2 { Array < Number > } param m2 { Number } param n2 { Number } throws return _multiply = function ( data1 , m1 , n1 , data2 , m2 , n2 ) : Array < number > { let col , col2 , row , row1 , values ; if ( n1 !== m2 ) { throw new Error ( 'wrong combination of rows and cols' ) ; } values = [ ] ; for ( row = 0 ; row < m1 ; row ++ ) { row1 = _row ( data1 , m1 , n1 , row ) ; for ( col = 0 ; col < n2 ; col ++ ) { col2 = _col ( data2 , m2 , n2 , col ) ; values . push ( Vector . dot ( row1 , col2 ) ) ; } } return values ; } ; param data { Array < Number > } param m { Number } param n { Number } param row { Number } throws return { Array < Number > } _row = function ( data , m , n , row ) : Array < number > { let col , values ; if ( row < 0 || row >= m ) { throw new Error ( 'row ' + row + ' out of range [0,' + m + ')' ) ; } values = [ ] ; for ( col = 0 ; col < n ; col ++ ) { values . push ( data [ _index ( m , n , row , col ) ] ) ; } return values ; } ; param data { Array < Number > } param m { Number } param n { Number } param row { Number } param col { Number } param value { Number } throws _set = function ( data , m , n , row , col , value ) { data [ _index ( m , n , row , col ) ] = value ; } ; param data { Array < Number > } param m { Number } param n { Number } return { String } _stringify = function ( data , m , n ) : string { let lastRow , lastCol , buf , row , col ; lastRow = m - 1 ; lastCol = n - 1 ; buf = [ ] ; buf . push ( '[' ) ; for ( row = 0 ; row < m ; row ++ ) { for ( col = 0 ; col < n ; col ++ ) { buf . push ( data [ n * row + col ] , col !== lastCol || row !== lastRow ? ', ' : '' ) ; } if ( row !== lastRow ) { buf . push ( '\n ' ) ; } } buf . push ( ']' ) ; return buf . join ( '' ) ; } ; param data { Array < Number > } param m { Number } param n { Number } return _transpose = function ( data , m , n ) : Array < number > { let values , row , col ; values = [ ] ; for ( col = 0 ; col < n ; col ++ ) { for ( row = 0 ; row < m ; row ++ ) { values . push ( data [ _index ( m , n , row , col ) ] ) ; } } return values ; } ; param data { Array } param m { Number } param n { Number } export class Matrix { static col = _col ; static diagonal = _diagonal ; static get = _get ; static identity = _identity ; static index = _index ; static jacobi = _jacobi ; static multiply = _multiply ; static row = _row ; static set = _set ; static stringify = _stringify ; static transpose = _transpose ; constructor ( public data , public m ? , public n ? ) { if ( this . m && this . n ) { return ; } if ( ! this . m && ! this . n ) { const side = Math . sqrt ( this . data . length ) ; if ( side !== Math . floor ( side ) ) { throw new Error ( 'matrix m,n unspecified, and matrix not square' ) ; } this . m = side ; this . n = side ; } else if ( ! this . m ) { this . m = this . data . length / this . n ; if ( this . m !== Math . floor ( this . m ) ) { throw new Error ( 'wrong number of data elements' ) ; } } else { this . n = this . data . length / this . m ; if ( this . n !== Math . floor ( this . n ) ) { throw new Error ( 'wrong number of data elements' ) ; } } } param that { Matrix } throws return add ( that ) { if ( this . m !== that . m || this . n !== that . n ) { throw new Error ( 'matrices must be same size' ) ; } return new Matrix ( Vector . add ( this . data , that . data ) , this . m , this . n ) ; } param col { Number } return { Array < Number > } col ( col ) { return _col ( this . data , this . m , this . n , col ) ; } return { Array < Number > } diagonal ( ) { return _diagonal ( this . data , this . m , this . n ) ; } param row { Number } param col { Number } return { Number } get ( row , col ) { return _get ( this . data , this . m , this . n , row , col ) ; } param maxRotations { Number } return { Array < any > } jacobi ( maxRotations = 100 ) { return _jacobi ( this . data , this . m , this . n , maxRotations ) ; } param that { Matrix } return { Matrix } multiply ( that ) { return new Matrix ( _multiply ( this . data , this . m , this . n , that . data , that . m , that . n ) , this . m , that . n ) ; } return { Matrix } negative ( ) { return new Matrix ( Vector . multiply ( this . data , - 1 ) , this . m , this . n ) ; } param row { Number } return { Array < Number > } row ( row ) { return _row ( this . data , this . m , this . n , row ) ; } param row { Number } param col { Number } param value { Number } set ( row , col , value ) { _set ( this . data , this . m , this . n , row , col , value ) ; } param that { Matrix } throws return subtract ( that ) { if ( this . m !== that . m || this . n !== that . n ) { throw new Error ( 'matrices must be same size' ) ; } return new Matrix ( Vector . subtract ( this . data , that . data ) , this . m , this . n ) ; } return { String } toString ( ) { return _stringify ( this . data , this . m , this . n ) ; } return { Matrix } transpose ( ) { return new Matrix ( _transpose ( this . data , this . m , this . n ) , this . n , this . m ) ; } }