Source: rat.js

/*
 * 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 integer !== 'object') {
  var integer = require('../src/integer.js').integer;
}
*/

/**
 * The type of array to store the numerator and denominator in
 *
 * @property RAT_ARRAY_TYPE
 * @static
 * @final
 */
if(!RAT_ARRAY_TYPE) {

    // Int32 doesn't allow much precision
    //var RAT_ARRAY_TYPE = typeof Int32Array !== 'undefined' ? Int32Array : Array;

    // Float32Array seems to work best overall, more recently some browsers treat large numbers at Infinity
    //var RAT_ARRAY_TYPE = typeof Float64Array !== 'undefined' ? Float64Array : Array;

    // most compatible
    var RAT_ARRAY_TYPE = Array;

}

/**
 * The inverse of the allowable difference in approximations
 *
 * @property RAT_INFINITESIMAL_PRECISION
 * @static
 * @final
 */
if(!RAT_INFINITESIMAL_PRECISION) {
    var RAT_INFINITESIMAL_PRECISION = Math.pow(2, 56);
}

/**
 * Exit (possibly infinite) loops after this many iterations
 *
 * @property RAT_MAX_LOOPS
 * @static
 * @final
 */
if(!RAT_MAX_LOOPS) {
    var RAT_MAX_LOOPS = 1<<24;
}

/**
 * @class Rational Number
 * @name rat
 * @requires integer
 */
var rat = {};

/**
 * Machine Epsilon, floats within this distance of eachother are considered equal (to avoid rounding errors)
 *
 * @property EPSILON
 * @static
 * @final
 */
rat.EPSILON = 2e-16;

/**
 * Exit (possibly infinite) loops after this many iterations
 *
 * @property MAX_LOOPS
 * @static
 * @final
 */
rat.MAX_LOOPS = RAT_MAX_LOOPS;

/**
 * Creates a new, empty rat
 *
 * @returns {rat} a new rational number
 */
rat.create = function() {
    var out = new RAT_ARRAY_TYPE(2);
    out[0] = 0;
    out[1] = 1;
    return out;
};

/**
 * Creates a new rat initialized with values from an existing number
 *
 * @param {rat} a number to clone
 * @returns {rat} a new rational number
 */
