import * as assert from 'assert' ; import * as Long from 'long' ; import { multiplyDivideFloor , multiplyDivideCeil , multiplyDivideRound } from './long' ; export default class Rational { static UZERO = new Rational ( Long . UZERO , Long . UONE , true ) ; private a : Long ; private b : Long ; public unsigned : boolean ; constructor ( numer , denom , unsigned ) { if ( ! unsigned ) { throw new Error ( 'signed rationals are not implemented' ) ; } assert . strictEqual ( numer . unsigned , unsigned , 'numerator is incorrectly signed' ) ; assert . strictEqual ( denom . unsigned , unsigned , 'denominator is incorrectly signed' ) ; assert ( ! denom . isZero ( ) , 'denominator must be non-zero' ) ; this . a = numer ; this . b = denom ; this . unsigned = unsigned ; } static isRational ( value ) { return value instanceof Rational ; } static fromNumbers ( numer , denom , unsigned ) { return new Rational ( Long . fromNumber ( numer , unsigned ) , Long . fromNumber ( denom , unsigned ) , unsigned ) ; } static fromNumber ( value , unsigned ) { if ( ! isFinite ( value ) ) { throw new Error ( 'value must be finite' ) ; } else if ( unsigned && value < 0 ) { throw new Error ( 'unsigned value must be positive' ) ; } if ( value % 1 === 0 ) { return Rational . fromNumbers ( value , 1 , unsigned ) ; } const mag = Math . floor ( Math . log ( value ) / Math . LN10 ) ; let shift = mag < 0 ? 18 : ( 18 - mag ) ; let den = 1 ; while ( Math . floor ( value * den ) !== value * den && shift > 0 ) { den *= 10 ; shift -- ; } return Rational . fromNumbers ( value * den , den , unsigned ) ; } /**
     * Multiply a rational by a Long without intermediate overflow.
     */ multiplyByLong ( value ) { return multiplyDivideFloor ( value , this . a , this . b ) ; } multiplyByLongCeil ( value ) { return multiplyDivideCeil ( value , this . a , this . b ) ; } multiplyByRational ( other ) { return new Rational ( this . a . multiply ( other . a ) , this . b . multiply ( other . b ) , this . unsigned ) ; } greaterThanOne ( ) { return this . a . greaterThan ( this . b ) ; } /**
     * Returns `1 - this`.
     */ complement ( ) { if ( this . a . greaterThan ( this . b ) ) { throw new Error ( 'cannot take complement of rational >1' ) ; } return new Rational ( this . b . subtract ( this . a ) , this . b , this . unsigned ) ; } /**
     * Returns `1 / this`.
     */ reciprocal ( ) { return new Rational ( this . b , this . a , this . unsigned ) ; } toNumber ( ) { return this . a . toNumber ( ) / this . b . toNumber ( ) ; } toString ( ) { const str = trimRight ( this . toFixed ( 19 ) , '0' ) ; return str [ str . length - 1 ] === '.' ? str . slice ( 0 , - 1 ) : str ; } private toFixed ( digits ? ) { digits = digits || 0 ; const quotient = this . a . divide ( this . b ) ; if ( digits === 0 ) { return quotient . toString ( ) ; } const remainder = this . a . modulo ( this . b ) ; const remainderString = multiplyDivideRound ( remainder , power10 ( digits ) , this . b ) . toString ( ) ; return quotient . toString ( ) + '.' + '0' . repeat ( digits - remainderString . length ) + remainderString ; } } function trimRight ( str , ch ) { for ( let i = str . length - 1 ; i >= 0 ; i -- ) { if ( str [ i ] !== ch ) { return str . slice ( 0 , i + 1 ) ; } } return '' ; } function power10 ( n ) { const ten = Long . fromNumber ( 10 , true ) ; let value = Long . UONE ; while ( n -- ) value = value . multiply ( ten ) ; return value ; }