/*
* rational.js - Javascript tools and libraries based around rational numbers.
* Copyright (C) 2013 Dylan Ferris
*
* This file is part of rational.js.
*
* rational.js is free software: you may redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* rational.js is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with rational.js. If not, see <http://www.gnu.org/licenses/>.
*/
/*
// for nodejs
if (typeof BigInteger !== 'function') {
var BigInteger = require('../src/biginteger.js').BigInteger;
}
if (typeof bigint !== 'object') {
var bigint = require('../src/bigint.js').bigint;
}
*/
/**
* The inverse of the allowable difference in approximations
*
* @property RAT_INFINITESIMAL_PRECISION
* @static
* @final
*/
if(!BIGRAT_INFINITESIMAL_PRECISION) {
var BIGRAT_INFINITESIMAL_PRECISION = new BigInteger('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
}
/**
* Exit (possibly infinite) loops after this many iterations
*
* @property BIGRAT_MAX_LOOPS
* @static
* @final
*/
if(!BIGRAT_MAX_LOOPS) {
var BIGRAT_MAX_LOOPS = 1<<30;
}
/**
* @class Arbitrary Sized Rational Number
* @name bigrat
* @requires bigint BigInteger
*/
var bigrat = {};
/**
* Machine Epsilon, floats within this distance of each other are considered equal
*
* @property EPSILON
* @static
* @final
*/
bigrat.EPSILON = 2e-16;
/**
* Exit (possibly infinite) loops after this many iterations
*
* @property MAX_LOOPS
* @static
* @final
*/
bigrat.MAX_LOOPS = BIGRAT_MAX_LOOPS;
/**
* Creates a new, empty bigrat
*
* @returns {bigrat} a new bigrational number
*/
bigrat.create = function() {
var out = [];
out[0] = BigInteger.ZERO;
out[1] = BigInteger.ONE;
return out;
};
/**
* Creates a new bigrat initialized with values from an existing number
*
* @param {bigrat} a number to clone
* @returns {bigrat} a new bigrational number
*/
bigrat.clone = function(a) {
var out = [];
out[0] = a[0];
out[1] = a[1];
return out;
};
/**
* Creates a new bigrat initialized with the given values
*
* @param {Number} n Numerator
* @param {Number} d Denominator
* @returns {bigrat} a new bigrational number
*/
bigrat.fromValues = function(n, d) {
var out = [];
out[0] = new BigInteger(n);
out[1] = new BigInteger(d);
return bigrat.normalize(out, out);
};
/**
* Copy the values from one bigrat to another
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the source number
* @returns {bigrat} out
*/
bigrat.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
return out;
};
/**
* Set the components of a bigrat to the given values
*
* @param {bigrat} out the receiving number
* @param {BigInteger} n Numerator
* @param {BigInteger} d Denominator
* @returns {bigrat} out
*/
bigrat.set = function(out, n, d) {
out[0] = n;
out[1] = d;
return bigrat.normalize(out, out);
};
/**
* Returns a rat from a bigrat
*
* @param {rat} out the receiving number
* @param {bigrat} a number to truncate
* @returns {rat} out
*/
bigrat.toRat = function (out, a) {
return rat.set(out, a[0].toJSValue(), a[1].toJSValue());
};
/**
* Returns a bigrat from a rat
*
* @param {bigrat} out the receiving number
* @param {rat} a number to convert
* @returns {bigrat} out
*/
bigrat.fromRat = function (out, a) {
return bigrat.set(out, BigInteger(a[0]), BigInteger(a[1]));
};
/**
* Absolute value of a bigrat
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number to take the absolute value of
* @returns {bigrat} out
*/
bigrat.abs = function(out, a) {
out[0] = a[0].abs();
out[1] = a[1];
return out;
};
/**
* Inverts a bigrat
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number to invert
* @returns {bigrat} out
*/
bigrat.invert = function(out, a) {
var temp = a[0];
out[0] = a[1];
out[1] = temp;
return out;
};
/**
* Alias for {@link bigrat.invert}
* @function
*/
bigrat.reciprocal = bigrat.invert;
/**
* Adds two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.add = function(out, a, b) {
if (a[1].compare(b[1])===0) {
out[0] = a[0].add(b[0]);
out[1] = a[1];
}
else {
out[0] = a[0].multiply(b[1]).add( b[0].multiply(a[1]) );
out[1] = a[1].multiply(b[1]);
}
return bigrat.normalize(out, out);
};
/**
* Subtracts two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.subtract = function(out, a, b) {
if (a[1].compare(b[1])===0) {
out[0] = a[0].subtract(b[0]);
out[1] = a[1];
}
else {
out[0] = a[0].multiply(b[1]).subtract( b[0].multiply(a[1]) );
out[1] = a[1].multiply(b[1]);
}
return bigrat.normalize(out, out);
};
/**
* Alias for {@link bigrat.subtract}
* @function
*/
bigrat.sub = bigrat.subtract;
/**
* Multiplies two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.multiply = function(out, a, b) {
out[0] = a[0].multiply(b[0]);
out[1] = a[1].multiply(b[1]);
return bigrat.normalize(out, out);
};
/**
* Alias for {@link bigrat.multiply}
* @function
*/
bigrat.mul = bigrat.multiply;
/**
* Mediant of two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out the sum of the numerators divided by the sum of the denominators
*/
bigrat.mediant = function(out, a, b) {
out[0] = a[0].add(b[0]);
out[1] = a[1].add(b[1]);
return bigrat.normalize(out, out);
};
/**
* Divides two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.divide = function(out, a, b) {
out[0] = a[0].multiply(b[1]);
out[1] = a[1].multiply(b[0]);
return bigrat.normalize(out, out);
};
/**
* Alias for {@link bigrat.divide}
* @function
*/
bigrat.div = bigrat.divide;
/**
* Returns true when the first bigrat is equal to the second
*
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {Bool} true when the two bigrats are equal
*/
bigrat.equals = function(a, b) {
if (a[0].isZero() && b[0].isZero()) return true; // both are Zero
if (a[1].isZero() && b[1].isZero()) return true; // both are Infinity
return (a[0].compare(b[0])===0) && (a[1].compare(b[1])===0);
};
/**
* Returns true when the first bigrat is approximately equal to the second
*
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {Bool} true when the difference between the two bigrats is less than bigrat.INFINITESIMAL
*/
bigrat.approximates = function(a, b) {
if (bigrat.equals(a, b)) return true;
var d = bigrat.create();
bigrat.sub(d, a, b);
bigrat.abs(d, d);
return bigrat.isLessThan(d, bigrat.INFINITESIMAL);
};
/**
* Returns true when the first bigrat is larger than the second
*
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {Bool} true when the first operand is larger
*/
bigrat.isGreaterThan = function(a, b) {
if (bigrat.equals(a, b)) return false;
return a[0].multiply(b[1]).compare( b[0].multiply(a[1]) ) > 0;
};
/**
* Returns true when the first bigrat is smaller than the second
*
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {Bool} true when the first operand is smaller
*/
bigrat.isLessThan = function(a, b) {
if (bigrat.equals(a, b)) return false;
return a[0].multiply(b[1]).compare( b[0].multiply(a[1]) ) < 0;
};
/**
* Returns true when the bigrat is negative
*
* @param {bigrat} a the number to check
* @returns {Bool} true when the number is less than zero
*/
bigrat.isNegative = function(a) {
return a[0].isNegative();
};
/**
* Returns the minimum of two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.min = function(out, a, b) {
if (bigrat.isLessThan(a, b)) {
out[0] = a[0];
out[1] = a[1];
}
else {
out[0] = b[0];
out[1] = b[1];
}
return out;
};
/**
* Returns the maximum of two bigrats
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {bigrat} out
*/
bigrat.max = function(out, a, b) {
if (bigrat.isGreaterThan(a, b)) {
out[0] = a[0];
out[1] = a[1];
}
else {
out[0] = b[0];
out[1] = b[1];
}
return out;
};
/**
* Multiplies a bigrat's numerator by an integer
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the number to multiply
* @param {Integer} b amount to multiply the number by
* @returns {bigrat} out
*/
bigrat.scalar_multiply = function(out, a, b) {
out[0] = a[0].multiply(b);
out[1] = a[1];
return bigrat.normalize(out, out);
};
/**
* Multiplies a bigrat's denominator by an integer
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the number to divide
* @param {Integer} b amount to divide by
* @returns {bigrat} out
*/
bigrat.scalar_divide = function(out, a, b) {
out[0] = a[0];
out[1] = a[1].multiply(b);
return bigrat.normalize(out, out);
};
/**
* Normalize a bigrat
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number to normalize
* @returns {bigrat} out
*/
bigrat.normalize = function(out, a) {
//if (isNaN(a[0])||isNaN(a[1])) return out = bigrat.clone(bigrat.INFINULL);
if (a[0].isZero()||a[1].isZero()) return out = a;
if (a[0].compare(a[1])===0) return out = bigrat.clone(bigrat.ONE);
if (!a[1].isNegative()) {
out[0] = a[0];
out[1] = a[1];
if (out[1].isZero()) return out
}
else {
out[0] = a[0].negate();
out[1] = a[1].negate();
}
var gcd = bigint.greatest_common_divisor(out[0].abs(), out[1]);
if (gcd.compare(BigInteger.ONE)>0) {
out[0] = out[0].quotient(gcd);
out[1] = out[1].quotient(gcd);
}
return out;
};
/**
* Negates a bigrat
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number to negate
* @returns {bigrat} out
*/
bigrat.opposite = function(out, a) {
out[0] = a[0].negate();
out[1] = a[1];
return out;
};
/**
* Alias for {@link bigrat.opposite}
* @function
*/
bigrat.negative = bigrat.opposite;
/**
* Alias for {@link bigrat.opposite}
* @function
*/
bigrat.neg = bigrat.opposite;
/**
* Raises a bigrat to an integer exponent
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the number to exponentiate
* @param {Integer} p power to raise the number by
* @returns {bigrat} out
*/
bigrat.power = function(out, a, p) {
if (p===2) {
out[0] = a[0].square();
out[1] = a[1].square();
}
else if (p>0) {
out[0] = a[0].pow(p);
out[1] = a[1].pow(p);
}
else if (p<0) {
p = Math.abs(p);
out[0] = a[1].pow(p);
out[1] = a[0].pow(p);
}
else {
bigrat.copy(out, bigrat.ONE);
}
return out;
};
/**
* Alias for {@link bigrat.power}
* @function
*/
bigrat.pow = bigrat.power;
/**
* Find a bigrational number which approximates the input number when multiplied by itself
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the number to find the root of
* @returns {bigrat} out
*/
bigrat.sqrt = function (out, a) {
return bigrat.nthRoot(out, a, 2);
};
/**
* Find a bigrat approximation which equals the input bigrat when raised to the given integer exponent
*
* Newton's method converges alot faster... could that be used to find the pattern in the SB tree?
*
* @param {bigrat} out the receiving number
* @param {bigrat} a the number to find the root of
* @param {Integer} n
* @returns {bigrat} out
*/
bigrat.nthRoot = function (out, a, n) {
if (bigrat.equals(a, bigrat.ZERO)) return bigrat.copy(out, bigrat.ZERO);
if (bigrat.equals(a, bigrat.ONE)) return bigrat.copy(out, bigrat.ONE);
if (bigrat.equals(a, bigrat.INFINITY)) return bigrat.copy(out, bigrat.INFINITY);
if (bigrat.equals(a, bigrat.INFINULL)) return bigrat.copy(out, bigrat.INFINULL);
var neg = bigrat.isNegative(a);
if (neg) a[0] = a[0].negate();
bigrat.copy(out, bigrat.ONE);
var m = [
BigInteger(1),
BigInteger(0),
BigInteger(0),
BigInteger(1)
];
var test = bigrat.clone(bigrat.ONE);
var c = BIGRAT_MAX_LOOPS;
while ( !bigrat.approximates(a, test) && c-- ) {
if (bigrat.isLessThan(a, test)) {
m[0] = m[0].add(m[1]);
m[2] = m[2].add(m[3]);
}
else {
m[1] = m[1].add(m[0]);
m[3] = m[3].add(m[2]);
}
out[0] = m[0].add(m[1]);
out[1] = m[2].add(m[3]);
bigrat.pow(test, out, n);
}
if (neg) {
a[0] = a[0].negate();
if (n%2===1) bigrat.neg(out, out);
}
return out;
};
/**
* Calculates the dot product of two bigrats
*
* @param {bigrat} a the first operand
* @param {bigrat} b the second operand
* @returns {Integer} dot product of a and b
*/
bigrat.dot = function (a, b) {
return a[0].multiply(b[0]).add( a[1].multiply(b[1]) );
};
/**
* Returns a string representation
*
* @param {bigrat} a number to represent as a string
* @returns {String} string representation of the number
*/
bigrat.str = function (a) {
return a[1].compare(BigInteger.ONE)===0 ? a[0].toString() : a[0].toString() + '/' + a[1].toString();
};
/**
* Returns a decimal approximation
*
* @param {bigrat} a number to approximate as a decimal
* @returns {Float} decimal approximation of the number
*/
bigrat.toDecimal = function (a) {
return a[0].toJSValue() / a[1].toJSValue();
};
/**
* Alias for {@link bigrat.toDecimal}
* @function
*/
bigrat.dec = bigrat.toDecimal;
/**
* Returns a big integer approximation (truncated towards zero)
*
* @param {bigrat} a number to round
* @returns {Integer} native integer approximation of the number
*/
bigrat.toInteger = function (a) {
return a[0].quotient(a[1]).toJSValue();
};
/**
* Alias for {@link bigrat.toInteger}
* @function
*/
bigrat.round = bigrat.toInteger;
/**
* Returns a big integer approximation (truncated towards zero)
*
* @param {bigrat} a number to round
* @returns {BigInteger} big integer approximation of the number
*/
bigrat.toBigInteger = function (a) {
return a[0].quotient(a[1]);
};
/**
* Returns the closest integer approximation by rounding down
*
* @param {bigrat} a number to round down to the nearest integer
* @returns {Integer} integer approximation of the number
*/
bigrat.floor = function (a) {
return Math.floor(bigrat.toDecimal(a));
};
/**
* Returns the closest integer approximation by rounding up
*
* @param {bigrat} a number to round up to the nearest integer
* @returns {Integer} integer approximation of the number
*/
bigrat.ceil = function (a) {
return Math.ceil(bigrat.toDecimal(a));
};
/**
* Returns a bigrat from an integer, copying to an existing bigrat
*
* @param {bigrat} out the receiving number
* @param {Integer} signed integer
* @returns {bigrat} out
*/
bigrat.fromInteger_copy = function (out, a) {
out[0] = BigInteger(a);
out[1] = BigInteger.ONE;
return out;
};
/**
* Returns a bigrat from an integer, creating a new bigrat
*
* @param {Integer} signed integer
* @returns {bigrat} out
*/
bigrat.fromInteger = function (a) {
return bigrat.fromInteger_copy(bigrat.create(), a);
};
/**
* Returns a bigrat from the inverse of an integer, copying to an existing bigrat
*
* @param {bigrat} out the receiving number
* @param {Integer} signed integer
* @returns {bigrat} out
*/
bigrat.fromIntegerInverse_copy = function (out, a) {
out[0] = BigInteger.ONE;
out[1] = BigInteger(a);
if (out[1].isNegative()) {
out[0] = out[0].negate();
out[1] = out[1].negate();
}
return out;
};
/**
* Returns a bigrat from the inverse of an integer, creating a new bigrat
*
* @param {Integer} signed integer
* @returns {bigrat} out
*/
bigrat.fromIntegerInverse = function (a) {
return bigrat.fromIntegerInverse_copy(bigrat.create(), a);
};
/**
* Returns a bigrat from a decimal number, creating a new bigrat
*
* @param {Number} a decimal number
* @returns {bigrat} out
*/
bigrat.fromDecimal = function (a) {
return bigrat.fromDecimal_copy(bigrat.create(), a);
};
/**
* Returns a bigrat from a decimal number, copying to an existing bigrat
*
* @param {bigrat} out the receiving number
* @param {Number} a decimal number
* @returns {bigrat} out
*/
bigrat.fromDecimal_copy = function (out, a) {
a = parseFloat(a);
if (isNaN(a)) return bigrat.copy(out, bigrat.INFINULL);
if (a===Infinity) return bigrat.copy(out, bigrat.INFINITY);
if (Math.abs(a) < bigrat.EPSILON) return bigrat.copy(out, bigrat.ZERO);
if (Math.abs(a-1) < bigrat.EPSILON) return bigrat.copy(out, bigrat.ONE);
if (Math.abs(a%1) < bigrat.EPSILON) return bigrat.fromInteger_copy(out, a);
if (Math.abs((1/a)%1) < bigrat.EPSILON) return bigrat.fromIntegerInverse_copy(out, Math.round(1/a));
bigrat.copy(out, bigrat.ONE);
var
m = [
BigInteger(1),
BigInteger(0),
BigInteger(0),
BigInteger(1)
],
test = a,
integer_part = BigInteger(1),
integer_part_difference = 0,
c = bigrat.MAX_LOOPS;
//while (c-- && Math.abs(out[0].valueOf() - a*out[1].valueOf()) > 2e-8) {
while (c-- && Math.abs(a - bigrat.toDecimal(out)) > bigrat.EPSILON) {
integer_part = BigInteger(Math.floor(test));
out[0] = integer_part.multiply(m[0]).add(m[2]);
out[1] = integer_part.multiply(m[1]).add(m[3]);
integer_part_difference = test - parseInt(integer_part.toString(), 10);
if (integer_part_difference) {
test = 1 / integer_part_difference;
}
else {
// return if result matches input
if (a.toString()===bigrat.toDecimal(out)) {
break;
}
// worse case, treat the input as an integer divided by 10 times the number of decimal places
var match = (a.toString()+'e+10').match(/(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
var decimal_count =
match
? Math.max(
// number of digits right of decimal point
(match[2] ? match[2].length : 0)
// adjust for scientific notation
//- (match[3] ? +match[3] : 0)
)
: 0;
bigrat.add(
out,
bigrat.fromInteger(match[1]),
bigrat.fromValues(match[2], Math.pow(10, decimal_count))
);
break;
}
m[2] = m[0];
m[3] = m[1];
m[0] = out[0];
m[1] = out[1];
}
return out;
};
/**
* Creates a new bigrat from two random integers
*
* @param {bigrat} out the receiving number
* @returns {bigrat} a random bigrational number
*/
bigrat.fromRandom = function(out) {
out[0] = BigInteger(Math.random()*0xFFFFFFFFFFFFF<<0);
out[1] = BigInteger(Math.abs(Math.random()*0xFFFFFFFFFFFFF<<0));
return bigrat.normalize(out, out);
};
/**
* Parametric sine: 2a / (1 + a²)
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number for which to calculate the parametric sine
* @returns {bigrat} out
*/
bigrat.sin = function(out, a) {
if (a[1].isZero()) return bigrat.copy(out, bigrat.ZERO);
bigrat.scalar_multiply(out, a, 2);
var d = bigrat.create();
bigrat.pow(d, a, 2);
bigrat.add(d, d, bigrat.ONE);
bigrat.divide(out, out, d);
return out;
};
/**
* Parametric cosine: (1 - a²) / (1 + a²)
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number for which to calculate the parametric cosine
* @returns {bigrat} out
*/
bigrat.cos = function(out, a) {
if (a[1].isZero()) return bigrat.neg(out, bigrat.ONE);
var a2 = bigrat.create();
bigrat.pow(a2, a, 2);
bigrat.sub(out, bigrat.ONE, a2);
var d = bigrat.create();
bigrat.add(d, bigrat.ONE, a2);
bigrat.divide(out, out, d);
return out;
};
/**
* Parametric tangent: sin(a) / cos(a)
*
* @param {bigrat} out the receiving number
* @param {bigrat} a number for which to calculate the parametric tangent
* @returns {bigrat} out
*/
bigrat.tan = function(out, a) {
bigrat.scalar_multiply(out, a, 2); // out = a * 2
var t = bigrat.create();
bigrat.pow(t, a, 2); // t = a * a
bigrat.scalar_multiply(t, t, 2); // t *= 2
bigrat.add(out, out, t); // out += t
bigrat.pow(t, a, 4); // t = a * a * a * a
bigrat.sub(t, bigrat.ONE, t); // t = 1 - t
bigrat.divide(out, out, t);
return out;
};
/**
* Returns an Egyptian representation
* The returned string can be evaluated in "calc - arbitrary precision calculator"
*
* @param {bigrat} a number to represent as an Egyptian fraction
* @returns {String} string representing the most simple sum of fractions having a numerator of one, in "calc" format
*/
bigrat.toEgyptian = function (a) {
var t = bigrat.clone(a);
bigrat.abs(t, t);
var b = bigrat.floor(t);
if (b) bigrat.sub(t, t, bigrat.fromInteger(b));
if (!t[0]) return b.toString();
if (!b) b = '';
var d = 1;
var f = bigrat.create();
while (t[0] !== 1) {
d++;
f = bigrat.fromValues(1, d);
if (bigrat.isGreaterThan(t, f)) {
if (b) b += ' + ';
b += bigrat.str(f);
bigrat.sub(t, t, f);
}
}
if (!t) {
if (!b) return '0';
return b;
}
return b ? b + ' + ' + bigrat.str(t) : bigrat.str(t);
};
/**
* Returns a Babylonian representation (base 60)
* The returned string can be evaluated in "calc - arbitrary precision calculator"
*
* @param {bigrat} a number to represent as a Babylonian fraction
* @returns {String} string containing the decimal representations of the base 60 digits and their powers, in "calc" format
*/
bigrat.toBabylonian = function (a) {
var s = '';
// there must be a better way to do this, avoiding conversion to decimal
var t = bigrat.toDecimal(a);
var n = parseInt(t);
var r = t - n;
var d = 0;
var p = 0;
while (n > 0) {
d = n % 60;
if (d) s = d + ' * 60^' + p + ( s ? ' + ' : '' ) + s;
n = (n - d) / 60;
p++;
}
p = -1;
while (r > 0) {
r *= 60;
d = parseInt(r + 1E-13);
r = r - d;
if (r < -1E-13) continue;
if (d) s += ( s ? ' + ' : '' ) + d + ' * 60^' + p;
p--;
}
return s ? s : '0';
};
/**
* Returns a string of L's and R's representing the Stern Brocot path
*
* @param {bigrat} a number to trace in the Stern Brocot tree
* @returns {String} Stern Brocot path
*/
bigrat.traceSternBrocot = function (a) {
var path = '';
if (
bigrat.equals(a, bigrat.ZERO)
||
bigrat.equals(a, bigrat.ONE)
||
bigrat.equals(a, bigrat.INFINITY)
||
bigrat.equals(a, bigrat.INFINULL)
) return path;
var test = bigrat.clone(a);
var neg = bigrat.isNegative(test);
if (neg) test[0] = test[0].negate();
var r = bigrat.clone(bigrat.ONE);
var m = [
BigInteger(1),
BigInteger(0),
BigInteger(0),
BigInteger(1)
];
var r_streak = 0;
var l_streak = 0;
var c = 65536;
while ( !bigrat.equals(test, r) && c-- ) {
if (bigrat.isLessThan(test, r)) {
m[0] = m[0].add(m[1]);
m[2] = m[2].add(m[3]);
l_streak++;
if (r_streak) {
path += 'R';
if (r_streak!==1) path += r_streak;
r_streak = 0;
path += ' ';
}
}
else {
m[1] = m[1].add(m[0]);
m[3] = m[3].add(m[2]);
r_streak++;
if (l_streak) {
path += 'L';
if (l_streak!==1) path += l_streak;
l_streak = 0;
path += ' ';
}
}
r[0] = m[0].add(m[1]);
r[1] = m[2].add(m[3]);
}
if (l_streak) {
path += 'L';
if (l_streak!==1) path += l_streak;
}
else if (r_streak) {
path += 'R';
if (r_streak!==1) path += r_streak;
}
if (c<0) path += '...';
return path;
};
/**
* Returns an array of integers representing the continued fraction
*
* @param {bigrat} a number to convert to a continued fraction
* @param {Integer} maximum number of iterations
* @returns {Array} integers of the continued fraction
*/
bigrat.toContinuedFraction = function (a, loop_limit) {
loop_limit = typeof loop_limit==='undefined' ? 65536 : parseInt(loop_limit);
if (bigrat.equals(a, bigrat.ZERO)) return [0];
if (bigrat.equals(a, bigrat.ONE)) return [1];
if (bigrat.equals(a, bigrat.NEGONE)) return [-1];
if (bigrat.equals(a, bigrat.INFINITY)) return [1, 0];
if (bigrat.equals(a, bigrat.INFINULL)) return [0, 0];
var test = bigrat.clone(a);
var neg = bigrat.isNegative(test);
if (neg) test[0] = test[0].negate();
var r = bigrat.clone(bigrat.ONE);
var m = [
BigInteger(1),
BigInteger(0),
BigInteger(0),
BigInteger(1)
];
var direction = 1;
var result = [0];
var result_last = result.length - 1;
while ( !bigrat.equals(test, r) && loop_limit-- ) {
if (bigrat.isLessThan(test, r)) {
if (direction===-1) {
result[result_last]++;
}
else {
direction = -1;
result.push(1);
result_last++;
}
m[0] = m[0].add(m[1]);
m[2] = m[2].add(m[3]);
}
else {
if (direction===1) {
result[result_last]++;
}
else {
direction = 1;
result.push(1);
result_last++;
}
m[1] = m[1].add(m[0]);
m[3] = m[3].add(m[2]);
}
r[0] = m[0].add(m[1]);
r[1] = m[2].add(m[3]);
}
// add a zero to the end to indicate an incomplete result
if (loop_limit<0) result.push(0);
else result[result_last]++;
if (neg) for (var i in result) result[i] = -result[i];
return result;
};
/**
* Returns a bigrat from an array of integers representing a continued fraction
*
* @param {bigrat} out the receiving number
* @param {Array} integers of the continued fraction
* @returns {bigrat} out
*/
bigrat.fromContinuedFraction = function(out, cf) {
bigrat.fromInteger_copy(out, cf[cf.length-1]);
for (var i=cf.length-2;i>-1;i--) {
bigrat.invert(out, out);
bigrat.add(out, bigrat.fromInteger(cf[i]), out);
}
return out;
};
/**
* Return the factorial of n as a big rational number
*
* @param {bigrat} out the receiving number
* @param {Integer} n
* @returns {bigrat} factorial of n
*/
bigrat.fromFactorial = function(out, n) {
if (typeof factorial.PREPARED[n] !== 'undefined') return bigrat.fromInteger_copy(out, factorial.PREPARED[n]);
// calculate the factorial if it was not predefiend
var p = factorial.PREPARED.length - 1;
bigrat.fromInteger_copy(out, factorial.PREPARED[p]);
while (p < n) {
bigrat.scalar_multiply(out, out, ++p);
}
return out;
}
/**
* Returns a string with the fraction in various formats
*
* @param {bigrat} a number to dump
* @returns {String} string various conversions
*/
bigrat.dump = function(r) {
//var t = bigrat.create();
return 'bigrat\t'+bigrat.str(r)
+ '\n~\t'+bigrat.toDecimal(r)
+ '\nCF:\t['+bigrat.toContinuedFraction(r)+']'
// + '\nSB:\t'+bigrat.traceSternBrocot(r)
//+ '\n'
//+ '\ntoBabylonian\t ~ '+bigrat.toBabylonian(r)
//+ '\ntoEgyptian\t = '+bigrat.toEgyptian(r) // can be very slow
//+ '\nsin:\t ~ '+bigrat.toDecimal(bigrat.sin(t, r))
//+ '\ncos:\t ~ '+bigrat.toDecimal(bigrat.cos(t, r))
//+ '\ntan:\t ~ '+bigrat.toDecimal(bigrat.tan(t, r))
+ '\n';
};
/**
* Zero, the additive identity
*
* @property ZERO
* @static
* @final
*/
bigrat.ZERO = bigrat.fromInteger(0);
/**
* One, the multiplicative identity
*
* @property ONE
* @static
* @final
*/
bigrat.ONE = bigrat.fromInteger(1);
/**
* Negative One, Zero minus One
*
* @property NEGONE
* @static
* @final
*/
bigrat.NEGONE = bigrat.fromInteger(-1);
/**
* Infinity, a non-Zero number divided by Zero
*
* @property INFINITY
* @static
* @final
*/
bigrat.INFINITY = bigrat.fromValues(1, 0);
/**
* Infinull, Zero divided by Zero
*
* @property INFINULL
* @static
* @final
*/
bigrat.INFINULL = bigrat.fromValues(0, 0);
/**
* Infinitesimal, the limit for approximations
*
* @property INFINITESIMAL
* @static
* @final
*/
bigrat.INFINITESIMAL = bigrat.clone([new BigInteger(1), BIGRAT_INFINITESIMAL_PRECISION]);
/**
* Pi, an approximation of the ratio between a circle's circumference and it's diameter
*
* @property PI
* @static
* @final
*/
bigrat.PI = bigrat.fromValues(
'3141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587',
'1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
);
if(typeof(exports) !== 'undefined') {
exports.bigrat = bigrat;
}