rat.clone = function(a) {
    var out = new RAT_ARRAY_TYPE(2);
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Creates a new rat initialized with the given values
 *
 * @param {Number} n Numerator
 * @param {Number} d Denominator
 * @returns {rat} a new rational number
 */
rat.fromValues = function(n, d) {
    var out = new RAT_ARRAY_TYPE(2);
    out[0] = n;
    out[1] = d;
    return rat.normalize(out, out);
};

/**
 * Copy the values from one rat to another
 *
 * @param {rat} out the receiving number
 * @param {rat} a the source number
 * @returns {rat} out
 */
rat.copy = function(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Set the components of a rat to the given values
 *
 * @param {rat} out the receiving number
 * @param {Number} n Numerator
 * @param {Number} d Denominator
 * @returns {rat} out
 */
rat.set = function(out, n, d) {
    out[0] = n;
    out[1] = d;
    return rat.normalize(out, out);
};

/**
 * Absolute value of a rat
 *
 * @param {rat} out the receiving number
 * @param {rat} a number to take the absolute value of
 * @returns {rat} out
 */
rat.abs = function(out, a) {
    out[0] = Math.abs(a[0]);
    out[1] = a[1];
    return out;
};

/**
 * Inverts a rat
 *
 * @param {rat} out the receiving number
 * @param {rat} a number to invert
 * @returns {rat} out
 */
rat.invert = function(out, a) {
    var temp = a[0];
    out[0] = a[1];
    out[1] = temp;
    return out;
};

/**
 * Alias for {@link rat.invert}
 * @function
 */
rat.reciprocal = rat.invert;

/**
 * Adds two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.add = function(out, a, b) {
    if (a[1]===b[1]) {
        out[0] = a[0] + b[0];
        out[1] = a[1];
    }
    else {
        out[0] = a[0] * b[1] + b[0] * a[1];
        out[1] = a[1] * b[1];
    }
    return rat.normalize(out, out);
};

/**
 * Subtracts two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.subtract = function(out, a, b) {
    if (a[1]===b[1]) {
        out[0] = a[0] - b[0];
        out[1] = a[1];
    }
    else {
        out[0] = a[0] * b[1] - b[0] * a[1];
        out[1] = a[1] * b[1];
    }
    return rat.normalize(out, out);
};

/**
 * Alias for {@link rat.subtract}
 * @function
 */
rat.sub = rat.subtract;

/**
 * Multiplies two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.multiply = function(out, a, b) {
    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];
    return rat.normalize(out, out);
};

/**
 * Alias for {@link rat.multiply}
 * @function
 */
rat.mul = rat.multiply;

/**
 * Mediant of two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out the sum of the numerators divided by the sum of the denominators
 */
rat.mediant = function(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    return rat.normalize(out, out);
};

/**
 * Divides two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.divide = function(out, a, b) {
    out[0] = a[0] * b[1];
    out[1] = a[1] * b[0];
    return rat.normalize(out, out);
};

/**
 * Alias for {@link rat.divide}
 * @function
 */
rat.div = rat.divide;

/**
 * Returns true when the first rat is equal to the second
 *
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {Bool} true when the two rats are equal
 */
rat.equals = function(a, b) {
    if (a[0] === 0 && b[0] === 0) return true; // both are Zero
    if (a[1] === 0 && b[1] === 0) return true; // both are Infinity
    return a[0] === b[0] && a[1] === b[1];
};

/**
 * Returns true when the first rat is approximately equal to the second
 *
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {Bool} true when the difference between the two rats is less than rat.INFINITESIMAL
 */
rat.approximates = function(a, b) {
    if (rat.equals(a, b)) return true;
    var d = rat.create();
    rat.sub(d, a, b);
    rat.abs(d, d);
    return rat.isLessThan(d, rat.INFINITESIMAL);
};

/**
 * Returns true when the first rat is larger than the second
 *
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {Bool} true when the first operand is larger
 */
rat.isGreaterThan = function(a, b) {
    if (rat.equals(a, b)) return false;
    return a[0] * b[1] > b[0] * a[1];
};

/**
 * Returns true when the first rat is smaller than the second
 *
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {Bool} true when the first operand is smaller
 */
rat.isLessThan = function(a, b) {
    if (rat.equals(a, b)) return false;
    return a[0] * b[1] < b[0] * a[1];
};

/**
 * Returns true when the rat is negative
 *
 * @param {rat} a the number to check
 * @returns {Bool} true when the number is less than zero
 */
rat.isNegative = function(a) {
    return a[0] < 0;
};

/**
 * Returns the minimum of two rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.min = function(out, a, b) {
    if (rat.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 rats
 *
 * @param {rat} out the receiving number
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {rat} out
 */
rat.max = function(out, a, b) {
    if (rat.isGreaterThan(a, b)) {
        out[0] = a[0];
        out[1] = a[1];
    }
    else {
        out[0] = b[0];
        out[1] = b[1];
    }
    return out;
};

/**
 * Multiplies a rat's numerator by an integer
 *
 * @param {rat} out the receiving number
 * @param {rat} a the number to multiply
 * @param {Integer} b amount to multiply the number by
 * @returns {rat} out
 */
rat.scalar_multiply = function(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1];
    return rat.normalize(out, out);
};


/**
 * Multiplies a rat's denominator by an integer
 *
 * @param {rat} out the receiving number
 * @param {rat} a the number to divide
 * @param {Integer} b amount to divide by
 * @returns {rat} out
 */
rat.scalar_divide = function(out, a, b) {
    out[0] = a[0];
    out[1] = a[1] * b;
    return rat.normalize(out, out);
};

/**
 * Normalize a rat
 *
 * @param {rat} out the receiving number
 * @param {rat} a number to normalize
 * @returns {rat} out
 */
rat.normalize = function(out, a) {
    if (isNaN(a[0])||isNaN(a[1])||(a[0]===0&&a[1]===0)) {
        out[0] = 0;
        out[1] = 0;
        return out;
    }
    if (a[0]===0) {
        out[0] = 0;
        out[1] = 1;
        return out;
    }
    if (a[1]===0){
        out[0] = 1;
        out[1] = 0;
        return out;
    }
    if (a[0]===a[1]){
        out[0] = 1;
        out[1] = 1;
        return out;
    }
    if (a[1] > 0) {
        out[0] = a[0];
        out[1] = a[1];
    }
    else {
        out[0] = -a[0];
        out[1] = -a[1];
    }
    var gcd = integer.greatest_common_divisor(Math.abs(out[0]), out[1]);
    if (gcd > 1) {
        out[0] /= gcd;
        out[1] /= gcd;
    }
    return out;
};

/**
 * Negates a rat
 *
 * @param {rat} out the receiving number
 * @param {rat} a number to negate
 * @returns {rat} out
 */
rat.opposite = function(out, a) {
    out[0] = -a[0];
    out[1] = a[1];
    return out;
};

/**
 * Alias for {@link rat.opposite}
 * @function
 */
rat.negative = rat.opposite;

/**
 * Alias for {@link rat.opposite}
 * @function
 */
rat.neg = rat.opposite;

/**
 * Raises a rat to an integer exponent
 *
 * @param {rat} out the receiving number
 * @param {rat} a the number to exponentiate
 * @param {Integer} p power to raise the number by
 * @returns {rat} out
 */
rat.power = function(out, a, p) {
    if (p===2) {
        out[0] = a[0] * a[0];
        out[1] = a[1] * a[1];
    }
    else if (p>0) {
        out[0] = Math.pow(a[0], p);
        out[1] = Math.pow(a[1], p);
    }
    else if (p<0) {
        p = Math.abs(p);
        out[0] = Math.pow(a[1], p);
        out[1] = Math.pow(a[0], p);
    }
    else {
        rat.copy(out, rat.ONE);
    }
    return rat.normalize(out, out);
};

/**
 * Alias for {@link rat.power}
 * @function
 */
rat.pow = rat.power;

/**
 * Find a rational number which approximates the input number when multiplied by itself
 *
 * @param {rat} out the receiving number
 * @param {rat} a the number to find the root of
 * @returns {rat} out
 */
rat.sqrt = function (out, a) {
    return rat.nthRoot(out, a, 2);
};

/**
 * Find a rat approximation which equals the input rat 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 {rat} out the receiving number
 * @param {rat} a the number to find the root of
 * @param {Integer} n
 * @returns {rat} out
 */
rat.nthRoot = function (out, a, n) {
    if (rat.equals(a, rat.ZERO)) return rat.copy(out, rat.ZERO);
    if (rat.equals(a, rat.ONE)) return rat.copy(out, rat.ONE);
    if (rat.equals(a, rat.INFINITY)) return rat.copy(out, rat.INFINITY);
    if (rat.equals(a, rat.INFINULL)) return rat.copy(out, rat.INFINULL);

    var neg = rat.isNegative(a);
    if (neg) a[0] = -a[0];

    out = rat.copy(out, rat.ONE);
    var m = [1, 0, 0, 1];
    var test = rat.clone(rat.ONE);

    var c = rat.MAX_LOOPS;
    while ( !rat.approximates(a, test) && c-- ) {
        if (rat.isLessThan(a, test)) {
            m[0] += m[1];
            m[2] += m[3];
        }
        else {
            m[1] += m[0];
            m[3] += m[2];
        }
        out[0] = m[0] + m[1];
        out[1] = m[2] + m[3];
        rat.pow(test, out, n);
    }

    if (neg) {
        a[0] = -a[0];
        if (n%2===1) rat.neg(out, out);
    }

    return out;
};

/**
 * Calculates the dot product of two rats
 *
 * @param {rat} a the first operand
 * @param {rat} b the second operand
 * @returns {Integer} dot product of a and b
 */
rat.dot = function (a, b) {
    return a[0] * b[0] + a[1] * b[1];
};

/**
 * Returns a string representation
 *
 * @param {rat} a number to represent as a string
 * @returns {String} string representation of the number
 */
rat.str = function (a) {
    return a[1] === 1 ? a[0].toString() : a[0] + '/' + a[1];
};

/**
 * Returns a decimal approximation
 *
 * @param {rat} a number to approximate as a decimal
 * @returns {Float} decimal approximation of the number
 */
rat.toDecimal = function (a) {
    return a[0] / a[1];
};

/**
 * Alias for {@link rat.toDecimal}
 * @function
 */
rat.dec = rat.toDecimal;

/**
 * Returns the closest integer approximation
 *
 * @param {rat} a number to round to the nearest integer
 * @returns {Integer} integer approximation of the number
 */
rat.toInteger = function (a) {
    return Math.round(rat.toDecimal(a));
};

/**
 * Alias for {@link rat.toInteger}
 * @function
 */
rat.round = rat.toInteger;

/**
 * Returns the closest integer approximation by rounding down
 *
 * @param {rat} a number to round down to the nearest integer
 * @returns {Integer} integer approximation of the number
 */
rat.floor = function (a) {
    return Math.floor(rat.toDecimal(a));
};

/**
 * Returns the closest integer approximation by rounding up
 *
 * @param {rat} a number to round up to the nearest integer
 * @returns {Integer} integer approximation of the number
 */
rat.ceil = function (a) {
    return Math.ceil(rat.toDecimal(a));
};

/**
 * Returns a rat from an integer, copying to an existing rat
 *
 * @param {rat} out the receiving number
 * @param {Integer} signed integer
 * @returns {rat} out
 */
rat.fromInteger_copy = function (out, a) {
    out[0] = parseInt(a);
    out[1] = 1;
    return out;
};

/**
 * Returns a rat from an integer, creating a new rat
 *
 * @param {Integer} signed integer
 * @returns {rat} out
 */
rat.fromInteger = function (a) {
    return rat.fromInteger_copy(rat.create(), a);
};

/**
 * Returns a rat from the inverse of an integer, copying to an existing rat
 *
 * @param {rat} out the receiving number
 * @param {Integer} signed integer
 * @returns {rat} out
 */
rat.fromIntegerInverse_copy = function (out, a) {
    out[0] = 1;
    out[1] = parseInt(a);
    if (out[1]<0) {
        out[0] = -out[0];
        out[1] = -out[1];
    }
    return out;
};

/**
 * Returns a rat from the inverse of an integer, creating a new rat
 *
 * @param {Integer} signed integer
 * @returns {rat} out
 */
rat.fromIntegerInverse = function (a) {
    return rat.fromIntegerInverse_copy(rat.create(), a);
};

/**
 * Returns a rat from a decimal number, creating a new rat
 *
 * @param {Number} a decimal number
 * @returns {rat} out
 */
rat.fromDecimal = function (a) {
    return rat.fromDecimal_copy(rat.create(), a);
};

/**
 * Returns a rat from a decimal number, copying to an existing rat
 *
 * @param {rat} out the receiving number
 * @param {Number} a decimal number
 * @returns {rat} out
 */
rat.fromDecimal_copy = function (out, a) {
    a = parseFloat(a);
    if (isNaN(a)) return rat.copy(out, rat.INFINULL);
    if (a===Infinity) return rat.copy(out, rat.INFINITY);
    if (Math.abs(a) < rat.EPSILON) return rat.copy(out, rat.ZERO);
    if (Math.abs(a-1) < rat.EPSILON) return rat.copy(out, rat.ONE);
    if (Math.abs(a%1) < rat.EPSILON) return rat.fromInteger_copy(out, a);
    if (Math.abs((1/a)%1) < rat.EPSILON) return rat.fromIntegerInverse_copy(out, Math.round(1/a));
    rat.copy(out, rat.ONE);
    var
        m = [
            1,
            0,
            0,
            1
        ],
        test = a,
        integer_part = 1,
        c = rat.MAX_LOOPS;
    //while (c-- && Math.abs(out[0] - a*out[1]) > 2e-8) {
    while (c-- && Math.abs(a - rat.toDecimal(out)) > rat.EPSILON) {
        integer_part = Math.floor(test);
        out[0] = integer_part * m[0] + m[2];
        out[1] = integer_part * m[1] + m[3];
        if (test === integer_part) break;
        test = 1 / (test - integer_part);
        m[2] = m[0];
        m[3] = m[1];
        m[0] = out[0];
        m[1] = out[1];
    }
    return out;
};

/**
 * Creates a new rat from two random integers
 *
 * @param {rat} out the receiving number
 * @returns {rat} a random rational number
 */
rat.fromRandom = function(out) {
    out[0] = Math.random()*0xFFFFFFFFFFFFF<<0;
    out[1] = Math.abs(Math.random()*0xFFFFFFFFFFFFF<<0);
    return rat.normalize(out, out);
};

/**
 * Parametric sine: 2a / (1 + a²)
 *
 * @param {rat} out the receiving number
 * @param {rat} a number for which to calculate the parametric sine
 * @returns {rat} out
 */
rat.sin = function(out, a) {
    if (a[1] === 0) return rat.copy(out, rat.ZERO);
    rat.scalar_multiply(out, a, 2);
    var d = rat.create();
    rat.pow(d, a, 2);
    rat.add(d, d, rat.ONE);
    rat.divide(out, out, d);
    return out;
};

/**
 * Parametric cosine: (1 - a²) / (1 + a²)
 *
 * @param {rat} out the receiving number
 * @param {rat} a number for which to calculate the parametric cosine
 * @returns {rat} out
 */
rat.cos = function(out, a) {
    if (a[1] === 0) return rat.neg(out, rat.ONE);
    var a2 = rat.create();
    rat.pow(a2, a, 2);
    rat.sub(out, rat.ONE, a2);
    var d = rat.create();
    rat.add(d, rat.ONE, a2);
    rat.divide(out, out, d);
    return out;
};

/**
 * Parametric tangent: sin(a) / cos(a)
 *
 * @param {rat} out the receiving number
 * @param {rat} a number for which to calculate the parametric tangent
 * @returns {rat} out
 */
rat.tan = function(out, a) {
    rat.scalar_multiply(out, a, 2);
    var t = rat.create();
    rat.pow(t, a, 2);
    rat.scalar_multiply(t, t, 2);
    rat.add(out, out, t);
    rat.pow(t, a, 4);
    rat.sub(t, rat.ONE, t);
    rat.divide(out, out, t);
    return out;
};

/**
 * Returns an Egyptian representation
 * The returned string can be evaluated in "calc - arbitrary precision calculator"
 *
 * @param {rat} 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
 */
rat.toEgyptian = function (a) {
    var t = rat.clone(a);
    rat.abs(t, t);
    var b = rat.floor(t);
    if (b) rat.sub(t, t, rat.fromInteger(b));
    if (!t[0]) return b.toString();
    if (!b) b = '';
    var d = 1;
    var f = rat.create();
    while (t[0] !== 1) {
        d++;
        f = rat.fromValues(1, d);
        if (rat.isGreaterThan(t, f)) {
            if (b) b += ' + ';
            b += rat.str(f);
            rat.sub(t, t, f);
        }
    }
    if (!t) {
        if (!b) return '0';
        return b;
    }
    return b ? b + ' + ' + rat.str(t) : rat.str(t);
};

/**
 * Returns a Babylonian representation (base 60)
 * The returned string can be evaluated in "calc - arbitrary precision calculator"
 *
 * @param {rat} 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
 */
rat.toBabylonian = function (a) {
    var s = '';
     // there must be a better way to do this, avoiding conversion to decimal
    var t = rat.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 {rat} a number to trace in the Stern Brocot tree
 * @returns {String} Stern Brocot path
 */
rat.traceSternBrocot = function (a) {
    var path = '';
    if (
        rat.equals(a, rat.ZERO)
    ||
        rat.equals(a, rat.ONE)
    ||
        rat.equals(a, rat.INFINITY)
    ||
        rat.equals(a, rat.INFINULL)
        ) return path;

    var test = rat.clone(a);
    var neg = rat.isNegative(test);
    if (neg) test[0] = -test[0];

    var r = rat.clone(rat.ONE);
    var m = [1, 0, 0, 1];

    var r_streak = 0;
    var l_streak = 0;

    var c = rat.MAX_LOOPS;
    while ( !rat.approximates(test, r) && c-- ) {
        if (rat.isLessThan(test, r)) {
            m[0] += m[1];
            m[2] += m[3];
            l_streak++;
            if (r_streak) {
                path += 'R';
                if (r_streak!==1) path += r_streak;
                r_streak = 0;
                path += ' ';
            }
        }
        else {
            m[1] += m[0];
            m[3] += m[2];
            r_streak++;
            if (l_streak) {
                path += 'L';
                if (l_streak!==1) path += l_streak;
                l_streak = 0;
                path += ' ';
            }
        }
        r[0] = m[0] + m[1];
        r[1] = m[2] + 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 {rat} a number to convert to a continued fraction
 * @param {Integer} maximum number of iterations
 * @returns {Array} integers of the continued fraction
 */
rat.toContinuedFraction = function (a, loop_limit) {
    loop_limit = typeof loop_limit==='undefined' ? 65536 : parseInt(loop_limit);
    if (rat.equals(a, rat.ZERO)) return [0];
    if (rat.equals(a, rat.ONE)) return [1];
    if (rat.equals(a, rat.NEGONE)) return [-1];
    if (rat.equals(a, rat.INFINITY)) return [1, 0];
    if (rat.equals(a, rat.INFINULL)) return [0, 0];

    var test = rat.clone(a);
    var neg = rat.isNegative(test);
    if (neg) test[0] = -test[0];

    var r = rat.clone(rat.ONE);

    var m = [1,0,0,1];

    var direction = 1;
    var result = [0];
    var result_last = result.length - 1;

    while ( !rat.equals(test, r) && loop_limit-- ) {
        if (rat.isLessThan(test, r)) {
            if (direction===-1) {
                result[result_last]++;
            }
            else {
                direction = -1;
                result.push(1);
                result_last++;
            }
            m[0] += m[1];
            m[2] += m[3];
        }
        else {
            if (direction===1) {
                result[result_last]++;
            }
            else {
                direction = 1;
                result.push(1);
                result_last++;
            }
            m[1] += m[0];
            m[3] += m[2];
        }
        r[0] = m[0] + m[1];
        r[1] = m[2] + 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 rat from an array of integers representing a continued fraction
 *
 * @param {rat} out the receiving number
 * @param {Array} integers of the continued fraction
 * @returns {rat} out
 */
rat.fromContinuedFraction = function(out, cf) {
    rat.fromInteger_copy(out, cf[cf.length-1]);
    for (var i=cf.length-2;i>-1;i--) {
        rat.invert(out, out);
        rat.add(out, rat.fromInteger(cf[i]), out);
    }
    return out;
};

/**
 * Returns a string with the fraction in various formats
 *
 * @param {rat} a number to dump
 * @returns {String} string various conversions
 */
rat.dump = function(r) {
    var t = rat.create();
    return 'rat\t'+rat.str(r)
    + '\n~\t'+rat.toDecimal(r)
    + '\nCF:\t['+rat.toContinuedFraction(r)+']'
    //+ '\nSB:\t'+rat.traceSternBrocot(r)
    //+ '\n'
    //+ '\ntoBabylonian\t ~ '+rat.toBabylonian(r)
    //+ '\ntoEgyptian\t = '+rat.toEgyptian(r)  // can be very slow
    //+ '\nsin:\t ~ '+rat.toDecimal(rat.sin(t, r))
    //+ '\ncos:\t ~ '+rat.toDecimal(rat.cos(t, r))
    //+ '\ntan:\t ~ '+rat.toDecimal(rat.tan(t, r))
    + '\n';
};

/**
 * Zero, the additive identity
 *
 * @property ZERO
 * @type rat
 * @static
 * @final
 */
rat.ZERO = rat.fromInteger(0);

/**
 * One, the multiplicative identity
 *
 * @property ONE
 * @type rat
 * @static
 * @final
 */
rat.ONE = rat.fromInteger(1);

/**
 * Negative One, Zero minus One
 *
 * @property NEGONE
 * @type rat
 * @static
 * @final
 */
rat.NEGONE = rat.fromInteger(-1);

/**
 * Infinity, a non-Zero number divided by Zero
 *
 * @property INFINITY
 * @type rat
 * @static
 * @final
 */
rat.INFINITY = rat.fromValues(1, 0);

/**
 * Infinull, Zero divided by Zero
 *
 * @property INFINULL
 * @type rat
 * @static
 * @final
 */
rat.INFINULL = rat.fromValues(0, 0);

/**
 * Infinitesimal, the limit for approximations
 *
 * @property INFINITESIMAL
 * @type rat
 * @static
 * @final
 */
rat.INFINITESIMAL = rat.clone([1, RAT_INFINITESIMAL_PRECISION]);

/**
 * Pi, an approximation of the ratio between a circle's circumference and it's diameter
 *
 * @property PI
 * @type rat
 * @static
 * @final
 */
rat.PI = rat.fromValues(
    1320192667429,
    420230377710
);

if(typeof(exports) !== 'undefined') {
    exports.rat = rat;
}