import { Variable } from './Variable' ; import { Variables } from './Variables' ; import * as Constants from '../Constants' ; import { Interval } from '../Utils/Interval' ; import { CommandInterface } from '../Commands' ; export class Matrix extends Variable { private static MATRIX_TYPENAME : string = 'matrix' ; private _typename : string ; private _basename : string ; private _fixedIndices : Array < number > ; private _freeIndices : Array < number > ; private _children : Array < Variable > ; private _availableChildrenRange : Array < boolean > ; private _validValue : boolean ; private _parsedValue : boolean ; private _extendedTypename : string ; private _typeRegex : RegExp ; param name param value param freeIndices param fixedIndices param validValue constructor ( name = '' , value = '' , freeIndices = [ ] , fixedIndices = [ ] , validValue = true , type = Matrix . MATRIX_TYPENAME ) { super ( ) ; this . _typeRegex = new RegExp ( type , 'i' ) ; this . _basename = name ; this . _typename = type ; this . setIndices ( freeIndices , fixedIndices ) ; this . setValue ( value , validValue ) ; } public static cleanComplex ( value ) { return value . replace ( /(?:\s+([\+\-])\s+)/g , "$1" ) ; } public setIndices ( free , fixed ) { this . _freeIndices = free ; this . _fixedIndices = fixed ; this . _name = this . makeName ( this . _basename , this . _freeIndices , this . _fixedIndices ) ; if ( this . _freeIndices . length !== 0 ) { this . _numberOfChildren = this . _freeIndices [ this . _freeIndices . length - 1 ] ; if ( this . _numberOfChildren !== 0 ) { Variables . addReferenceTo ( this ) ; } const size = this . _freeIndices . join ( Constants . SIZE_SEPARATOR ) ; this . _extendedTypename = `${ this . typename ( ) } ${ size }` ; } } public setValue ( val , isValid ) { this . _validValue = isValid ; if ( isValid ) { val = val . replace ( this . _typeRegex , '' ) . replace ( /\n\s+/g , '\n' ) . trim ( ) ; if ( this . isComplex ( ) ) { val = Matrix . cleanComplex ( val ) ; } } this . _value = val ; } public basename ( ) { return this . _basename ; } public typename ( ) { return this . _typename ; } public extendedTypename ( ) { return this . _extendedTypename ; } public fixedIndices ( ) { return this . _fixedIndices ; } public freeIndices ( ) { return this . _freeIndices ; } public loads ( type ) { return type . endsWith ( this . typename ( ) ) ; } public isComplex ( ) { return this . typename ( ) . includes ( "complex" ) ; } public createConcreteType ( name , value , freeIndices , fixedIndices , validValue , type ) { return new Matrix ( name , value , freeIndices , fixedIndices , validValue , type ) ; } public createChildType ( value , freeIndices , fixedIndices , validValue ) { return new Matrix ( this . basename ( ) , value , freeIndices , fixedIndices , validValue , this . typename ( ) ) ; } public loadNew ( name , type , runtime , callback ) { Variables . getSize ( name , runtime , ( size ) => { const loadable = Variables . loadable ( size ) ; const buildWith = ( value ) => { const matrix = this . createConcreteType ( name , value , size , [ ] , loadable , type ) ; callback ( matrix ) ; } ; if ( loadable ) { Variables . getValue ( name , runtime , buildWith ) ; } else { buildWith ( size . join ( Constants . SIZE_SEPARATOR ) ) ; } } ) ; } public listChildren ( runtime , count , start , callback ) { if ( this . _numberOfChildren === 0 ) { throw "Error: matrix has no children!" ; } if ( count === 0 && start === 0 ) { count = this . _numberOfChildren ; } const interval = new Interval ( start , start + count ) ; const self = this ; this . makeChildrenAvailable ( runtime , interval , ( ) => { if ( self . _numberOfChildren !== self . _children . length ) { throw `Error: listChildren ${ self . _numberOfChildren } !== ${ self . _children . length }!` ; } if ( count === self . _children . length ) { callback ( self . _children ) ; } else { callback ( self . _children . slice ( start , start + count ) ) ; } } ) ; } public makeChildrenAvailable ( runtime , interval , callback ) { const self = this ; if ( this . _validValue ) { if ( ! this . _parsedValue ) { this . parseAllChildren ( ( children ) => { self . _children = children ; callback ( ) ; } ) ; } else { callback ( ) ; } } else { const rangesToFetch = this . unavailableOverlappingRanges ( interval ) ; if ( rangesToFetch . length !== 0 ) { this . fetchRanges ( rangesToFetch , runtime , callback ) ; } else { callback ( ) ; } } } public fetchRanges ( ranges , runtime , callback ) { let fetchedRanges = 0 ; const self = this ; ranges . forEach ( interval => { this . fetchChildrenRange ( interval , runtime , ( children ) => { self . addChildrenFrom ( interval , children ) ; ++ fetchedRanges ; if ( fetchedRanges === ranges . length ) { callback ( ) ; } } ) ; } ) ; } public addChildrenFrom ( interval , children ) { if ( this . _children === undefined ) { this . _children = new Array < Variable > ( this . _numberOfChildren ) ; } if ( this . _children . length < interval . min ( ) + children . length ) { throw `Matrix::addChildrenFrom dst ${ this . _children . length } < src ${ interval . min ( ) } + ${ children . length }` ; } for ( let i = 0 ; i !== children . length ; ++ i ) { this . _children [ i + interval . min ( ) ] = children [ i ] ; } const a = Math . trunc ( interval . min ( ) / Constants . CHUNKS_SIZE ) ; const b = Math . ceil ( interval . max ( ) / Constants . CHUNKS_SIZE ) ; for ( let i = a ; i !== b ; ++ i ) { this . _availableChildrenRange [ i ] = true ; } } public unavailableOverlappingRanges ( interval ) { if ( this . _availableChildrenRange === undefined ) { const rangeCount = Math . ceil ( this . _numberOfChildren / Constants . CHUNKS_SIZE ) ; this . _availableChildrenRange = new Array < boolean > ( rangeCount ) ; } const a = Math . trunc ( interval . min ( ) / Constants . CHUNKS_SIZE ) ; const b = Math . ceil ( interval . max ( ) / Constants . CHUNKS_SIZE ) ; let unavailable = new Array < Interval > ( ) ; for ( let i = a ; i !== b ; ++ i ) { if ( ! this . _availableChildrenRange [ i ] ) { const min = i * Constants . CHUNKS_SIZE ; const max = Math . min ( min + Constants . CHUNKS_SIZE , this . _numberOfChildren ) ; const interval = new Interval ( min , max ) ; if ( unavailable . length !== 0 ) { const last = unavailable [ unavailable . length - 1 ] ; if ( last . contigous ( interval ) ) { last . expandWith ( interval ) ; } else { unavailable . push ( interval ) ; } } else { unavailable . push ( interval ) ; } } } return unavailable ; } public fetchChildrenRange ( interval , runtime , callback ) { const offset = interval . min ( ) ; const count = interval . size ( ) ; this . fetchChildren ( runtime , offset , count , callback ) ; } public parseChildren ( value , offset , count , callback ) { const N = this . _freeIndices . length ; switch ( N ) { case 1 : this . parseChildrenOf1DMatrix ( value , offset , count , callback ) ; break ; case 2 : this . parseChildrenOf2DMatrix ( value , offset , count , callback ) ; break ; default : throw "Parsing of n > 2 dimensional matrices is not supported!" ; } } public parseAllChildren ( callback ) { const value = this . _value ; if ( this . _freeIndices . length > 2 ) { throw `freeIndices.length: ${ this . _freeIndices . length }, expected <= 2!` ; } const count = this . _freeIndices [ this . _freeIndices . length - 1 ] ; const self = this ; this . parseChildren ( value , 0 , count , ( vars ) => { self . _parsedValue = true ; callback ( vars ) ; } ) ; } public parseChildrenOf1DMatrix ( value , offset , count , callback ) { const childFreeIndices = [ ] ; const vars = new Array < Variable > ( count ) ; const vectors = Matrix . extractArrayElements ( value , this . isComplex ( ) ) ; if ( vectors . length !== 1 ) { throw `vectors.length: ${ vectors . length } != 1!` ; } const vector = vectors [ 0 ] ; if ( vector . length !== count ) { throw `vector.length: ${ vector . length } != ${ count }!` ; } for ( let i = 0 ; i !== count ; ++ i ) { const childFixedIndices = [ i + offset + 1 ] . concat ( this . _fixedIndices ) ; vars [ i ] = this . createChildType ( '' + vector [ i ] , childFreeIndices , childFixedIndices , true ) ; } callback ( vars ) ; } public parseAllChildrenOf1DMatrix ( value , callback ) { if ( this . _freeIndices . length !== 1 ) { throw `freeIndices.length: ${ this . _freeIndices . length }, expected 1!` ; } this . parseChildrenOf1DMatrix ( value , 0 , this . _freeIndices [ 0 ] , callback ) ; } public parseChildrenOf2DMatrix ( value , offset , count , callback ) { const childFreeIndices = this . _freeIndices . slice ( 0 , this . _freeIndices . length - 1 ) ; const vars = new Array < Variable > ( count ) ; const vectors = Matrix . extractArrayElements ( value , this . isComplex ( ) ) ; if ( vectors . length !== count ) { throw `vectors.length: ${ vectors . length } != ${ count }!` ; } for ( let i = 0 ; i !== count ; ++ i ) { const childFixedIndices = [ i + offset + 1 ] . concat ( this . _fixedIndices ) ; const childValue = vectors [ i ] . join ( Constants . COLUMN_ELEMENTS_SEPARATOR ) ; vars [ i ] = this . createChildType ( childValue , childFreeIndices , childFixedIndices , true ) ; } callback ( vars ) ; } public parseAllChildrenOf2DMatrix ( value , callback ) { if ( this . _freeIndices . length !== 2 ) { throw `freeIndices.length: ${ this . _freeIndices . length }, expected 2!` ; } const N = this . _freeIndices [ this . _freeIndices . length - 1 ] ; this . parseChildrenOf2DMatrix ( value , 0 , N , callback ) ; } public fetchChildren ( runtime , offset , count , callback ) { if ( this . _freeIndices . length === 0 ) { throw `fetchChildren::freeIndices.length: ${ this . _freeIndices . length } === 0` ; } const childrenFreeIndices = this . _freeIndices . slice ( 0 , this . _freeIndices . length - 1 ) ; const loadable = Variables . loadable ( childrenFreeIndices , count ) ; if ( loadable ) { this . loadChildrenRange ( runtime , offset , count , callback ) ; } else { const value = childrenFreeIndices . join ( Constants . SIZE_SEPARATOR ) ; const vars = new Array < Variable > ( count ) ; for ( let i = 0 ; i !== count ; ++ i ) { const childrenFixedIndices = [ offset + i + 1 ] . concat ( this . _fixedIndices ) ; vars [ i ] = this . createChildType ( value , childrenFreeIndices , childrenFixedIndices , false ) ; } callback ( vars ) ; } } public loadChildrenRange ( runtime , offset , count , callback ) { const rangeName = this . makeRangeName ( offset , count ) ; Variables . getValue ( rangeName , runtime , ( value ) => { value = value . replace ( this . _typeRegex , '' ) . trim ( ) ; this . parseChildren ( value , offset , count , callback ) ; } ) ; } public fetchAllChildren ( runtime , callback ) { const Nchildren = this . _freeIndices [ this . _freeIndices . length - 1 ] ; this . fetchChildren ( runtime , 0 , Nchildren , callback ) ; } public makeName ( name , freeIndices , fixedIndices , left = Constants . DEFAULT_LEFT , right = Constants . DEFAULT_RIGHT ) { let freeIndicesStr = '' , fixedIndicesStr = '' ; if ( fixedIndices . length !== 0 ) { fixedIndicesStr = fixedIndices . join ( ',' ) ; } else { return name ; } if ( freeIndices . length !== 0 ) { freeIndicesStr = ':,' . repeat ( freeIndices . length ) ; } return `${ name }${ left }${ freeIndicesStr }${ fixedIndicesStr }${ right }` ; } public makeRangeName ( offset , count ) { const name = this . _basename ; const freeIndices = this . _freeIndices ; const fixedIndices = this . _fixedIndices ; let freeIndicesStr = '' , fixedIndicesStr = '' ; if ( fixedIndices . length !== 0 ) { fixedIndicesStr = ',' + fixedIndices . join ( ',' ) ; } if ( freeIndices . length !== 0 ) { freeIndicesStr = ':,' . repeat ( freeIndices . length - 1 ) ; const a = offset + 1 ; const b = offset + count ; freeIndicesStr += `${ a }:${ b }` ; } return `${ name }(${ freeIndicesStr }${ fixedIndicesStr })` ; } public static split ( value , isComplex ) { value = value . trim ( ) ; if ( isComplex ) { value = Matrix . cleanComplex ( value ) ; } const elements = value . split ( ' ' ) . filter ( line => line ) ; return elements ; } public static transpose ( matrix ) { const N_rows = matrix . length ; let rowValues = matrix [ 0 ] ; const N_cols = rowValues . length ; const transposed = new Array < Array < string > > ( N_cols ) ; for ( let col = 0 ; col !== N_cols ; ++ col ) { transposed [ col ] = new Array < string > ( N_rows ) ; transposed [ col ] [ 0 ] = rowValues [ col ] ; } for ( let row = 1 ; row !== N_rows ; ++ row ) { rowValues = matrix [ row ] ; if ( rowValues . length !== N_cols ) { throw `rowValues.length !== Ncols: ${ rowValues . length } !== ${ N_cols }` ; } for ( let col = 0 ; col !== N_cols ; ++ col ) { transposed [ col ] [ row ] = rowValues [ col ] ; } } return transposed ; } private static readonly GROUPS_REGEX = /Columns? \d+(?: ((?:through)|(?:and)?) \d+)?:/ ; public static extractArrayElements ( value , isComplex ) { const mutipleColumnsGroup = Matrix . GROUPS_REGEX ; const inLines = value . trim ( ) . split ( '\n' ) . filter ( line => line ) ; const N_lines = inLines . length ; let elements = new Array < Array < string > > ( ) ; if ( inLines [ 0 ] . match ( mutipleColumnsGroup ) ) { let line = 1 ; for ( ; line !== N_lines && ! inLines [ line ] . match ( mutipleColumnsGroup ) ; ++ line ) { elements . push ( Matrix . split ( inLines [ line ] , isComplex ) ) ; } if ( line !== N_lines ) { ++ line ; } for ( let row = 0 ; line !== N_lines ; ++ line ) { if ( inLines [ line ] . match ( mutipleColumnsGroup ) ) { row = 0 ; } else { Array . prototype . push . apply ( elements [ row ++ ] , Matrix . split ( inLines [ line ] , isComplex ) ) ; } } } else { for ( let line = 0 ; line !== N_lines ; ++ line ) { elements . push ( Matrix . split ( inLines [ line ] , isComplex ) ) ; } } return Matrix . transpose ( elements ) ; } }