import { BaseInfo32 , limbSize32 , limbInv32 , limbsPerDigit32 } from './BaseInfo32' ; import { BigFloatBase } from './BigFloatBase' ; import { trimNumber } from './util' ; export class BigFloat32 implements BigFloatBase < BigFloat32 > { constructor ( value ? , base ? ) { value ? this . setValue ( value , base ) : this . setZero ( ) ; } clone ( ) { return ( new BigFloat32 ( ) . setBig ( this ) ) ; } setZero ( ) { this . sign = 1 ; this . fractionLen = 0 ; this . len = 0 ; return ( this ) ; } setValue ( other , base ? ) { if ( typeof ( other ) == 'number' ) { return ( this . setNumber ( other ) ) ; } if ( other instanceof BigFloat32 ) { return ( this . setBig ( other ) ) ; } return ( this . setString ( other . toString ( ) , base || 10 ) ) ; } private setBig ( other ) { const len = other . len ; this . sign = other . sign ; this . fractionLen = other . fractionLen ; this . len = len ; for ( let pos = 0 ; pos < len ; ++ pos ) { this . limbList [ pos ] = other . limbList [ pos ] ; } return ( this ) ; } /** Set value from a floating point number (probably IEEE 754 double). */ private setNumber ( value ) { if ( value < 0 ) { value = - value ; this . sign = - 1 ; } else { this . sign = 1 ; } let iPart = Math . floor ( value ) ; let fPart = value - iPart ; let fractionLen = 0 ; const limbList = this . limbList ; let limb ; let len = 0 ; while ( fPart ) { fPart *= limbSize32 ; limb = fPart >>> 0 ; fPart -= limb ; limbList [ len ++ ] = limb ; ++ fractionLen ; } let pos = 0 ; while ( -- len > pos ) { limb = limbList [ pos ] ; limbList [ pos ++ ] = limbList [ len ] ; limbList [ len ] = limb ; } len += pos + 1 ; while ( iPart ) { limb = iPart % limbSize32 ; iPart = ( iPart - limb ) / limbSize32 ; limbList [ len ++ ] = limb ; } this . limbList = limbList ; this . fractionLen = fractionLen ; this . len = len ; return ( this ) ; } private parseFraction ( value , base , start , offset , limbBase , limbDigits ) { const limbList = this . limbList ; let pos = value . length ; let limbNum = offset - 1 ; while ( limbNum ) { limbList [ -- limbNum ] = 0 ; } let posNext = pos - ( ( pos - start + limbDigits - 1 ) % limbDigits + 1 ) ; limbList [ offset - 1 ] = parseInt ( value . substr ( posNext , pos - posNext ) , base ) ; this . divInt ( Math . pow ( base , pos - posNext ) , offset ) ; pos = posNext ; while ( pos > start ) { pos -= limbDigits ; limbList [ offset - 1 ] = parseInt ( value . substr ( pos , limbDigits ) , base ) ; this . divInt ( limbBase , offset ) ; } } private setString ( value , base ) { const { limbBase , limbDigits , limbDigitsExact } = BaseInfo32 . init ( base ) ; const limbList = this . limbList ; let pos = - 1 ; let c ; this . sign = 1 ; while ( 1 ) { c = value . charAt ( ++ pos ) ; switch ( c ) { case '-' : this . sign = - 1 ; case '+' : case '0' : continue ; } break ; } const posDot = ( value . indexOf ( '.' , pos ) + 1 || value . length + 1 ) - 1 ; if ( posDot < value . length - 1 ) { const len = ~ ~ ( ( value . length - posDot - 1 ) / limbDigitsExact ) + 1 ; this . parseFraction ( value , base , posDot + 1 , len + 1 , limbBase , limbDigits ) ; this . fractionLen = len ; this . len = len ; this . trimLeast ( ) ; } else { this . fractionLen = 0 ; this . len = 0 ; } const offset = this . fractionLen ; if ( posDot > pos ) { let posNext = pos + ( posDot - pos + limbDigits - 1 ) % limbDigits + 1 ; ++ this . len ; limbList [ offset ] = parseInt ( value . substr ( pos , posNext - pos ) , base ) ; pos = posNext ; while ( pos < posDot ) { if ( this . mulInt ( limbBase , limbList , offset , offset , 0 ) ) ++ this . len ; limbList [ offset ] += parseInt ( value . substr ( pos , limbDigits ) , base ) ; pos += limbDigits ; } } return ( this ) ; } /** Trim zero limbs from most significant end. */ private trimMost ( ) { const limbList = this . limbList ; const fractionLen = this . fractionLen ; let len = this . len ; while ( len > fractionLen && ! limbList [ len - 1 ] ) -- len ; this . len = len ; } /** Trim zero limbs from least significant end. */ private trimLeast ( ) { const limbList = this . limbList ; let len = this . fractionLen ; let pos = 0 ; while ( pos < len && ! limbList [ pos ] ) ++ pos ; if ( pos ) this . truncate ( len - pos ) ; } /** Multiply by an integer and write output limbs to another list. */ private mulInt ( factor , dstLimbList , srcPos , dstPos , overwriteMask ) { if ( ! factor ) return ( 0 ) ; const limbList = this . limbList ; const limbCount = this . len ; let limb ; let lo ; let carry = 0 ; while ( srcPos < limbCount ) { limb = limbList [ srcPos ++ ] ; carry += factor * ( limb & 0xffff ) ; lo = carry & 0xffff ; carry = ( carry - lo ) / 65536 ; carry += factor * ( limb >>> 16 ) ; lo |= carry << 16 ; limb = ( ( dstLimbList [ dstPos ] & overwriteMask ) + lo ) >>> 0 ; dstLimbList [ dstPos ++ ] = limb ; carry = ( carry / 65536 ) >>> 0 ; carry += ( lo ^ ( ( ( limb - lo ) ^ lo ) & ~ ( limb ^ lo ) ) ) >>> 31 ; } if ( carry ) dstLimbList [ dstPos ] = carry ; return ( carry ) ; } private mulBig ( multiplier , product ) { if ( this . isZero ( ) || multiplier . isZero ( ) ) return ( product . setZero ( ) ) ; const multiplierLimbs = multiplier . limbList ; const lenMultiplier = multiplier . len ; const productLimbs = product . limbList ; let posProduct = this . len + lenMultiplier ; product . len = posProduct ; while ( posProduct -- ) { productLimbs [ posProduct ] = 0 ; } this . mulInt ( multiplierLimbs [ 0 ] , productLimbs , 0 , 0 , 0 ) ; for ( let posMultiplier = 1 ; posMultiplier < lenMultiplier ; ++ posMultiplier ) { this . mulInt ( multiplierLimbs [ posMultiplier ] , productLimbs , 0 , posMultiplier , 0xffffffff ) ; } product . sign = this . sign * multiplier . sign as ( - 1 | 1 ) ; product . fractionLen = this . fractionLen + multiplier . fractionLen ; product . trimMost ( ) ; product . trimLeast ( ) ; return ( product ) ; } /** Multiply and return product in a new BigFloat32. */ mul ( multiplier , product ? ) { product = product || new BigFloat32 ( ) ; if ( typeof ( multiplier ) == 'number' ) { multiplier = temp32 . setNumber ( multiplier ) ; } if ( product == this ) throw ( new Error ( 'Multiplication in place is unsupported' ) ) ; return ( this . mulBig ( multiplier , product ) ) ; } absDeltaFrom ( other ) { if ( typeof ( other ) == 'number' ) { other = temp32 . setNumber ( other ) ; } const limbList = this . limbList ; const otherList = other . limbList ; let limbCount = this . len ; let otherCount = other . len ; let d = ( limbCount - this . fractionLen ) - ( otherCount - other . fractionLen ) ; while ( ! d && limbCount && otherCount ) d = limbList [ -- limbCount ] - otherList [ -- otherCount ] ; if ( d ) return ( d ) ; if ( limbCount ) { do d = limbList [ -- limbCount ] ; while ( ! d && limbCount ) ; } else if ( otherCount ) { do d = - otherList [ -- otherCount ] ; while ( ! d && otherCount ) ; } return ( d ) ; } cmp : ( other : number | BigFloat32 ) => number ; isZero ( ) { return ( this . len == 0 ) ; } getSign ( ) { return ( this . len && this . sign ) ; } /** Return an arbitrary number with sign matching the result of this - other. */ deltaFrom ( other ) { if ( typeof ( other ) == 'number' ) { other = temp32 . setNumber ( other ) ; } return ( this . len + other . len && ( this . sign - other . sign || this . absDeltaFrom ( other ) * this . sign ) ) ; } private addBig ( addend , sum ) { let augend = this ; let fractionLen = augend . fractionLen ; let len = fractionLen - addend . fractionLen ; if ( len < 0 ) { len = - len ; fractionLen += len ; augend = addend ; addend = this ; } sum . sign = this . sign ; sum . fractionLen = fractionLen ; let sumLimbs = sum . limbList ; let augendLimbs = augend . limbList ; let addendLimbs = addend . limbList ; let posAugend = 0 ; let posAddend = 0 ; let carry = 0 ; let limbSum ; while ( posAugend < len ) { sumLimbs [ posAugend ] = augendLimbs [ posAugend ] ; ++ posAugend ; } let lenAddend = addend . len ; len = augend . len - posAugend ; if ( len > lenAddend ) len = lenAddend ; while ( posAddend < len ) { carry += augendLimbs [ posAugend ] + addendLimbs [ posAddend ++ ] ; limbSum = carry >>> 0 ; carry = carry - limbSum && 1 ; sumLimbs [ posAugend ++ ] = limbSum ; } let posSum = posAugend ; if ( len < lenAddend ) { len = lenAddend ; augend = addend ; posAugend = posAddend ; augendLimbs = addendLimbs ; } else len = augend . len ; while ( posAugend < len ) { carry += augendLimbs [ posAugend ++ ] ; limbSum = carry >>> 0 ; carry = carry - limbSum && 1 ; sumLimbs [ posSum ++ ] = limbSum ; } if ( carry ) sumLimbs [ posSum ++ ] = carry ; sum . len = posSum ; sum . trimLeast ( ) ; return ( sum ) ; } private subBig ( subtrahend , difference ) { let minuend = this ; difference . sign = this . sign ; if ( minuend . absDeltaFrom ( subtrahend ) < 0 ) { minuend = subtrahend ; subtrahend = this ; difference . sign = - this . sign as ( - 1 | 1 ) ; } let fractionLen = minuend . fractionLen ; let len = fractionLen - subtrahend . fractionLen ; let differenceLimbs = difference . limbList ; let minuendLimbs = minuend . limbList ; let subtrahendLimbs = subtrahend . limbList ; let lenMinuend = minuend . len ; let lenSubtrahend = subtrahend . len ; let lenFinal = lenMinuend ; let posMinuend = 0 ; let posSubtrahend = 0 ; let posDifference = 0 ; let carry = 0 ; let limbDiff ; if ( len >= 0 ) { while ( posMinuend < len ) { differenceLimbs [ posMinuend ] = minuendLimbs [ posMinuend ] ; ++ posMinuend ; } len += lenSubtrahend ; if ( len > lenMinuend ) len = lenMinuend ; posDifference = posMinuend ; } else { len = - len ; fractionLen += len ; lenFinal += len ; while ( posSubtrahend < len ) { carry -= subtrahendLimbs [ posSubtrahend ] ; limbDiff = carry >>> 0 ; carry = - ( carry < 0 ) ; differenceLimbs [ posSubtrahend ++ ] = limbDiff ; } len += lenMinuend ; if ( len > lenSubtrahend ) len = lenSubtrahend ; posDifference = posSubtrahend ; } difference . fractionLen = fractionLen ; while ( posDifference < len ) { carry += minuendLimbs [ posMinuend ++ ] - subtrahendLimbs [ posSubtrahend ++ ] ; limbDiff = carry >>> 0 ; carry = - ( carry < 0 ) ; differenceLimbs [ posDifference ++ ] = limbDiff ; } while ( posDifference < lenFinal ) { carry += minuendLimbs [ posMinuend ++ ] ; limbDiff = carry >>> 0 ; carry = - ( carry < 0 ) ; differenceLimbs [ posDifference ++ ] = limbDiff ; } difference . len = posDifference ; difference . trimMost ( ) ; difference . trimLeast ( ) ; return ( difference ) ; } private addSub ( addend , sign , result ? ) { result = result || new BigFloat32 ( ) ; if ( result == this ) throw ( new Error ( 'Addition and subtraction in place is unsupported' ) ) ; if ( typeof ( addend ) == 'number' ) { addend = temp32 . setNumber ( addend ) ; } if ( this . sign * addend . sign * sign < 0 ) { return ( this . subBig ( addend , result ) ) ; } else { return ( this . addBig ( addend , result ) ) ; } } /** Add and return sum in a new BigFloat32. */ add ( addend , sum ? ) { return ( this . addSub ( addend , 1 , sum ) ) ; } /** Subtract and return difference in a new BigFloat32. */ sub ( subtrahend , difference ? ) { return ( this . addSub ( subtrahend , - 1 , difference ) ) ; } /** Round towards zero, to given number of base 2^32 fractional digits. */ truncate ( fractionLimbCount ) { const diff = this . fractionLen - fractionLimbCount ; if ( diff > 0 ) { this . fractionLen = fractionLimbCount ; this . len -= diff ; const len = this . len ; const limbList = this . limbList ; for ( let pos = 0 ; pos < len ; ++ pos ) { limbList [ pos ] = limbList [ pos + diff ] ; } } return ( this ) ; } round ( decimalCount ) { return ( this . truncate ( 1 + ~ ~ ( decimalCount * limbsPerDigit32 ) ) ) ; } /** Divide by integer, replacing current value by quotient. Return integer remainder. */ private divInt ( divisor , pos ) { let limbList = this . limbList ; let limb ; let hi , lo ; let carry = 0 ; if ( limbList [ pos - 1 ] < divisor ) { carry = limbList [ -- pos ] ; this . len = pos ; } while ( pos -- ) { limb = limbList [ pos ] ; carry = carry * 0x10000 + ( limb >>> 16 ) ; hi = ( carry / divisor ) >>> 0 ; carry = carry - hi * divisor ; carry = carry * 0x10000 + ( limb & 0xffff ) ; lo = ( carry / divisor ) >>> 0 ; carry = carry - lo * divisor ; limbList [ pos ] = ( ( hi << 16 ) | lo ) >>> 0 ; } return ( carry ) ; } private fractionToString ( base , digitList ) { const { pad , limbBase } = BaseInfo32 . init ( base ) ; let limbList = this . limbList ; let limbCount = this . fractionLen ; let limbNum = 0 ; let limbStr ; if ( base & 1 ) { throw ( new Error ( 'Conversion of floating point values to odd bases is unsupported' ) ) ; } while ( limbNum < limbCount && ! limbList [ limbNum ] ) ++ limbNum ; if ( limbNum >= limbCount ) return ; digitList . push ( '.' ) ; const fPart = temp32 ; fPart . limbList = limbList . slice ( limbNum , limbCount ) ; fPart . len = limbCount - limbNum ; limbNum = 0 ; while ( limbNum < fPart . len ) { if ( fPart . limbList [ limbNum ] ) { let carry = fPart . mulInt ( limbBase , fPart . limbList , limbNum , limbNum , 0 ) ; limbStr = carry . toString ( base ) ; digitList . push ( pad . substr ( limbStr . length ) + limbStr ) ; } else ++ limbNum ; } } getExpansion ( output ) { const limbList = this . limbList ; const len = this . len ; let exp = this . sign ; let pos = this . fractionLen ; while ( pos -- ) { exp *= limbInv32 ; } while ( ++ pos < len ) { output [ pos ] = limbList [ pos ] * exp ; exp *= limbSize32 ; } return ( len ) ; } valueOf ( ) { const limbList = this . limbList ; let result = 0 ; let exp = limbInv32 * this . sign ; let len = this . fractionLen ; let pos = 0 ; while ( pos < len ) { result = result * limbInv32 + limbList [ pos ++ ] ; } len = this . len ; while ( pos < len ) { result = result * limbInv32 + limbList [ pos ++ ] ; exp *= limbSize32 ; } return ( result * exp ) ; } return toString ( base = 10 ) { const { pad , limbBase } = BaseInfo32 . init ( base ) ; let digitList = [ ] ; let limbList = this . limbList ; let limb ; let limbStr ; if ( limbBase != limbSize32 ) { let iPart = temp32 ; iPart . limbList = limbList . slice ( this . fractionLen , this . len ) ; iPart . len = this . len - this . fractionLen ; while ( iPart . len > 1 ) { limbStr = iPart . divInt ( limbBase , iPart . len ) . toString ( base ) ; digitList . push ( pad . substr ( limbStr . length ) + limbStr ) ; } digitList . push ( '' + ( iPart . limbList [ 0 ] || 0 ) ) ; if ( this . sign < 0 ) digitList . push ( '-' ) ; digitList . reverse ( ) ; this . fractionToString ( base , digitList ) ; } else { let limbNum = this . len ; const fractionPos = this . fractionLen ; if ( this . sign < 0 ) digitList . push ( '-' ) ; if ( limbNum == fractionPos ) digitList . push ( '0' ) ; while ( limbNum -- ) { limbStr = limbList [ limbNum ] . toString ( base ) ; if ( limbNum == fractionPos - 1 ) digitList . push ( '.' ) ; digitList . push ( pad . substr ( limbStr . length ) + limbStr ) ; } } return ( trimNumber ( digitList . join ( '' ) ) ) ; } private sign : - 1 | 1 ; /** List of digits in base 2^32, least significant first. */ private limbList : number [ ] = [ ] ; /** Number of limbs belonging to fractional part. */ private fractionLen : number ; private len : number ; } BigFloat32 . prototype . cmp = BigFloat32 . prototype . deltaFrom ; const temp32 = new BigFloat32 ( ) ;