Skip to content

Commit

Permalink
convert to TypeScript, wrap BigInteger and BigRational, #158
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelzoom committed Aug 23, 2023
1 parent 634ab0e commit c7fe14f
Showing 1 changed file with 42 additions and 77 deletions.
119 changes: 42 additions & 77 deletions js/common/model/RationalNumber.ts
Original file line number Diff line number Diff line change
@@ -1,193 +1,158 @@
// Copyright 2016-2023, University of Colorado Boulder

/**
* A wrapper around BigRational.js. Since this wraps only the portion of BigRational.js needed by this simulation,
* it's unlikely to be useful in other sims, and inappropriate to move to a common-code repository.
* RationalNumber is a wrapper around BigRational.js. Since this wraps only the portion of BigRational.js needed by
* this simulation, it's unlikely to be useful in other sims, and inappropriate to move to a common-code repository.
*
* Requires BigInteger.js and BigRational.js to be added to phet.preload in package.json.
* This implementation requires BigInteger.js and BigRational.js to be added to phet.preload in package.json.
* See https://github.com/peterolson/BigRational.js
*
* @author Chris Malley (PixelZoom, Inc.)
*/

import functionBuilder from '../../functionBuilder.js';

// The subset of the BigInteger API that we are using.
type BigInteger = {
valueOf: () => number;
};

// The subset of the BigRational API that we are using.
type BigRational = {
numerator: BigInteger;
denominator: BigInteger;
valueOf: () => number;
plus: ( value: number ) => BigRational;
minus: ( value: number ) => BigRational;
times: ( value: number ) => BigRational;
divide: ( value: number ) => BigRational;
abs: () => BigRational;
toString: () => string;
};

export default class RationalNumber {

/**
* @param numerator
* @param denominator
*/
constructor( numerator, denominator ) {
private readonly bigRational: BigRational;

public constructor( numerator: number, denominator: number ) {

assert && assert( Number.isInteger( numerator ) );
assert && assert( Number.isInteger( denominator ) );

// @private {BigRational} bigRat is a global created by preloading BigRational.js
// @ts-expect-error bigRat is a global created by preloading BigRational.js
this.bigRational = bigRat( numerator, denominator );
}

/**
* Gets the numerator. Assumes that BigRational stores its value in reduced form.
*
* @returns {number}
* @public
*/
getNumerator() { return this.bigRational.numerator.valueOf(); }
public getNumerator(): number { return this.bigRational.numerator.valueOf(); }

get numerator() { return this.getNumerator(); }
public get numerator(): number { return this.getNumerator(); }

/**
* Gets the denominator. Assumes that BigRational stores its value in reduced form.
*
* @returns {number}
* @public
*/
getDenominator() { return this.bigRational.denominator.valueOf(); }
public getDenominator(): number { return this.bigRational.denominator.valueOf(); }

get denominator() { return this.getDenominator(); }
public get denominator(): number { return this.getDenominator(); }

/**
* Two rational numbers are equal if their values are equal.
* E.g. they can have different numerators and denominators, but still represent the same number.
*
* @param {RationalNumber} rationalNumber
* @returns {boolean}
* @public
*/
equals( rationalNumber ) {
public equals( rationalNumber: RationalNumber ): boolean {
return ( rationalNumber.valueOf() === this.valueOf() );
}

/**
* Gets the value of this RationalNumber.
*
* @returns {number}
* @public
*/
valueOf() {
public valueOf(): number {
return this.bigRational.valueOf();
}

/**
* String representation, do not rely on the format of this!
*
* @returns {string}
* @public
*/
toString() {
public toString(): string {
return this.bigRational.toString();
}

/**
* Adds this RationalNumber and an integer, returns a new instance.
*
* @param {number} integerValue
* @returns {RationalNumber}
* @public
*/
plus( integerValue ) {
public plus( integerValue: number ): RationalNumber {
assert && assert( Number.isInteger( integerValue ) );
return toRationalNumber( this.bigRational.plus( integerValue ) );
}

/**
* Subtracts this RationalNumber and an integer, returns a new instance.
*
* @param {number} integerValue
* @returns {RationalNumber}
* @public
*/
minus( integerValue ) {
public minus( integerValue: number ): RationalNumber {
assert && assert( Number.isInteger( integerValue ) );
return toRationalNumber( this.bigRational.minus( integerValue ) );
}

/**
* Multiplies this RationalNumber and an integer, returns a new instance.
*
* @param {number} integerValue
* @returns {RationalNumber}
* @public
*/
times( integerValue ) {
public times( integerValue: number ): RationalNumber {
assert && assert( Number.isInteger( integerValue ) );
return toRationalNumber( this.bigRational.times( integerValue ) );
}

/**
* Divides this RationalNumber by an integer, returns a new instance.
*
* @param {number} integerValue
* @returns {RationalNumber}
* @public
*/
divide( integerValue ) {
public divide( integerValue: number ): RationalNumber {
assert && assert( Number.isInteger( integerValue ) );
return toRationalNumber( this.bigRational.divide( integerValue ) );
}

/**
* Absolute value of this RationalNumber, returns a new instance.
*
* @returns {RationalNumber}
* @public
*/
abs() {
public abs(): RationalNumber {
return toRationalNumber( this.bigRational.abs() );
}

/**
* Is this RationalNumber an integer?
*
* @returns {boolean}
* @public
*/
isInteger() {
public isInteger(): boolean {
return ( this.valueOf() % 1 === 0 );
}

/**
* Gets the whole number part of this RationalNumber's value.
*
* @returns {number}
* @public
*/
wholeNumberPart() {
public wholeNumberPart(): number {
const value = this.bigRational.valueOf();
return ( value < 0 ? -1 : 1 ) * Math.floor( Math.abs( value ) );
}

/**
* Gets the fractional part of this RationalNumber's value, returns a new instance.
*
* @returns {RationalNumber}
* @public
*/
fractionPart() {
public fractionPart(): RationalNumber {
return this.minus( this.wholeNumberPart() );
}

/**
* Creates a RationalNumber from an integer.
*
* @param {number} integerValue
* @returns {RationalNumber}
* @public
* @static
*/
static withInteger( integerValue ) {
public static withInteger( integerValue: number ): RationalNumber {
assert && assert( Number.isInteger( integerValue ) );
return new RationalNumber( integerValue, 1 );
}
}

/**
* Converts a BigRational to a RationalNumber
* @param {BigRational} bigRational
* @returns {RationalNumber}
*/
function toRationalNumber( bigRational ) {
function toRationalNumber( bigRational: BigRational ): RationalNumber {

// BigRational.js does not export type BigRational. This verification works only when unminified.
assert && assert( bigRational.constructor.name === 'BigRational' );
Expand Down

0 comments on commit c7fe14f

Please sign in to comment.