import DivisionRingOperators from './DivisionRingOperators';
import mustBeInteger from '../checks/mustBeInteger';
import readOnly from '../i18n/readOnly';
const magicCode = Math.random()
/**
* The QQ class represents a rational number, ℚ.
*
* The QQ implementation is that of an immutable (value) type.
*
* The numerator and denominator are reduced to their lowest form.
*
* Construct new instances using the static valueOf method.
*/
export class QQ implements DivisionRingOperators {
/**
*
*/
private _numer: number;
/**
*
*/
private _denom: number;
/**
* Intentionally undocumented.
*/
constructor(n: number, d: number, code: number) {
if (code !== magicCode) {
throw new Error("Use the static create method instead of the constructor")
}
mustBeInteger('n', n);
mustBeInteger('d', d);
var g: number;
const gcd = function(a: number, b: number) {
var temp: number;
if (a < 0) {
a = -a;
}
if (b < 0) {
b = -b;
}
if (b > a) {
temp = a;
a = b;
b = temp;
}
while (true) {
a %= b;
if (a === 0) {
return b;
}
b %= a;
if (b === 0) {
return a;
}
}
};
if (d === 0) {
throw new Error("denominator must not be zero");
}
if (n === 0) {
g = 1;
}
else {
g = gcd(Math.abs(n), Math.abs(d));
}
if (d < 0) {
n = -n;
d = -d;
}
this._numer = n / g;
this._denom = d / g;
}
/**
*
* @readOnly
*/
get numer(): number {
return this._numer;
}
set numer(unused: number) {
throw new Error(readOnly('numer').message)
}
/**
*
* @readOnly
*/
get denom(): number {
return this._denom;
}
set denom(unused: number) {
throw new Error(readOnly('denom').message)
}
/**
* @param rhs
* @returns
*/
add(rhs: QQ): QQ {
return QQ.valueOf(this._numer * rhs._denom + this._denom * rhs._numer, this._denom * rhs._denom);
}
/**
* @param rhs
* @returns
*/
sub(rhs: QQ): QQ {
return QQ.valueOf(this._numer * rhs._denom - this._denom * rhs._numer, this._denom * rhs._denom);
}
/**
* @param rhs
* @returns
*/
mul(rhs: QQ): QQ {
return QQ.valueOf(this._numer * rhs._numer, this._denom * rhs._denom);
}
/**
* @param rhs
* @returns
*/
div(rhs: QQ): QQ {
const numer = this._numer * rhs._denom
const denom = this._denom * rhs._numer
if (numer === 0) {
if (denom === 0) {
// How do we handle undefined?
return QQ.valueOf(numer, denom)
}
else {
return QQ.ZERO
}
}
else {
if (denom === 0) {
// How do we handle division by zero.
return QQ.valueOf(numer, denom)
}
else {
return QQ.valueOf(numer, denom)
}
}
}
/**
* @returns
*/
isOne(): boolean {
return this._numer === 1 && this._denom === 1
}
/**
* @returns
*/
isZero(): boolean {
return this._numer === 0 && this._denom === 1
}
/**
* @returns
*/
hashCode(): number {
return 37 * this.numer + 13 * this.denom
}
/**
* Computes the multiplicative inverse of this rational number.
*
* @returns
*/
inv(): QQ {
return QQ.valueOf(this._denom, this._numer);
}
/**
* Computes the additive inverse of this rational number.
*
* @returns
*/
neg(): QQ {
return QQ.valueOf(-this._numer, this._denom);
}
/**
* Determines whether two rational numbers are equal.
*
* @param other
* @returns
*/
equals(other: QQ): boolean {
if (other instanceof QQ) {
return this._numer * other._denom === this._denom * other._numer;
}
else {
return false;
}
}
/**
* Computes a non-normative string representation of this rational.
*
* @returns
*/
toString(): string {
return "" + this._numer + "/" + this._denom + ""
}
/**
* @param rhs
* @returns
*/
__add__(rhs: QQ): QQ {
if (rhs instanceof QQ) {
return this.add(rhs)
}
else {
return void 0
}
}
/**
* @param lhs
* @returns
*/
__radd__(lhs: QQ): QQ {
if (lhs instanceof QQ) {
return lhs.add(this)
}
else {
return void 0
}
}
/**
* @param rhs
* @returns
*/
__sub__(rhs: QQ): QQ {
if (rhs instanceof QQ) {
return this.sub(rhs)
}
else {
return void 0
}
}
/**
* @param lhs
* @returns
*/
__rsub__(lhs: QQ): QQ {
if (lhs instanceof QQ) {
return lhs.sub(this)
}
else {
return void 0
}
}
/**
* @param rhs
* @returns
*/
__mul__(rhs: QQ): QQ {
if (rhs instanceof QQ) {
return this.mul(rhs)
}
else {
return void 0
}
}
/**
* @param lhs
* @returns
*/
__rmul__(lhs: QQ): QQ {
if (lhs instanceof QQ) {
return lhs.mul(this)
}
else {
return void 0
}
}
/**
* @param div
* @returns
*/
__div__(rhs: QQ): QQ {
if (rhs instanceof QQ) {
return this.div(rhs)
}
else {
return void 0
}
}
/**
* @param lhs
* @returns
*/
__rdiv__(lhs: QQ): QQ {
if (lhs instanceof QQ) {
return lhs.div(this)
}
else {
return void 0
}
}
/**
* @returns
*/
__pos__(): QQ {
return this
}
/**
* @returns
*/
__neg__(): QQ {
return this.neg()
}
//
// Immutable constants allow us to avoid creating
// temporary QQ instances for the common values.
//
private static POS_08_01: QQ = new QQ(8, 1, magicCode)
private static POS_07_01: QQ = new QQ(7, 1, magicCode)
private static POS_06_01: QQ = new QQ(6, 1, magicCode)
private static POS_05_01: QQ = new QQ(5, 1, magicCode)
private static POS_04_01: QQ = new QQ(4, 1, magicCode)
private static POS_03_01: QQ = new QQ(3, 1, magicCode)
private static POS_02_01: QQ = new QQ(2, 1, magicCode)
private static ONE: QQ = new QQ(1, 1, magicCode)
private static POS_01_02: QQ = new QQ(1, 2, magicCode)
private static POS_01_03: QQ = new QQ(1, 3, magicCode)
private static POS_01_04: QQ = new QQ(1, 4, magicCode)
private static POS_01_05: QQ = new QQ(1, 5, magicCode)
private static ZERO: QQ = new QQ(0, 1, magicCode)
private static NEG_01_03: QQ = new QQ(-1, 3, magicCode)
private static NEG_01_01: QQ = new QQ(-1, 1, magicCode)
private static NEG_02_01: QQ = new QQ(-2, 1, magicCode)
private static NEG_03_01: QQ = new QQ(-3, 1, magicCode)
private static POS_02_03: QQ = new QQ(2, 3, magicCode)
/**
* @param numer
* @param denom
* @returns
*/
static valueOf(n: number, d: number): QQ {
if (n === 0) {
if (d !== 0) {
return QQ.ZERO
}
else {
// This is the undefined case, 0/0.
}
}
else if (d === 0) {
// Fall through
}
else if (n === d) {
return QQ.ONE
}
else if (n === 1) {
if (d === 2) {
return QQ.POS_01_02
}
else if (d === 3) {
return QQ.POS_01_03
}
else if (d === 4) {
return QQ.POS_01_04
}
else if (d === 5) {
return QQ.POS_01_05
}
else if (d === -3) {
return QQ.NEG_01_03
}
}
else if (n === -1) {
if (d === 1) {
return QQ.NEG_01_01
}
else if (d === 3) {
return QQ.NEG_01_03
}
}
else if (n === 2) {
if (d === 1) {
return QQ.POS_02_01
}
else if (d === 3) {
return QQ.POS_02_03
}
}
else if (n === -2) {
if (d === 1) {
return QQ.NEG_02_01
}
}
else if (n === 3) {
if (d === 1) {
return QQ.POS_03_01
}
}
else if (n === -3) {
if (d === 1) {
return QQ.NEG_03_01
}
}
else if (n === 4) {
if (d === 1) {
return QQ.POS_04_01
}
}
else if (n === 5) {
if (d === 1) {
return QQ.POS_05_01
}
}
else if (n === 6) {
if (d === 1) {
return QQ.POS_06_01
}
}
else if (n === 7) {
if (d === 1) {
return QQ.POS_07_01
}
}
else if (n === 8) {
if (d === 1) {
return QQ.POS_08_01
}
}
// console.warn(`QQ.valueOf(${n},${d}) is not cached.`)
return new QQ(n, d, magicCode)
}
}