UNPKG

btm-expressions

Version:

BTM (bowtie-math) is a math object model to enable parsing of mathematical expressions into a tree structure that can be manipulated, evaluated, and compared.

241 lines (203 loc) 6.07 kB
/*! * BTM JavaScript Library v@VERSION * https://github.com/dbrianwalton/BTM * * Copyright D. Brian Walton * Released under the MIT license (https://opensource.org/licenses/MIT) * * Date: @DATE */ /* ******************************* ** Define a class to work with rational numbers ******************************* */ import { real_number } from "./real_number.js"; /* Private utility commands. */ function isInt(x) { var retValue = false; if (Number.isInteger === undefined) { retValue = (x == Math.floor(x)); } else { retValue = Number.isInteger(x); } return retValue; } // Implement Euclid's algorithm. export function findGCD(a,b) { var c; a = Math.abs(a); b = Math.abs(b); if (a < b) { c=a; a=b; b=c; } if (b == 0) return 0; // In this loop, we always have a > b. while (b > 0) { c = a % b; a = b; b = c; } return a; } export class rational_number extends real_number { constructor(p,q) { if (q == undefined) { super(p); this.p = p; this.q = 1; } else { super(p/q); if (q == 0) { this.p = Math.sqrt(-1); this.q = 1; } else if (p == 0) { this.p = 0; this.q = 1; } else { if (q < 0) { this.p = -p; this.q = -q; } else { this.p = p; this.q = q; } this.simplify(); } } } // Return a numerical value of the rational expression. value() { return (this.p/this.q); } // Use Euclid's algorithm to find the gcd, then reduce simplify() { var a; // Don't simplify if not ratio of integers. if (this.p % 1 != 0 || this.q % 1 != 0) { return; } a = findGCD(this.p, this.q); this.p /= a; this.q /= a; } equal(other) { if (other instanceof rational_number) { return (this.p.valueOf()==other.p.valueOf() && this.q.valueOf() == other.q.valueOf()); } else { return (this.value()==other.value()); } } // Add to this rational another rational number and create new object. add(other) { var sum; if (other instanceof rational_number) { sum = new rational_number(this.p*other.q+other.p*this.q, this.q*other.q); } else if (isInt(other)) { sum = new rational_number(this.p+other*this.q, this.q); } else { sum = new real_number(this.value() + other); } return(sum); } // Subtract from this rational another rational number and create new object. subtract(other) { var sum; if (other instanceof rational_number) { sum = new rational_number(this.p*other.q-other.p*this.q, this.q*other.q); } else if (isInt(other)) { sum = new rational_number(this.p-other*this.q, this.q); } else { sum = new real_number(this.value() - other); } return(sum); } // Multiply this rational by another rational number and create new object. multiply(other) { var product; if (other instanceof rational_number) { product = new rational_number(this.p*other.p, this.q*other.q); } else if (isInt(other)) { product = new rational_number(this.p*other, this.q); } else { product = new real_number(this.value() * other); } return(product); } // Divide this rational by another rational number and create new object. divide(other) { var product; if (other instanceof rational_number) { product = new rational_number(this.p*other.q, this.q*other.p); } else if (isInt(other)) { product = new rational_number(this.p, this.q*other); } else { product = new real_number(this.value() / other); } return(product); } // Additive Inverse addInverse() { var inverse = new rational_number(-this.p, this.q); return(inverse); } // Multiplicative Inverse multInverse() { var inverse; if (this.p != 0) { inverse = new rational_number(this.q, this.p); } else { inverse = new real_number(NaN); } return(inverse); } // Format the rational number as string. toString(leadSign) { if (typeof leadSign == 'undefined') { leadSign = false; } var str = (leadSign && this.p>0) ? '+' : ''; if (isNaN(this.p)) { str = 'NaN'; } else if (this.q == 1) { str = str + this.p; } else { str = str + this.p + '/' + this.q; } return(str); } // Format the rational number as TeX string. toTeX(leadSign) { if (typeof leadSign == 'undefined') { leadSign = false; } var str = (leadSign && this.p>0) ? '+' : ''; if (isNaN(this.p)) { str = '\\mathrm{NaN}'; } else if (this.q == 1) { str = str + this.p; } else { if (this.p < 0) { str = '-\\frac{' + -this.p + '}{' + this.q + '}'; } else { str = str + '\\frac{' + this.p + '}{' + this.q + '}'; } } return(str); } toMathML() { if (typeof leadSign == 'undefined') { leadSign = false; } var opAStr = "<cn>" + this.p + "</cn>", opBStr = "<cn>" + this.q + "</cn>"; return("<cn>" + this.toString() + "</cn>"); if (isNaN(this.p)) { str = "<cn>?</cn>"; } else if (this.q == 1) { str = opAStr; } else { str = "<apply><divide/>"+opAStr+opBStr+"</apply>"; } } }