diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/calculator-backends.js b/calculator-backends.js
new file mode 100644
index 0000000..86395f4
--- /dev/null
+++ b/calculator-backends.js
@@ -0,0 +1,393 @@
+// # calculator-backends.js
+//
+// more things to do with a simple calculator parser
+//
+// [calculator-parser.js](calculator-parser.html) contains a simple parser.
+// It contains enough code that you can actually do some basic math with it.
+// But what else can you do with a parser?
+//
+// This file contains seven different applications of
+// the calculator parser.
+//
+// [Try them out.](../calculator.html)
+
+// ## Interpreters
+
+// ### 1. Evaluate using floating-point numbers
+
+// This behaves like a stripped-down version of JavaScript `eval()`.
+function evaluateAsFloat(code) {
+ var calculator = {
+ number: function (s) { return parseInt(s); },
+ add: function (a, b) { return a + b; },
+ sub: function (a, b) { return a - b; },
+ mul: function (a, b) { return a * b; },
+ div: function (a, b) { return a / b; },
+ _variables: Object.create(null),
+ name: function (name) { return this._variables[name] || 0; }
+ };
+
+ calculator._variables.e = Math.E;
+ calculator._variables.pi = Math.PI;
+
+ return parse(code, calculator);
+}
+
+
+// ### 2. Evaluate using precise fraction arithmetic
+//
+// Our little language is a tiny subset of JavaScript. But that doesn’t meant
+// it has to behave exactly like JavaScript. This is our language.
+// It can behave however we want.
+
+// So how about a calculator that does arbitrary precision arithmetic?
+// Let’s start by defining a `Fraction` class...
+
+var BigInteger = require('biginteger').BigInteger;
+
+function gcd(a, b) {
+ while (!b.isZero()) {
+ var tmp = a;
+ a = b;
+ b = tmp.remainder(b);
+ }
+ return a;
+}
+
+function Fraction(n, d) {
+ if (d === undefined)
+ d = new BigInteger(1);
+ var x = gcd(n, d);
+ this.n = n.divide(x);
+ this.d = d.divide(x);
+}
+
+// …and some Fraction methods. You learned these techniques in grade school,
+// though you may have forgotten some of them.
+Fraction.prototype = {
+ add: function (x) {
+ return new Fraction(this.n.multiply(x.d).add(x.n.multiply(this.d)),
+ this.d.multiply(x.d));
+ },
+ negate: function (x) {
+ return new Fraction(this.n.negate(), this.d);
+ },
+ sub: function (x) {
+ return this.add(x.negate());
+ },
+ mul: function (x) {
+ return new Fraction(this.n.multiply(x.n), this.d.multiply(x.d));
+ },
+ div: function (x) {
+ return new Fraction(this.n.multiply(x.d), this.d.multiply(x.n));
+ },
+ toString: function () {
+ var ns = this.n.toString(), ds = this.d.toString();
+ if (ds === "1")
+ return ns;
+ else
+ return ns + "/" + ds;
+ }
+};
+
+// Now simply write an `out` object that computes the results using `Fraction`
+// objects rather than JavaScript numbers. It’s almost too easy.
+function evaluateAsFraction(code) {
+ var fractionCalculator = {
+ number: function (s) { return new Fraction(new BigInteger(s)); },
+ add: function (a, b) { return a.add(b); },
+ sub: function (a, b) { return a.sub(b); },
+ mul: function (a, b) { return a.mul(b); },
+ div: function (a, b) { return a.div(b); },
+ name: function (name) { throw new SyntaxError("no variables in fraction mode, sorry"); }
+ };
+ return parse(code, fractionCalculator);
+}
+
+// Our tiny programming language is suddenly doing something JavaScript itself
+// doesn’t do: arithmetic with exact (not floating-point) results. Tests:
+assert.strictEqual(evaluateAsFraction("1 / 3").toString(), "1/3");
+assert.strictEqual(evaluateAsFraction("(2/3) * (3/2)").toString(), "1");
+assert.strictEqual(evaluateAsFraction("1/7 + 4/7 + 2/7").toString(), "1");
+assert.strictEqual(
+ evaluateAsFraction("5996788328646786302319492/2288327879043508396784319").toString(),
+ "324298349324/123749732893");
+
+
+// ## Code as data
+
+// ### 3. Convert to DOM
+//
+// Both examples above compute a result. But that isn’t the only thing you can
+// do with language. Let’s make a program that doesn’t compute anything at all:
+// it simply spits out DOM nodes that show how the program would look in
+// Scratch. (!)
+
+// Helper function to create DOM elements.
+function span(className, contents) {
+ var e = document.createElement("span");
+ e.className = className;
+ for (var i = 0; i < contents.length; i++) {
+ var kid = contents[i];
+ if (typeof kid === "string")
+ kid = document.createTextNode(kid);
+ e.appendChild(kid);
+ }
+ return e;
+}
+
+// Yet another pluggable `out` object.
+function convertToDOM(code) {
+ var spanBuilder = {
+ number: function (s) { return span("num", [s]); },
+ add: function (a, b) { return span("expr", [a, "+", b]); },
+ sub: function (a, b) { return span("expr", [a, "\u2212", b]); }, // −
+ mul: function (a, b) { return span("expr", [a, "\u00d7", b]); }, // ×
+ div: function (a, b) { return span("expr", [a, "\u00f7", b]); }, // ÷
+ name: function (name) { return span("var", [name]); }
+ };
+ return parse(code, spanBuilder);
+}
+
+
+// ### 4. Convert to JSON
+//
+// Let’s make one that builds a tree describing the input formula. This is
+// called an abstract syntax tree, or AST. **This is most likely what you would
+// do if you planned to make your own programming language.** It was once
+// common to parse and emit code in a single pass. Languages were carefully
+// designed to make sure that was possible. Today, there’s really no reason not
+// to build a complete AST or other intermediate form, then emit code in a
+// second pass.
+
+// Each method simply returns a new JS object.
+function convertToAST(code) {
+ var astBuilder = {
+ number: function (s) { return {type: "number", value: s}; },
+ add: function (a, b) { return {type: "add", left: a, right: b}; },
+ sub: function (a, b) { return {type: "sub", left: a, right: b}; },
+ mul: function (a, b) { return {type: "mul", left: a, right: b}; },
+ div: function (a, b) { return {type: "div", left: a, right: b}; },
+ name: function (name) { return {type: "name", name: name}; }
+ };
+ return parse(code, astBuilder);
+}
+
+// And test it.
+assert.deepEqual(
+ convertToAST("(1 + 2) / 3"),
+ {
+ type: "div",
+ left: {
+ type: "add",
+ left: {type: "number", value: "1"},
+ right: {type: "number", value: "2"}
+ },
+ right: {type: "number", value: "3"}
+ });
+
+
+// ### 5. MathML output
+//
+// One more riff on this theme: How about generating beautiful MathML output?
+// (Unfortunately, some browsers still do not support MathML. Firefox does.)
+
+// The hardest part of this was figuring out how to make MathML elements.
+// Here’s some code to help with that.
+var mathml = "http://www.w3.org/1998/Math/MathML";
+
+function mo(s) {
+ var e = document.createElementNS(mathml, "mo");
+ var t = document.createTextNode(s);
+ e.appendChild(t);
+ return {prec: 3, element: e};
+}
+
+// Create a new MathML DOM element of the specified type and contents.
+// `precedence` is used to determine whether or not to add parentheses around
+// any of the contents. If it’s `null`, no parentheses are added.
+function make(name, precedence, contents) {
+ var e = document.createElementNS(mathml, name);
+ for (var i = 0; i < contents.length; i++) {
+ var kid = contents[i];
+ var node;
+
+ if (typeof kid === "string") {
+ node = document.createTextNode(kid);
+ } else {
+ // If precedence is non-null and higher than this child’s
+ // precedence, wrap the child in parentheses.
+ if (precedence !== null
+ && (kid.prec < precedence
+ || (kid.prec == precedence && i != 0)))
+ {
+ kid = make("mrow", null, [mo("("), kid, mo(")")]);
+ }
+ node = kid.element;
+ }
+ e.appendChild(node);
+ }
+ if (precedence === null)
+ precedence = 3;
+ return {prec: precedence, element: e};
+}
+
+function convertToMathML(code) {
+ var mathmlBuilder = {
+ number: function (s) { return make("mn", 3, [s]); },
+ add: function (a, b) { return make("mrow", 1, [a, make("mo", 3, ["+"]), b]); },
+ sub: function (a, b) { return make("mrow", 1, [a, make("mo", 3, ["-"]), b]); },
+ mul: function (a, b) { return make("mrow", 2, [a, b]); },
+ div: function (a, b) { return make("mfrac", null, [a, b]); },
+ name: function (name) { return make("mi", 3, [name]); }
+ };
+ var e = parse(code, mathmlBuilder);
+ return make("math", null, [e]);
+}
+
+
+// ## Compilers
+
+// ### 6. JavaScript function output
+
+// This is just to show some very basic code generation.
+//
+// Code generation for a real compiler will be harder, because the target
+// language is typically quite a bit different from the source language. Here
+// they are virtually identical, so code generation is very easy.
+//
+function compileToJSFunction(code) {
+ var jsFunctionBuilder = {
+ number: function (s) { return s; },
+ add: function (a, b) { return "(" + a + " + " + b + ")"; },
+ sub: function (a, b) { return "(" + a + " - " + b + ")"; },
+ mul: function (a, b) { return "(" + a + " * " + b + ")"; },
+ div: function (a, b) { return "(" + a + " / " + b + ")"; },
+ name: function (name) {
+ // Only allow the name "x".
+ if (name !== "x")
+ throw SyntaxError("only the name 'x' is allowed");
+ return name;
+ }
+ };
+
+ var code = parse(code, jsFunctionBuilder);
+ return Function("x", "return " + code + ";");
+}
+
+assert.strictEqual(compileToJSFunction("x*x - 2*x + 1")(1), 0);
+assert.strictEqual(compileToJSFunction("x*x - 2*x + 1")(2), 1);
+assert.strictEqual(compileToJSFunction("x*x - 2*x + 1")(3), 4);
+assert.strictEqual(compileToJSFunction("x*x - 2*x + 1")(4), 9);
+
+
+// ### 7. Complex function output
+
+// This one returns a JS function that operates on complex numbers.
+//
+// TODO - explain what is going on here.
+//
+function compileToComplexFunction(code) {
+ var nextTmpId = 0;
+
+ function genName() {
+ return "tmp" + nextTmpId++;
+ }
+
+ var complexFunctionBuilder = {
+ number: function (s) {
+ return { setup: "", re: s, im: "0" };
+ },
+ add: function (a, b) {
+ return {
+ setup: a.setup + b.setup,
+ re: a.re + " + " + b.re,
+ im: a.im + " + " + b.im
+ };
+ },
+ sub: function (a, b) {
+ return {
+ setup: a.setup + b.setup,
+ re: a.re + " - " + b.re,
+ im: a.im + " - " + b.im
+ };
+ },
+ mul: function (a, b) {
+ // This requires some setup. First write some code to store the
+ // real and imaginary parts of a and b in temporary variables.
+ // We have to store them in temporary variables because the formula
+ // for complex multiplication uses each component twice, and we
+ // don’t want to compute them twice.
+ var atmp = genName(),
+ btmp = genName();
+ var setup = a.setup + b.setup +
+ ("var A_re = " + a.re + ", A_im = " + a.im + ";\n").replace(/A/g, atmp) +
+ ("var B_re = " + b.re + ", B_im = " + b.im + ";\n").replace(/B/g, btmp);
+
+ // Now return the setup, along with expressions for computing the
+ // real and imaginary parts of (a * b).
+ return {
+ setup: setup,
+ re: "A_re * B_re - A_im * B_im".replace(/A/g, atmp).replace(/B/g, btmp),
+ im: "A_re * B_im + A_im * B_re".replace(/A/g, atmp).replace(/B/g, btmp)
+ };
+ },
+ div: function (a, b) {
+ // Just as for multiplication, first write some code to store the real
+ // and imaginary parts of a and b in temporary variables.
+ var atmp = genName(),
+ btmp = genName(),
+ tmp = genName();
+ var setup = a.setup + b.setup +
+ ("var A_re = " + a.re + ", A_im = " + a.im + ";\n").replace(/A/g, atmp) +
+ ("var B_re = " + b.re + ", B_im = " + b.im + ";\n").replace(/B/g, btmp) +
+ ("var T = B_re * B_re + B_im * B_im;\n").replace(/T/g, tmp).replace(/B/g, btmp);
+ return {
+ setup: setup,
+ re: "(A_re * B_re + A_im * B_im) / T".replace(/A/g, atmp).replace(/B/g, btmp).replace(/T/g, tmp),
+ im: "(A_im * B_re - A_re * B_im) / T".replace(/A/g, atmp).replace(/B/g, btmp).replace(/T/g, tmp)
+ };
+ },
+ name: function (name) {
+ if (name === "i")
+ return {setup: "", re: "0", im: "1"};
+ if (name !== "z")
+ throw SyntaxError("undefined variable: " + name);
+ return {
+ setup: "",
+ re: name + "_re",
+ im: name + "_im"
+ };
+ }
+ };
+
+ var result = parse(code, complexFunctionBuilder);
+ var tmp = genName();
+ var code =
+ result.setup +
+ "return {re: " + result.re + ", im: " + result.im + "};\n";
+ return Function("z_re, z_im", code);
+
+ /*
+ I had planned to have this generate asm.js code for extra speed, but it's
+ so fast already that unless I can think of a more computationally
+ intensive task, there is no need.
+ result.setup +
+ "var " + tmp + " = " + result.re + ";\n" +
+ "z_im = " + result.im + ";\n" +
+ "z_re = " + tmp + ";\n" +
+ */
+
+}
+
+// The last bit of code here simply stores all seven back ends in one place
+// where other code can get to them.
+var parseModes = {
+ calc: evaluateAsFloat,
+ fraction: evaluateAsFraction,
+ blocks: convertToDOM,
+ json: convertToAST,
+ mathml: convertToMathML,
+ graph: compileToJSFunction,
+ complex: compileToComplexFunction
+};
diff --git a/calculator-parser.js b/calculator-parser.js
new file mode 100644
index 0000000..4b2e926
--- /dev/null
+++ b/calculator-parser.js
@@ -0,0 +1,206 @@
+// # calculator-parser.js
+//
+// a simple calculator language, in three acts
+
+// This program parses a very simple language that just does a little basic
+// arithmetic. Here are some simple examples of the sort of thing you can
+// write in the calculator language:
+//
+// * `2 + 2`
+// * `1 * 2 + 3 * 4 + 5 / 6`
+// * `3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + 1/(1 + 1/(1 + 1/1))))))`
+// * `1 / ((z + 1) * (z - 1))`
+//
+// If you’d like to try it out, open [calculator.html](../calculator.html).
+
+
+// ## Act One – Breaking code down into tokens
+
+// This function, `tokenize(code)`, takes a string `code` and splits it into
+// *tokens*, the numbers, words, and symbols that make up our little calculator
+// mini-language.
+function tokenize(code) {
+ var results = [];
+ var tokenRegExp = /\s*([A-Za-z]+|[0-9]+|\S)\s*/g;
+
+ var m;
+ while ((m = tokenRegExp.exec(code)) !== null)
+ results.push(m[1]);
+ return results;
+}
+
+// Let’s test as we go!
+var assert = require('assert');
+assert.deepEqual(tokenize("123\n"), ["123"]);
+assert.deepEqual(tokenize("2+2"), ["2", "+", "2"]);
+assert.deepEqual(tokenize("+-*/"), ["+", "-", "*", "/"]);
+assert.deepEqual(tokenize(" 1 * 24 +\n\n pi"), ["1", "*", "24", "+", "pi"]);
+assert.deepEqual(tokenize("()"), ["(", ")"]);
+assert.deepEqual(tokenize(" "), []);
+
+
+
+// Here are a few helper functions for working with tokens. To keep things
+// simple, a number is any sequence of digits.
+function isNumber(token) {
+ return token !== undefined && token.match(/^[0-9]+$/) !== null;
+}
+
+// And a *name*, or identifier, is any sequence of letters.
+function isName(token) {
+ return token !== undefined && token.match(/^[A-Za-z]+$/) !== null;
+}
+
+// Tests.
+assert(isNumber("123"));
+assert(!isNumber("x"));
+assert(!isNumber("-"));
+assert(isName("xyz"));
+assert(!isName("+"));
+
+
+// ## Act Three – Parser output
+
+// The parser’s only job is to *decode* the input.
+//
+// *Executing* a program is this object’s job. I’m putting this
+// right up front so you can see what the language can actually do,
+// before reading on.
+//
+// The parser will call these six methods as it decodes each
+// piece of the input code.
+//
+var calculator = {
+ number: function (s) { return parseInt(s); },
+ add: function (a, b) { return a + b; },
+ sub: function (a, b) { return a - b; },
+ mul: function (a, b) { return a * b; },
+ div: function (a, b) { return a / b; },
+ _variables: Object.create(null),
+ name: function (name) { return this._variables[name] || 0; }
+};
+
+calculator._variables.e = Math.E;
+calculator._variables.pi = Math.PI;
+
+
+
+// ## Act Two – The parser
+
+// Parse the given string `code` as an expression in our little language.
+//
+function parse(code, out) {
+ // Break the input into tokens.
+ var tokens = tokenize(code);
+
+ // The parser will do a single left-to-right pass over `tokens`, with no
+ // backtracking. `position` is the index of the next token. Start at
+ // 0. We’ll increment this as we go.
+ var position = 0;
+
+ // `peek()` returns the next token without advancing `position`.
+ function peek() {
+ return tokens[position];
+ }
+
+ // `consume(token)` consumes one token, moving `position` to point to the next one.
+ function consume(token) {
+ assert.strictEqual(token, tokens[position]);
+ position++;
+ }
+
+ // Now we have the functions that are actually responsible for parsing.
+ // This is the cool part. Each group of syntax rules is translated to one
+ // function.
+
+ // Parse a *PrimaryExpr*—that is, tokens matching one of the three syntax
+ // rules below. Whatever kind of expression we find, we return the result
+ // of some `out.something()` method.
+ //
+ //
+ //
*PrimaryExpr* **:**
+ //
+ //
*Number*
+ //
*Name*
+ //
(
*Expr* )
+ //
+ //
+ function parsePrimaryExpr() {
+ var t = peek();
+
+ if (isNumber(t)) {
+ consume(t);
+ return out.number(t);
+ } else if (isName(t)) {
+ consume(t);
+ return out.name(t);
+ } else if (t === "(") {
+ consume(t);
+ var expr = parseExpr();
+ if (peek() !== ")")
+ throw new SyntaxError("expected )");
+ consume(")");
+ return expr;
+ } else {
+ // If we get here, the next token doesn’t match any of the three
+ // rules. So it’s an error.
+ throw new SyntaxError("expected a number, a variable, or parentheses");
+ }
+ }
+
+ //
+ // *MulExpr* **:**
+ //
+ //
*PrimaryExpr* ( \*
*PrimaryExpr* | /
*PrimaryExpr* )\*
+ //
+ //
+ function parseMulExpr() {
+ var expr = parsePrimaryExpr();
+ var t = peek();
+ while (t === "*" || t === "/") {
+ consume(t);
+ var rhs = parsePrimaryExpr();
+ if (t === "*")
+ expr = out.mul(expr, rhs);
+ else
+ expr = out.div(expr, rhs);
+ t = peek();
+ }
+ return expr;
+ }
+
+ //
+ // *Expr* **:**
+ //
+ //
*MulExpr* ( +
*MulExpr* | -
*MulExpr* )\*
+ //
+ //
+ function parseExpr() {
+ var expr = parseMulExpr();
+ var t = peek();
+ while (t === "+" || t === "-") {
+ consume(t);
+ var rhs = parseMulExpr();
+ if (t === "+")
+ expr = out.add(expr, rhs);
+ else
+ expr = out.sub(expr, rhs);
+ t = peek();
+ }
+ return expr;
+ }
+
+ // Now all that remains, really, is to call `parseExpr()` to parse an *Expr*.
+ var result = parseExpr();
+
+ // Well, one more thing. Make sure `parseExpr()` consumed *all* the
+ // input. If it didn’t, that means the next token didn’t match any syntax
+ // rule, which is an error.
+ if (position !== tokens.length)
+ throw new SyntaxError("unexpected '" + peek() + "'");
+ return result;
+}
+
+assert.strictEqual(parse("2 + 2", calculator), 4);
+assert.strictEqual(parse("3 * 4 * 5", calculator), 60);
+assert.strictEqual(parse("5 * (2 + 2)", calculator), 20);
diff --git a/calculator.html b/calculator.html
new file mode 100644
index 0000000..8335e87
--- /dev/null
+++ b/calculator.html
@@ -0,0 +1,78 @@
+
+
+
+
+ Compilers 101 - A toy calculator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third-party-scripts/Chart.js b/third-party-scripts/Chart.js
new file mode 100644
index 0000000..7024c94
--- /dev/null
+++ b/third-party-scripts/Chart.js
@@ -0,0 +1,1426 @@
+//Define the global Chart Variable as a class.
+var Chart = function(context){
+
+ var chart = this;
+
+
+ //Easing functions adapted from Robert Penner's easing equations
+ //http://www.robertpenner.com/easing/
+
+ var animationOptions = {
+ linear : function (t){
+ return t;
+ },
+ easeInQuad: function (t) {
+ return t*t;
+ },
+ easeOutQuad: function (t) {
+ return -1 *t*(t-2);
+ },
+ easeInOutQuad: function (t) {
+ if ((t/=1/2) < 1) return 1/2*t*t;
+ return -1/2 * ((--t)*(t-2) - 1);
+ },
+ easeInCubic: function (t) {
+ return t*t*t;
+ },
+ easeOutCubic: function (t) {
+ return 1*((t=t/1-1)*t*t + 1);
+ },
+ easeInOutCubic: function (t) {
+ if ((t/=1/2) < 1) return 1/2*t*t*t;
+ return 1/2*((t-=2)*t*t + 2);
+ },
+ easeInQuart: function (t) {
+ return t*t*t*t;
+ },
+ easeOutQuart: function (t) {
+ return -1 * ((t=t/1-1)*t*t*t - 1);
+ },
+ easeInOutQuart: function (t) {
+ if ((t/=1/2) < 1) return 1/2*t*t*t*t;
+ return -1/2 * ((t-=2)*t*t*t - 2);
+ },
+ easeInQuint: function (t) {
+ return 1*(t/=1)*t*t*t*t;
+ },
+ easeOutQuint: function (t) {
+ return 1*((t=t/1-1)*t*t*t*t + 1);
+ },
+ easeInOutQuint: function (t) {
+ if ((t/=1/2) < 1) return 1/2*t*t*t*t*t;
+ return 1/2*((t-=2)*t*t*t*t + 2);
+ },
+ easeInSine: function (t) {
+ return -1 * Math.cos(t/1 * (Math.PI/2)) + 1;
+ },
+ easeOutSine: function (t) {
+ return 1 * Math.sin(t/1 * (Math.PI/2));
+ },
+ easeInOutSine: function (t) {
+ return -1/2 * (Math.cos(Math.PI*t/1) - 1);
+ },
+ easeInExpo: function (t) {
+ return (t==0) ? 1 : 1 * Math.pow(2, 10 * (t/1 - 1));
+ },
+ easeOutExpo: function (t) {
+ return (t==1) ? 1 : 1 * (-Math.pow(2, -10 * t/1) + 1);
+ },
+ easeInOutExpo: function (t) {
+ if (t==0) return 0;
+ if (t==1) return 1;
+ if ((t/=1/2) < 1) return 1/2 * Math.pow(2, 10 * (t - 1));
+ return 1/2 * (-Math.pow(2, -10 * --t) + 2);
+ },
+ easeInCirc: function (t) {
+ if (t>=1) return t;
+ return -1 * (Math.sqrt(1 - (t/=1)*t) - 1);
+ },
+ easeOutCirc: function (t) {
+ return 1 * Math.sqrt(1 - (t=t/1-1)*t);
+ },
+ easeInOutCirc: function (t) {
+ if ((t/=1/2) < 1) return -1/2 * (Math.sqrt(1 - t*t) - 1);
+ return 1/2 * (Math.sqrt(1 - (t-=2)*t) + 1);
+ },
+ easeInElastic: function (t) {
+ var s=1.70158;var p=0;var a=1;
+ if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
+ },
+ easeOutElastic: function (t) {
+ var s=1.70158;var p=0;var a=1;
+ if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
+ return a*Math.pow(2,-10*t) * Math.sin( (t*1-s)*(2*Math.PI)/p ) + 1;
+ },
+ easeInOutElastic: function (t) {
+ var s=1.70158;var p=0;var a=1;
+ if (t==0) return 0; if ((t/=1/2)==2) return 1; if (!p) p=1*(.3*1.5);
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )*.5 + 1;
+ },
+ easeInBack: function (t) {
+ var s = 1.70158;
+ return 1*(t/=1)*t*((s+1)*t - s);
+ },
+ easeOutBack: function (t) {
+ var s = 1.70158;
+ return 1*((t=t/1-1)*t*((s+1)*t + s) + 1);
+ },
+ easeInOutBack: function (t) {
+ var s = 1.70158;
+ if ((t/=1/2) < 1) return 1/2*(t*t*(((s*=(1.525))+1)*t - s));
+ return 1/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2);
+ },
+ easeInBounce: function (t) {
+ return 1 - animationOptions.easeOutBounce (1-t);
+ },
+ easeOutBounce: function (t) {
+ if ((t/=1) < (1/2.75)) {
+ return 1*(7.5625*t*t);
+ } else if (t < (2/2.75)) {
+ return 1*(7.5625*(t-=(1.5/2.75))*t + .75);
+ } else if (t < (2.5/2.75)) {
+ return 1*(7.5625*(t-=(2.25/2.75))*t + .9375);
+ } else {
+ return 1*(7.5625*(t-=(2.625/2.75))*t + .984375);
+ }
+ },
+ easeInOutBounce: function (t) {
+ if (t < 1/2) return animationOptions.easeInBounce (t*2) * .5;
+ return animationOptions.easeOutBounce (t*2-1) * .5 + 1*.5;
+ }
+ };
+
+ //Variables global to the chart
+ var width = context.canvas.width;
+ var height = context.canvas.height;
+
+
+ //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
+ if (window.devicePixelRatio) {
+ context.canvas.style.width = width + "px";
+ context.canvas.style.height = height + "px";
+ context.canvas.height = height * window.devicePixelRatio;
+ context.canvas.width = width * window.devicePixelRatio;
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
+ }
+
+ this.PolarArea = function(data,options){
+
+ chart.PolarArea.defaults = {
+ scaleOverlay : true,
+ scaleOverride : false,
+ scaleSteps : null,
+ scaleStepWidth : null,
+ scaleStartValue : null,
+ scaleShowLine : true,
+ scaleLineColor : "rgba(0,0,0,.1)",
+ scaleLineWidth : 1,
+ scaleShowLabels : true,
+ scaleLabel : "<%=value%>",
+ scaleFontFamily : "'Arial'",
+ scaleFontSize : 12,
+ scaleFontStyle : "normal",
+ scaleFontColor : "#666",
+ scaleShowLabelBackdrop : true,
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
+ scaleBackdropPaddingY : 2,
+ scaleBackdropPaddingX : 2,
+ segmentShowStroke : true,
+ segmentStrokeColor : "#fff",
+ segmentStrokeWidth : 2,
+ animation : true,
+ animationSteps : 100,
+ animationEasing : "easeOutBounce",
+ animateRotate : true,
+ animateScale : false,
+ onAnimationComplete : null
+ };
+
+ var config = (options)? mergeChartConfig(chart.PolarArea.defaults,options) : chart.PolarArea.defaults;
+
+ return new PolarArea(data,config,context);
+ };
+
+ this.Radar = function(data,options){
+
+ chart.Radar.defaults = {
+ scaleOverlay : false,
+ scaleOverride : false,
+ scaleSteps : null,
+ scaleStepWidth : null,
+ scaleStartValue : null,
+ scaleShowLine : true,
+ scaleLineColor : "rgba(0,0,0,.1)",
+ scaleLineWidth : 1,
+ scaleShowLabels : false,
+ scaleLabel : "<%=value%>",
+ scaleFontFamily : "'Arial'",
+ scaleFontSize : 12,
+ scaleFontStyle : "normal",
+ scaleFontColor : "#666",
+ scaleShowLabelBackdrop : true,
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
+ scaleBackdropPaddingY : 2,
+ scaleBackdropPaddingX : 2,
+ angleShowLineOut : true,
+ angleLineColor : "rgba(0,0,0,.1)",
+ angleLineWidth : 1,
+ pointLabelFontFamily : "'Arial'",
+ pointLabelFontStyle : "normal",
+ pointLabelFontSize : 12,
+ pointLabelFontColor : "#666",
+ pointDot : true,
+ pointDotRadius : 3,
+ pointDotStrokeWidth : 1,
+ datasetStroke : true,
+ datasetStrokeWidth : 2,
+ datasetFill : true,
+ animation : true,
+ animationSteps : 60,
+ animationEasing : "easeOutQuart",
+ onAnimationComplete : null
+ };
+
+ var config = (options)? mergeChartConfig(chart.Radar.defaults,options) : chart.Radar.defaults;
+
+ return new Radar(data,config,context);
+ };
+
+ this.Pie = function(data,options){
+ chart.Pie.defaults = {
+ segmentShowStroke : true,
+ segmentStrokeColor : "#fff",
+ segmentStrokeWidth : 2,
+ animation : true,
+ animationSteps : 100,
+ animationEasing : "easeOutBounce",
+ animateRotate : true,
+ animateScale : false,
+ onAnimationComplete : null
+ };
+
+ var config = (options)? mergeChartConfig(chart.Pie.defaults,options) : chart.Pie.defaults;
+
+ return new Pie(data,config,context);
+ };
+
+ this.Doughnut = function(data,options){
+ chart.Doughnut.defaults = {
+ segmentShowStroke : true,
+ segmentStrokeColor : "#fff",
+ segmentStrokeWidth : 2,
+ percentageInnerCutout : 50,
+ animation : true,
+ animationSteps : 100,
+ animationEasing : "easeOutBounce",
+ animateRotate : true,
+ animateScale : false,
+ onAnimationComplete : null
+ };
+
+ var config = (options)? mergeChartConfig(chart.Doughnut.defaults,options) : chart.Doughnut.defaults;
+
+ return new Doughnut(data,config,context);
+
+ };
+
+ this.Line = function(data,options){
+
+ chart.Line.defaults = {
+ scaleOverlay : false,
+ scaleOverride : false,
+ scaleSteps : null,
+ scaleStepWidth : null,
+ scaleStartValue : null,
+ scaleLineColor : "rgba(0,0,0,.1)",
+ scaleLineWidth : 1,
+ scaleShowLabels : true,
+ scaleLabel : "<%=value%>",
+ scaleFontFamily : "'Arial'",
+ scaleFontSize : 12,
+ scaleFontStyle : "normal",
+ scaleFontColor : "#666",
+ scaleShowGridLines : true,
+ scaleGridLineColor : "rgba(0,0,0,.05)",
+ scaleGridLineWidth : 1,
+ bezierCurve : true,
+ pointDot : true,
+ pointDotRadius : 4,
+ pointDotStrokeWidth : 2,
+ datasetStroke : true,
+ datasetStrokeWidth : 2,
+ datasetFill : true,
+ animation : true,
+ animationSteps : 60,
+ animationEasing : "easeOutQuart",
+ onAnimationComplete : null
+ };
+ var config = (options) ? mergeChartConfig(chart.Line.defaults,options) : chart.Line.defaults;
+
+ return new Line(data,config,context);
+ }
+
+ this.Bar = function(data,options){
+ chart.Bar.defaults = {
+ scaleOverlay : false,
+ scaleOverride : false,
+ scaleSteps : null,
+ scaleStepWidth : null,
+ scaleStartValue : null,
+ scaleLineColor : "rgba(0,0,0,.1)",
+ scaleLineWidth : 1,
+ scaleShowLabels : true,
+ scaleLabel : "<%=value%>",
+ scaleFontFamily : "'Arial'",
+ scaleFontSize : 12,
+ scaleFontStyle : "normal",
+ scaleFontColor : "#666",
+ scaleShowGridLines : true,
+ scaleGridLineColor : "rgba(0,0,0,.05)",
+ scaleGridLineWidth : 1,
+ barShowStroke : true,
+ barStrokeWidth : 2,
+ barValueSpacing : 5,
+ barDatasetSpacing : 1,
+ animation : true,
+ animationSteps : 60,
+ animationEasing : "easeOutQuart",
+ onAnimationComplete : null
+ };
+ var config = (options) ? mergeChartConfig(chart.Bar.defaults,options) : chart.Bar.defaults;
+
+ return new Bar(data,config,context);
+ }
+
+ var clear = function(c){
+ c.clearRect(0, 0, width, height);
+ };
+
+ var PolarArea = function(data,config,ctx){
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString;
+
+
+ calculateDrawingSizes();
+
+ valueBounds = getValueBounds();
+
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null;
+
+ //Check and set the scale
+ if (!config.scaleOverride){
+
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
+ }
+ else {
+ calculatedScale = {
+ steps : config.scaleSteps,
+ stepValue : config.scaleStepWidth,
+ graphMin : config.scaleStartValue,
+ labels : []
+ }
+ for (var i=0; i upperValue) {upperValue = data[i].value;}
+ if (data[i].value < lowerValue) {lowerValue = data[i].value;}
+ };
+
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
+
+ return {
+ maxValue : upperValue,
+ minValue : lowerValue,
+ maxSteps : maxSteps,
+ minSteps : minSteps
+ };
+
+
+ }
+ }
+
+ var Radar = function (data,config,ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString;
+
+ //If no labels are defined set to an empty array, so referencing length for looping doesn't blow up.
+ if (!data.labels) data.labels = [];
+
+ calculateDrawingSizes();
+
+ var valueBounds = getValueBounds();
+
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null;
+
+ //Check and set the scale
+ if (!config.scaleOverride){
+
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
+ }
+ else {
+ calculatedScale = {
+ steps : config.scaleSteps,
+ stepValue : config.scaleStepWidth,
+ graphMin : config.scaleStartValue,
+ labels : []
+ }
+ for (var i=0; i Math.PI){
+ ctx.textAlign = "right";
+ }
+ else{
+ ctx.textAlign = "left";
+ }
+
+ ctx.textBaseline = "middle";
+
+ ctx.fillText(data.labels[k],opposite,-adjacent);
+
+ }
+ ctx.restore();
+ };
+ function calculateDrawingSizes(){
+ maxSize = (Min([width,height])/2);
+
+ labelHeight = config.scaleFontSize*2;
+
+ var labelLength = 0;
+ for (var i=0; ilabelLength) labelLength = textMeasurement;
+ }
+
+ //Figure out whats the largest - the height of the text or the width of what's there, and minus it from the maximum usable size.
+ maxSize -= Max([labelLength,((config.pointLabelFontSize/2)*1.5)]);
+
+ maxSize -= config.pointLabelFontSize;
+ maxSize = CapValue(maxSize, null, 0);
+ scaleHeight = maxSize;
+ //If the label height is less than 5, set it to 5 so we don't have lines on top of each other.
+ labelHeight = Default(labelHeight,5);
+ };
+ function getValueBounds() {
+ var upperValue = Number.MIN_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+
+ for (var i=0; i upperValue){upperValue = data.datasets[i].data[j]}
+ if (data.datasets[i].data[j] < lowerValue){lowerValue = data.datasets[i].data[j]}
+ }
+ }
+
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
+
+ return {
+ maxValue : upperValue,
+ minValue : lowerValue,
+ maxSteps : maxSteps,
+ minSteps : minSteps
+ };
+
+
+ }
+ }
+
+ var Pie = function(data,config,ctx){
+ var segmentTotal = 0;
+
+ //In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge.
+ var pieRadius = Min([height/2,width/2]) - 5;
+
+ for (var i=0; i 0){
+ ctx.save();
+ ctx.textAlign = "right";
+ }
+ else{
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ for (var i=0; i 0){
+ ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize);
+ ctx.rotate(-(rotateLabels * (Math.PI/180)));
+ ctx.fillText(data.labels[i], 0,0);
+ ctx.restore();
+ }
+
+ else{
+ ctx.fillText(data.labels[i], yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize+3);
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY+3);
+
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if(config.scaleShowGridLines && i>0){
+ ctx.lineWidth = config.scaleGridLineWidth;
+ ctx.strokeStyle = config.scaleGridLineColor;
+ ctx.lineTo(yAxisPosX + i * valueHop, 5);
+ }
+ else{
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY+3);
+ }
+ ctx.stroke();
+ }
+
+ //Y axis
+ ctx.lineWidth = config.scaleLineWidth;
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX,xAxisPosY+5);
+ ctx.lineTo(yAxisPosX,5);
+ ctx.stroke();
+
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j=0; j longestText)? measuredText : longestText;
+ }
+ //Add a little extra padding from the y axis
+ longestText +=10;
+ }
+ xAxisLength = width - longestText - widestXLabel;
+ valueHop = Math.floor(xAxisLength/(data.labels.length-1));
+
+ yAxisPosX = width-widestXLabel/2-xAxisLength;
+ xAxisPosY = scaleHeight + config.scaleFontSize/2;
+ }
+ function calculateDrawingSizes(){
+ maxSize = height;
+
+ //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees.
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
+ widestXLabel = 1;
+ for (var i=0; i widestXLabel)? textLength : widestXLabel;
+ }
+ if (width/data.labels.length < widestXLabel){
+ rotateLabels = 45;
+ if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){
+ rotateLabels = 90;
+ maxSize -= widestXLabel;
+ }
+ else{
+ maxSize -= Math.sin(rotateLabels) * widestXLabel;
+ }
+ }
+ else{
+ maxSize -= config.scaleFontSize;
+ }
+
+ //Add a little padding between the x line and the text
+ maxSize -= 5;
+
+
+ labelHeight = config.scaleFontSize;
+
+ maxSize -= labelHeight;
+ //Set 5 pixels greater than the font size to allow for a little padding from the X axis.
+
+ scaleHeight = maxSize;
+
+ //Then get the area above we can safely draw on.
+
+ }
+ function getValueBounds() {
+ var upperValue = Number.MIN_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ for (var i=0; i upperValue) { upperValue = data.datasets[i].data[j] };
+ if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] };
+ }
+ };
+
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
+
+ return {
+ maxValue : upperValue,
+ minValue : lowerValue,
+ maxSteps : maxSteps,
+ minSteps : minSteps
+ };
+
+
+ }
+
+
+ }
+
+ var Bar = function(data,config,ctx){
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY,barWidth, rotateLabels = 0;
+
+ calculateDrawingSizes();
+
+ valueBounds = getValueBounds();
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : "";
+ if (!config.scaleOverride){
+
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
+ }
+ else {
+ calculatedScale = {
+ steps : config.scaleSteps,
+ stepValue : config.scaleStepWidth,
+ graphMin : config.scaleStartValue,
+ labels : []
+ }
+ for (var i=0; i 0){
+ ctx.save();
+ ctx.textAlign = "right";
+ }
+ else{
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ for (var i=0; i 0){
+ ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize);
+ ctx.rotate(-(rotateLabels * (Math.PI/180)));
+ ctx.fillText(data.labels[i], 0,0);
+ ctx.restore();
+ }
+
+ else{
+ ctx.fillText(data.labels[i], yAxisPosX + i*valueHop + valueHop/2,xAxisPosY + config.scaleFontSize+3);
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + (i+1) * valueHop, xAxisPosY+3);
+
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ ctx.lineWidth = config.scaleGridLineWidth;
+ ctx.strokeStyle = config.scaleGridLineColor;
+ ctx.lineTo(yAxisPosX + (i+1) * valueHop, 5);
+ ctx.stroke();
+ }
+
+ //Y axis
+ ctx.lineWidth = config.scaleLineWidth;
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX,xAxisPosY+5);
+ ctx.lineTo(yAxisPosX,5);
+ ctx.stroke();
+
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j=0; j longestText)? measuredText : longestText;
+ }
+ //Add a little extra padding from the y axis
+ longestText +=10;
+ }
+ xAxisLength = width - longestText - widestXLabel;
+ valueHop = Math.floor(xAxisLength/(data.labels.length));
+
+ barWidth = (valueHop - config.scaleGridLineWidth*2 - (config.barValueSpacing*2) - (config.barDatasetSpacing*data.datasets.length-1) - ((config.barStrokeWidth/2)*data.datasets.length-1))/data.datasets.length;
+
+ yAxisPosX = width-widestXLabel/2-xAxisLength;
+ xAxisPosY = scaleHeight + config.scaleFontSize/2;
+ }
+ function calculateDrawingSizes(){
+ maxSize = height;
+
+ //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees.
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
+ widestXLabel = 1;
+ for (var i=0; i widestXLabel)? textLength : widestXLabel;
+ }
+ if (width/data.labels.length < widestXLabel){
+ rotateLabels = 45;
+ if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){
+ rotateLabels = 90;
+ maxSize -= widestXLabel;
+ }
+ else{
+ maxSize -= Math.sin(rotateLabels) * widestXLabel;
+ }
+ }
+ else{
+ maxSize -= config.scaleFontSize;
+ }
+
+ //Add a little padding between the x line and the text
+ maxSize -= 5;
+
+
+ labelHeight = config.scaleFontSize;
+
+ maxSize -= labelHeight;
+ //Set 5 pixels greater than the font size to allow for a little padding from the X axis.
+
+ scaleHeight = maxSize;
+
+ //Then get the area above we can safely draw on.
+
+ }
+ function getValueBounds() {
+ var upperValue = Number.MIN_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ for (var i=0; i upperValue) { upperValue = data.datasets[i].data[j] };
+ if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] };
+ }
+ };
+
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
+
+ return {
+ maxValue : upperValue,
+ minValue : lowerValue,
+ maxSteps : maxSteps,
+ minSteps : minSteps
+ };
+
+
+ }
+ }
+
+ function calculateOffset(val,calculatedScale,scaleHop){
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
+ var adjustedValue = val - calculatedScale.graphMin;
+ var scalingFactor = CapValue(adjustedValue/outerValue,1,0);
+ return (scaleHop*calculatedScale.steps) * scalingFactor;
+ }
+
+ function animationLoop(config,drawScale,drawData,ctx){
+ var animFrameAmount = (config.animation)? 1/CapValue(config.animationSteps,Number.MAX_VALUE,1) : 1,
+ easingFunction = animationOptions[config.animationEasing],
+ percentAnimComplete =(config.animation)? 0 : 1;
+
+
+
+ if (typeof drawScale !== "function") drawScale = function(){};
+
+ requestAnimFrame(animLoop);
+
+ function animateFrame(){
+ var easeAdjustedAnimationPercent =(config.animation)? CapValue(easingFunction(percentAnimComplete),null,0) : 1;
+ clear(ctx);
+ if(config.scaleOverlay){
+ drawData(easeAdjustedAnimationPercent);
+ drawScale();
+ } else {
+ drawScale();
+ drawData(easeAdjustedAnimationPercent);
+ }
+ }
+ function animLoop(){
+ //We need to check if the animation is incomplete (less than 1), or complete (1).
+ percentAnimComplete += animFrameAmount;
+ animateFrame();
+ //Stop the loop continuing forever
+ if (percentAnimComplete <= 1){
+ requestAnimFrame(animLoop);
+ }
+ else{
+ if (typeof config.onAnimationComplete == "function") config.onAnimationComplete();
+ }
+
+ }
+
+ }
+
+ //Declare global functions to be called within this namespace here.
+ // shim layer with setTimeout fallback
+ var requestAnimFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+ })();
+
+ function calculateScale(drawingHeight,maxSteps,minSteps,maxValue,minValue,labelTemplateString){
+ var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum;
+
+ valueRange = maxValue - minValue;
+
+ rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange);
+ //var graphRange = (Math.ceil(rangeMultipland) * Math.pow(10, rangeOrderOfMagnitude));
+ graphMin = (Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude));
+
+ graphMax = (Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude));
+
+ graphRange = graphMax - graphMin;
+
+ stepValue = Math.pow(10, rangeOrderOfMagnitude);
+ numberOfSteps = Math.round(graphRange / stepValue);
+
+
+ while(numberOfSteps < minSteps || numberOfSteps > maxSteps) {
+ if (numberOfSteps < minSteps){
+ stepValue /= 2;
+ numberOfSteps = Math.round(graphRange/stepValue);
+ }
+ else{
+ stepValue *=2;
+ numberOfSteps = Math.round(graphRange/stepValue);
+ }
+ };
+
+ //Compare number of steps to the max and min for that size graph, and add in half steps if need be.
+
+ //Create an array of all the labels by interpolating the string.
+
+ var labels = [];
+ if(labelTemplateString){
+ //Fix floating point errors by setting to fixed the on the same decimal as the stepValue.
+ for (var i=1; i maxValue ) {
+ return maxValue;
+ }
+ }
+ if(isNumber(minValue)){
+ if ( valueToCap < minValue ){
+ return minValue;
+ }
+ }
+ return valueToCap;
+ }
+ function getDecimalPlaces (num){
+ var numberOfDecimalPlaces;
+ if (num%1!=0){
+ return num.toString().split(".")[1].length
+ }
+ else{
+ return 0;
+ }
+
+ }
+ function mergeChartConfig(defaults,userDefined){
+ var returnObj = {};
+ for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; }
+ for (var attrname in userDefined) { returnObj[attrname] = userDefined[attrname]; }
+ return returnObj;
+ }
+ //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
+ var cache = {};
+
+ function tmpl(str, data){
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ var fn = !/\W/.test(str) ?
+ cache[str] = cache[str] ||
+ tmpl(document.getElementById(str).innerHTML) :
+
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\t\n]/g, " ")
+ .split("<%").join("\t")
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)%>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("%>").join("p.push('")
+ .split("\r").join("\\'")
+ + "');}return p.join('');");
+
+ // Provide some basic currying to the user
+ return data ? fn( data ) : fn;
+ };
+}
+
+
+
diff --git a/third-party-scripts/assert.js b/third-party-scripts/assert.js
new file mode 100644
index 0000000..7db7b15
--- /dev/null
+++ b/third-party-scripts/assert.js
@@ -0,0 +1,297 @@
+var assert = function () { return assert.ok.apply(this, arguments); };
+(function (exports) {
+ "use strict";
+
+// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
+//
+// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
+//
+// Originally from narwhal.js (http://narwhaljs.org)
+// Copyright (c) 2009 Thomas Robinson <280north.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the 'Software'), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var pSlice = Array.prototype.slice;
+
+// 1. The assert module provides functions that throw
+// AssertionError's when particular conditions are not met. The
+// assert module must conform to the following interface.
+
+var assert = exports;
+
+// 2. The AssertionError is defined in assert.
+// new assert.AssertionError({ message: message,
+// actual: actual,
+// expected: expected })
+
+assert.AssertionError = function AssertionError(options) {
+ this.name = 'AssertionError';
+ this.message = options.message;
+ this.actual = options.actual;
+ this.expected = options.expected;
+ this.operator = options.operator;
+ var stackStartFunction = options.stackStartFunction || fail;
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, stackStartFunction);
+ }
+};
+assert.AssertionError.prototype = Object.create(Error.prototype);
+
+assert.AssertionError.prototype.toString = function() {
+ if (this.message) {
+ return [this.name + ':', this.message].join(' ');
+ } else {
+ return [this.name + ':',
+ JSON.stringify(this.expected),
+ this.operator,
+ JSON.stringify(this.actual)].join(' ');
+ }
+};
+
+// assert.AssertionError instanceof Error
+
+assert.AssertionError.__proto__ = Error.prototype;
+
+// At present only the three keys mentioned above are used and
+// understood by the spec. Implementations or sub modules can pass
+// other keys to the AssertionError's constructor - they will be
+// ignored.
+
+// 3. All of the following functions must throw an AssertionError
+// when a corresponding condition is not met, with a message that
+// may be undefined if not provided. All assertion methods provide
+// both the actual and expected values to the assertion error for
+// display purposes.
+
+function fail(actual, expected, message, operator, stackStartFunction) {
+ throw new assert.AssertionError({
+ message: message,
+ actual: actual,
+ expected: expected,
+ operator: operator,
+ stackStartFunction: stackStartFunction
+ });
+}
+
+// EXTENSION! allows for well behaved errors defined elsewhere.
+assert.fail = fail;
+
+// 4. Pure assertion tests whether a value is truthy, as determined
+// by !!guard.
+// assert.ok(guard, message_opt);
+// This statement is equivalent to assert.equal(true, guard,
+// message_opt);. To test strictly for the value true, use
+// assert.strictEqual(true, guard, message_opt);.
+
+assert.ok = function ok(value, message) {
+ if (!!!value) fail(value, true, message, '==', assert.ok);
+};
+
+// 5. The equality assertion tests shallow, coercive equality with
+// ==.
+// assert.equal(actual, expected, message_opt);
+
+assert.equal = function equal(actual, expected, message) {
+ if (actual != expected) fail(actual, expected, message, '==', assert.equal);
+};
+
+// 6. The non-equality assertion tests for whether two objects are not equal
+// with != assert.notEqual(actual, expected, message_opt);
+
+assert.notEqual = function notEqual(actual, expected, message) {
+ if (actual == expected) {
+ fail(actual, expected, message, '!=', assert.notEqual);
+ }
+};
+
+// 7. The equivalence assertion tests a deep equality relation.
+// assert.deepEqual(actual, expected, message_opt);
+
+assert.deepEqual = function deepEqual(actual, expected, message) {
+ if (!_deepEqual(actual, expected)) {
+ fail(actual, expected, message, 'deepEqual', assert.deepEqual);
+ }
+};
+
+function _deepEqual(actual, expected) {
+ // 7.1. All identical values are equivalent, as determined by ===.
+ if (actual === expected) {
+ return true;
+
+ // 7.2. If the expected value is a Date object, the actual value is
+ // equivalent if it is also a Date object that refers to the same time.
+ } else if (actual instanceof Date && expected instanceof Date) {
+ return actual.getTime() === expected.getTime();
+
+ // 7.3. Other pairs that do not both pass typeof value == 'object',
+ // equivalence is determined by ==.
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
+ return actual == expected;
+
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
+ // determined by having the same number of owned properties (as verified
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
+ // (although not necessarily the same order), equivalent values for every
+ // corresponding key, and an identical 'prototype' property. Note: this
+ // accounts for both named and indexed properties on Arrays.
+ } else {
+ return objEquiv(actual, expected);
+ }
+}
+
+function isUndefinedOrNull(value) {
+ return value === null || value === undefined;
+}
+
+function isArguments(object) {
+ return Object.prototype.toString.call(object) == '[object Arguments]';
+}
+
+function objEquiv(a, b) {
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
+ return false;
+ // an identical 'prototype' property.
+ if (a.prototype !== b.prototype) return false;
+ //~~~I've managed to break Object.keys through screwy arguments passing.
+ // Converting to array solves the problem.
+ if (isArguments(a)) {
+ if (!isArguments(b)) {
+ return false;
+ }
+ a = pSlice.call(a);
+ b = pSlice.call(b);
+ return _deepEqual(a, b);
+ }
+ try {
+ var ka = Object.keys(a),
+ kb = Object.keys(b),
+ key, i;
+ } catch (e) {//happens when one is a string literal and the other isn't
+ return false;
+ }
+ // having the same number of owned properties (keys incorporates
+ // hasOwnProperty)
+ if (ka.length != kb.length)
+ return false;
+ //the same set of keys (although not necessarily the same order),
+ ka.sort();
+ kb.sort();
+ //~~~cheap key test
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] != kb[i])
+ return false;
+ }
+ //equivalent values for every corresponding key, and
+ //~~~possibly expensive deep test
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ if (!_deepEqual(a[key], b[key])) return false;
+ }
+ return true;
+}
+
+// 8. The non-equivalence assertion tests for any deep inequality.
+// assert.notDeepEqual(actual, expected, message_opt);
+
+assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
+ if (_deepEqual(actual, expected)) {
+ fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
+ }
+};
+
+// 9. The strict equality assertion tests strict equality, as determined by ===.
+// assert.strictEqual(actual, expected, message_opt);
+
+assert.strictEqual = function strictEqual(actual, expected, message) {
+ if (actual !== expected) {
+ fail(actual, expected, message, '===', assert.strictEqual);
+ }
+};
+
+// 10. The strict non-equality assertion tests for strict inequality, as
+// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
+
+assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
+ if (actual === expected) {
+ fail(actual, expected, message, '!==', assert.notStrictEqual);
+ }
+};
+
+function expectedException(actual, expected) {
+ if (!actual || !expected) {
+ return false;
+ }
+
+ if (expected instanceof RegExp) {
+ return expected.test(actual);
+ } else if (actual instanceof expected) {
+ return true;
+ } else if (expected.call({}, actual) === true) {
+ return true;
+ }
+
+ return false;
+}
+
+function _throws(shouldThrow, block, expected, message) {
+ var actual;
+
+ if (typeof expected === 'string') {
+ message = expected;
+ expected = null;
+ }
+
+ try {
+ block();
+ } catch (e) {
+ actual = e;
+ }
+
+ message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
+ (message ? ' ' + message : '.');
+
+ if (shouldThrow && !actual) {
+ fail('Missing expected exception' + message);
+ }
+
+ if (!shouldThrow && expectedException(actual, expected)) {
+ fail('Got unwanted exception' + message);
+ }
+
+ if ((shouldThrow && actual && expected &&
+ !expectedException(actual, expected)) || (!shouldThrow && actual)) {
+ throw actual;
+ }
+}
+
+// 11. Expected to throw an error:
+// assert.throws(block, Error_opt, message_opt);
+
+assert.throws = function(block, /*optional*/error, /*optional*/message) {
+ _throws.apply(this, [true].concat(pSlice.call(arguments)));
+};
+
+// EXTENSION! This is annoying to write outside this module.
+assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
+ _throws.apply(this, [false].concat(pSlice.call(arguments)));
+};
+
+assert.ifError = function(err) { if (err) {throw err;}};
+
+}(assert));
diff --git a/third-party-scripts/biginteger.js b/third-party-scripts/biginteger.js
new file mode 100644
index 0000000..50a29a5
--- /dev/null
+++ b/third-party-scripts/biginteger.js
@@ -0,0 +1,1620 @@
+/*
+ JavaScript BigInteger library version 0.9
+ http://silentmatt.com/biginteger/
+
+ Copyright (c) 2009 Matthew Crumley
+ Copyright (c) 2010,2011 by John Tobey
+ Licensed under the MIT license.
+
+ Support for arbitrary internal representation base was added by
+ Vitaly Magerya.
+*/
+
+/*
+ File: biginteger.js
+
+ Exports:
+
+
+*/
+var biginteger = {};
+(function(exports) {
+"use strict";
+/*
+ Class: BigInteger
+ An arbitrarily-large integer.
+
+ objects should be considered immutable. None of the "built-in"
+ methods modify *this* or their arguments. All properties should be
+ considered private.
+
+ All the methods of instances can be called "statically". The
+ static versions are convenient if you don't already have a
+ object.
+
+ As an example, these calls are equivalent.
+
+ > BigInteger(4).multiply(5); // returns BigInteger(20);
+ > BigInteger.multiply(4, 5); // returns BigInteger(20);
+
+ > var a = 42;
+ > var a = BigInteger.toJSValue("0b101010"); // Not completely useless...
+*/
+
+var CONSTRUCT = {}; // Unique token to call "private" version of constructor
+
+/*
+ Constructor: BigInteger()
+ Convert a value to a .
+
+ Although is the constructor for objects, it is
+ best not to call it as a constructor. If *n* is a object, it is
+ simply returned as-is. Otherwise, is equivalent to
+ without a radix argument.
+
+ > var n0 = BigInteger(); // Same as
+ > var n1 = BigInteger("123"); // Create a new with value 123
+ > var n2 = BigInteger(123); // Create a new with value 123
+ > var n3 = BigInteger(n2); // Return n2, unchanged
+
+ The constructor form only takes an array and a sign. *n* must be an
+ array of numbers in little-endian order, where each digit is between 0
+ and BigInteger.base. The second parameter sets the sign: -1 for
+ negative, +1 for positive, or 0 for zero. The array is *not copied and
+ may be modified*. If the array contains only zeros, the sign parameter
+ is ignored and is forced to zero.
+
+ > new BigInteger([5], -1): create a new BigInteger with value -5
+
+ Parameters:
+
+ n - Value to convert to a .
+
+ Returns:
+
+ A value.
+
+ See Also:
+
+ ,
+*/
+function BigInteger(n, s, token) {
+ if (token !== CONSTRUCT) {
+ if (n instanceof BigInteger) {
+ return n;
+ }
+ else if (typeof n === "undefined") {
+ return ZERO;
+ }
+ return BigInteger.parse(n);
+ }
+
+ n = n || []; // Provide the nullary constructor for subclasses.
+ while (n.length && !n[n.length - 1]) {
+ --n.length;
+ }
+ this._d = n;
+ this._s = n.length ? (s || 1) : 0;
+}
+
+BigInteger._construct = function(n, s) {
+ return new BigInteger(n, s, CONSTRUCT);
+};
+
+// Base-10 speedup hacks in parse, toString, exp10 and log functions
+// require base to be a power of 10. 10^7 is the largest such power
+// that won't cause a precision loss when digits are multiplied.
+var BigInteger_base = 10000000;
+var BigInteger_base_log10 = 7;
+
+BigInteger.base = BigInteger_base;
+BigInteger.base_log10 = BigInteger_base_log10;
+
+var ZERO = new BigInteger([], 0, CONSTRUCT);
+// Constant: ZERO
+// 0.
+BigInteger.ZERO = ZERO;
+
+var ONE = new BigInteger([1], 1, CONSTRUCT);
+// Constant: ONE
+// 1.
+BigInteger.ONE = ONE;
+
+var M_ONE = new BigInteger(ONE._d, -1, CONSTRUCT);
+// Constant: M_ONE
+// -1.
+BigInteger.M_ONE = M_ONE;
+
+// Constant: _0
+// Shortcut for .
+BigInteger._0 = ZERO;
+
+// Constant: _1
+// Shortcut for .
+BigInteger._1 = ONE;
+
+/*
+ Constant: small
+ Array of from 0 to 36.
+
+ These are used internally for parsing, but useful when you need a "small"
+ .
+
+ See Also:
+
+ , , <_0>, <_1>
+*/
+BigInteger.small = [
+ ZERO,
+ ONE,
+ /* Assuming BigInteger_base > 36 */
+ new BigInteger( [2], 1, CONSTRUCT),
+ new BigInteger( [3], 1, CONSTRUCT),
+ new BigInteger( [4], 1, CONSTRUCT),
+ new BigInteger( [5], 1, CONSTRUCT),
+ new BigInteger( [6], 1, CONSTRUCT),
+ new BigInteger( [7], 1, CONSTRUCT),
+ new BigInteger( [8], 1, CONSTRUCT),
+ new BigInteger( [9], 1, CONSTRUCT),
+ new BigInteger([10], 1, CONSTRUCT),
+ new BigInteger([11], 1, CONSTRUCT),
+ new BigInteger([12], 1, CONSTRUCT),
+ new BigInteger([13], 1, CONSTRUCT),
+ new BigInteger([14], 1, CONSTRUCT),
+ new BigInteger([15], 1, CONSTRUCT),
+ new BigInteger([16], 1, CONSTRUCT),
+ new BigInteger([17], 1, CONSTRUCT),
+ new BigInteger([18], 1, CONSTRUCT),
+ new BigInteger([19], 1, CONSTRUCT),
+ new BigInteger([20], 1, CONSTRUCT),
+ new BigInteger([21], 1, CONSTRUCT),
+ new BigInteger([22], 1, CONSTRUCT),
+ new BigInteger([23], 1, CONSTRUCT),
+ new BigInteger([24], 1, CONSTRUCT),
+ new BigInteger([25], 1, CONSTRUCT),
+ new BigInteger([26], 1, CONSTRUCT),
+ new BigInteger([27], 1, CONSTRUCT),
+ new BigInteger([28], 1, CONSTRUCT),
+ new BigInteger([29], 1, CONSTRUCT),
+ new BigInteger([30], 1, CONSTRUCT),
+ new BigInteger([31], 1, CONSTRUCT),
+ new BigInteger([32], 1, CONSTRUCT),
+ new BigInteger([33], 1, CONSTRUCT),
+ new BigInteger([34], 1, CONSTRUCT),
+ new BigInteger([35], 1, CONSTRUCT),
+ new BigInteger([36], 1, CONSTRUCT)
+];
+
+// Used for parsing/radix conversion
+BigInteger.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
+
+/*
+ Method: toString
+ Convert a to a string.
+
+ When *base* is greater than 10, letters are upper case.
+
+ Parameters:
+
+ base - Optional base to represent the number in (default is base 10).
+ Must be between 2 and 36 inclusive, or an Error will be thrown.
+
+ Returns:
+
+ The string representation of the .
+*/
+BigInteger.prototype.toString = function(base) {
+ base = +base || 10;
+ if (base < 2 || base > 36) {
+ throw new Error("illegal radix " + base + ".");
+ }
+ if (this._s === 0) {
+ return "0";
+ }
+ if (base === 10) {
+ var str = this._s < 0 ? "-" : "";
+ str += this._d[this._d.length - 1].toString();
+ for (var i = this._d.length - 2; i >= 0; i--) {
+ var group = this._d[i].toString();
+ while (group.length < BigInteger_base_log10) group = '0' + group;
+ str += group;
+ }
+ return str;
+ }
+ else {
+ var numerals = BigInteger.digits;
+ base = BigInteger.small[base];
+ var sign = this._s;
+
+ var n = this.abs();
+ var digits = [];
+ var digit;
+
+ while (n._s !== 0) {
+ var divmod = n.divRem(base);
+ n = divmod[0];
+ digit = divmod[1];
+ // TODO: This could be changed to unshift instead of reversing at the end.
+ // Benchmark both to compare speeds.
+ digits.push(numerals[digit.valueOf()]);
+ }
+ return (sign < 0 ? "-" : "") + digits.reverse().join("");
+ }
+};
+
+// Verify strings for parsing
+BigInteger.radixRegex = [
+ /^$/,
+ /^$/,
+ /^[01]*$/,
+ /^[012]*$/,
+ /^[0-3]*$/,
+ /^[0-4]*$/,
+ /^[0-5]*$/,
+ /^[0-6]*$/,
+ /^[0-7]*$/,
+ /^[0-8]*$/,
+ /^[0-9]*$/,
+ /^[0-9aA]*$/,
+ /^[0-9abAB]*$/,
+ /^[0-9abcABC]*$/,
+ /^[0-9a-dA-D]*$/,
+ /^[0-9a-eA-E]*$/,
+ /^[0-9a-fA-F]*$/,
+ /^[0-9a-gA-G]*$/,
+ /^[0-9a-hA-H]*$/,
+ /^[0-9a-iA-I]*$/,
+ /^[0-9a-jA-J]*$/,
+ /^[0-9a-kA-K]*$/,
+ /^[0-9a-lA-L]*$/,
+ /^[0-9a-mA-M]*$/,
+ /^[0-9a-nA-N]*$/,
+ /^[0-9a-oA-O]*$/,
+ /^[0-9a-pA-P]*$/,
+ /^[0-9a-qA-Q]*$/,
+ /^[0-9a-rA-R]*$/,
+ /^[0-9a-sA-S]*$/,
+ /^[0-9a-tA-T]*$/,
+ /^[0-9a-uA-U]*$/,
+ /^[0-9a-vA-V]*$/,
+ /^[0-9a-wA-W]*$/,
+ /^[0-9a-xA-X]*$/,
+ /^[0-9a-yA-Y]*$/,
+ /^[0-9a-zA-Z]*$/
+];
+
+/*
+ Function: parse
+ Parse a string into a .
+
+ *base* is optional but, if provided, must be from 2 to 36 inclusive. If
+ *base* is not provided, it will be guessed based on the leading characters
+ of *s* as follows:
+
+ - "0x" or "0X": *base* = 16
+ - "0c" or "0C": *base* = 8
+ - "0b" or "0B": *base* = 2
+ - else: *base* = 10
+
+ If no base is provided, or *base* is 10, the number can be in exponential
+ form. For example, these are all valid:
+
+ > BigInteger.parse("1e9"); // Same as "1000000000"
+ > BigInteger.parse("1.234*10^3"); // Same as 1234
+ > BigInteger.parse("56789 * 10 ** -2"); // Same as 567
+
+ If any characters fall outside the range defined by the radix, an exception
+ will be thrown.
+
+ Parameters:
+
+ s - The string to parse.
+ base - Optional radix (default is to guess based on *s*).
+
+ Returns:
+
+ a instance.
+*/
+BigInteger.parse = function(s, base) {
+ // Expands a number in exponential form to decimal form.
+ // expandExponential("-13.441*10^5") === "1344100";
+ // expandExponential("1.12300e-1") === "0.112300";
+ // expandExponential(1000000000000000000000000000000) === "1000000000000000000000000000000";
+ function expandExponential(str) {
+ str = str.replace(/\s*[*xX]\s*10\s*(\^|\*\*)\s*/, "e");
+
+ return str.replace(/^([+\-])?(\d+)\.?(\d*)[eE]([+\-]?\d+)$/, function(x, s, n, f, c) {
+ c = +c;
+ var l = c < 0;
+ var i = n.length + c;
+ x = (l ? n : f).length;
+ c = ((c = Math.abs(c)) >= x ? c - x + l : 0);
+ var z = (new Array(c + 1)).join("0");
+ var r = n + f;
+ return (s || "") + (l ? r = z + r : r += z).substr(0, i += l ? z.length : 0) + (i < r.length ? "." + r.substr(i) : "");
+ });
+ }
+
+ s = s.toString();
+ if (typeof base === "undefined" || +base === 10) {
+ s = expandExponential(s);
+ }
+
+ var prefixRE;
+ if (typeof base === "undefined") {
+ prefixRE = '0[xcb]';
+ }
+ else if (base == 16) {
+ prefixRE = '0x';
+ }
+ else if (base == 8) {
+ prefixRE = '0c';
+ }
+ else if (base == 2) {
+ prefixRE = '0b';
+ }
+ else {
+ prefixRE = '';
+ }
+ var parts = new RegExp('^([+\\-]?)(' + prefixRE + ')?([0-9a-z]*)(?:\\.\\d*)?$', 'i').exec(s);
+ if (parts) {
+ var sign = parts[1] || "+";
+ var baseSection = parts[2] || "";
+ var digits = parts[3] || "";
+
+ if (typeof base === "undefined") {
+ // Guess base
+ if (baseSection === "0x" || baseSection === "0X") { // Hex
+ base = 16;
+ }
+ else if (baseSection === "0c" || baseSection === "0C") { // Octal
+ base = 8;
+ }
+ else if (baseSection === "0b" || baseSection === "0B") { // Binary
+ base = 2;
+ }
+ else {
+ base = 10;
+ }
+ }
+ else if (base < 2 || base > 36) {
+ throw new Error("Illegal radix " + base + ".");
+ }
+
+ base = +base;
+
+ // Check for digits outside the range
+ if (!(BigInteger.radixRegex[base].test(digits))) {
+ throw new Error("Bad digit for radix " + base);
+ }
+
+ // Strip leading zeros, and convert to array
+ digits = digits.replace(/^0+/, "").split("");
+ if (digits.length === 0) {
+ return ZERO;
+ }
+
+ // Get the sign (we know it's not zero)
+ sign = (sign === "-") ? -1 : 1;
+
+ // Optimize 10
+ if (base == 10) {
+ var d = [];
+ while (digits.length >= BigInteger_base_log10) {
+ d.push(parseInt(digits.splice(digits.length-BigInteger.base_log10, BigInteger.base_log10).join(''), 10));
+ }
+ d.push(parseInt(digits.join(''), 10));
+ return new BigInteger(d, sign, CONSTRUCT);
+ }
+
+ // Do the conversion
+ var d = ZERO;
+ base = BigInteger.small[base];
+ var small = BigInteger.small;
+ for (var i = 0; i < digits.length; i++) {
+ d = d.multiply(base).add(small[parseInt(digits[i], 36)]);
+ }
+ return new BigInteger(d._d, sign, CONSTRUCT);
+ }
+ else {
+ throw new Error("Invalid BigInteger format: " + s);
+ }
+};
+
+/*
+ Function: add
+ Add two .
+
+ Parameters:
+
+ n - The number to add to *this*. Will be converted to a .
+
+ Returns:
+
+ The numbers added together.
+
+ See Also:
+
+ , , ,
+*/
+BigInteger.prototype.add = function(n) {
+ if (this._s === 0) {
+ return BigInteger(n);
+ }
+
+ n = BigInteger(n);
+ if (n._s === 0) {
+ return this;
+ }
+ if (this._s !== n._s) {
+ n = n.negate();
+ return this.subtract(n);
+ }
+
+ var a = this._d;
+ var b = n._d;
+ var al = a.length;
+ var bl = b.length;
+ var sum = new Array(Math.max(al, bl) + 1);
+ var size = Math.min(al, bl);
+ var carry = 0;
+ var digit;
+
+ for (var i = 0; i < size; i++) {
+ digit = a[i] + b[i] + carry;
+ sum[i] = digit % BigInteger_base;
+ carry = (digit / BigInteger_base) | 0;
+ }
+ if (bl > al) {
+ a = b;
+ al = bl;
+ }
+ for (i = size; carry && i < al; i++) {
+ digit = a[i] + carry;
+ sum[i] = digit % BigInteger_base;
+ carry = (digit / BigInteger_base) | 0;
+ }
+ if (carry) {
+ sum[i] = carry;
+ }
+
+ for ( ; i < al; i++) {
+ sum[i] = a[i];
+ }
+
+ return new BigInteger(sum, this._s, CONSTRUCT);
+};
+
+/*
+ Function: negate
+ Get the additive inverse of a .
+
+ Returns:
+
+ A with the same magnatude, but with the opposite sign.
+
+ See Also:
+
+
+*/
+BigInteger.prototype.negate = function() {
+ return new BigInteger(this._d, (-this._s) | 0, CONSTRUCT);
+};
+
+/*
+ Function: abs
+ Get the absolute value of a .
+
+ Returns:
+
+ A with the same magnatude, but always positive (or zero).
+
+ See Also:
+
+
+*/
+BigInteger.prototype.abs = function() {
+ return (this._s < 0) ? this.negate() : this;
+};
+
+/*
+ Function: subtract
+ Subtract two .
+
+ Parameters:
+
+ n - The number to subtract from *this*. Will be converted to a .
+
+ Returns:
+
+ The *n* subtracted from *this*.
+
+ See Also:
+
+ , , ,
+*/
+BigInteger.prototype.subtract = function(n) {
+ if (this._s === 0) {
+ return BigInteger(n).negate();
+ }
+
+ n = BigInteger(n);
+ if (n._s === 0) {
+ return this;
+ }
+ if (this._s !== n._s) {
+ n = n.negate();
+ return this.add(n);
+ }
+
+ var m = this;
+ // negative - negative => -|a| - -|b| => -|a| + |b| => |b| - |a|
+ if (this._s < 0) {
+ m = new BigInteger(n._d, 1, CONSTRUCT);
+ n = new BigInteger(this._d, 1, CONSTRUCT);
+ }
+
+ // Both are positive => a - b
+ var sign = m.compareAbs(n);
+ if (sign === 0) {
+ return ZERO;
+ }
+ else if (sign < 0) {
+ // swap m and n
+ var t = n;
+ n = m;
+ m = t;
+ }
+
+ // a > b
+ var a = m._d;
+ var b = n._d;
+ var al = a.length;
+ var bl = b.length;
+ var diff = new Array(al); // al >= bl since a > b
+ var borrow = 0;
+ var i;
+ var digit;
+
+ for (i = 0; i < bl; i++) {
+ digit = a[i] - borrow - b[i];
+ if (digit < 0) {
+ digit += BigInteger_base;
+ borrow = 1;
+ }
+ else {
+ borrow = 0;
+ }
+ diff[i] = digit;
+ }
+ for (i = bl; i < al; i++) {
+ digit = a[i] - borrow;
+ if (digit < 0) {
+ digit += BigInteger_base;
+ }
+ else {
+ diff[i++] = digit;
+ break;
+ }
+ diff[i] = digit;
+ }
+ for ( ; i < al; i++) {
+ diff[i] = a[i];
+ }
+
+ return new BigInteger(diff, sign, CONSTRUCT);
+};
+
+(function() {
+ function addOne(n, sign) {
+ var a = n._d;
+ var sum = a.slice();
+ var carry = true;
+ var i = 0;
+
+ while (true) {
+ var digit = (a[i] || 0) + 1;
+ sum[i] = digit % BigInteger_base;
+ if (digit <= BigInteger_base - 1) {
+ break;
+ }
+ ++i;
+ }
+
+ return new BigInteger(sum, sign, CONSTRUCT);
+ }
+
+ function subtractOne(n, sign) {
+ var a = n._d;
+ var sum = a.slice();
+ var borrow = true;
+ var i = 0;
+
+ while (true) {
+ var digit = (a[i] || 0) - 1;
+ if (digit < 0) {
+ sum[i] = digit + BigInteger_base;
+ }
+ else {
+ sum[i] = digit;
+ break;
+ }
+ ++i;
+ }
+
+ return new BigInteger(sum, sign, CONSTRUCT);
+ }
+
+ /*
+ Function: next
+ Get the next (add one).
+
+ Returns:
+
+ *this* + 1.
+
+ See Also:
+
+ ,
+ */
+ BigInteger.prototype.next = function() {
+ switch (this._s) {
+ case 0:
+ return ONE;
+ case -1:
+ return subtractOne(this, -1);
+ // case 1:
+ default:
+ return addOne(this, 1);
+ }
+ };
+
+ /*
+ Function: prev
+ Get the previous (subtract one).
+
+ Returns:
+
+ *this* - 1.
+
+ See Also:
+
+ ,
+ */
+ BigInteger.prototype.prev = function() {
+ switch (this._s) {
+ case 0:
+ return M_ONE;
+ case -1:
+ return addOne(this, -1);
+ // case 1:
+ default:
+ return subtractOne(this, 1);
+ }
+ };
+})();
+
+/*
+ Function: compareAbs
+ Compare the absolute value of two .
+
+ Calling is faster than calling twice, then .
+
+ Parameters:
+
+ n - The number to compare to *this*. Will be converted to a .
+
+ Returns:
+
+ -1, 0, or +1 if *|this|* is less than, equal to, or greater than *|n|*.
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.compareAbs = function(n) {
+ if (this === n) {
+ return 0;
+ }
+
+ if (!(n instanceof BigInteger)) {
+ if (!isFinite(n)) {
+ return(isNaN(n) ? n : -1);
+ }
+ n = BigInteger(n);
+ }
+
+ if (this._s === 0) {
+ return (n._s !== 0) ? -1 : 0;
+ }
+ if (n._s === 0) {
+ return 1;
+ }
+
+ var l = this._d.length;
+ var nl = n._d.length;
+ if (l < nl) {
+ return -1;
+ }
+ else if (l > nl) {
+ return 1;
+ }
+
+ var a = this._d;
+ var b = n._d;
+ for (var i = l-1; i >= 0; i--) {
+ if (a[i] !== b[i]) {
+ return a[i] < b[i] ? -1 : 1;
+ }
+ }
+
+ return 0;
+};
+
+/*
+ Function: compare
+ Compare two .
+
+ Parameters:
+
+ n - The number to compare to *this*. Will be converted to a .
+
+ Returns:
+
+ -1, 0, or +1 if *this* is less than, equal to, or greater than *n*.
+
+ See Also:
+
+ , , ,
+*/
+BigInteger.prototype.compare = function(n) {
+ if (this === n) {
+ return 0;
+ }
+
+ n = BigInteger(n);
+
+ if (this._s === 0) {
+ return -n._s;
+ }
+
+ if (this._s === n._s) { // both positive or both negative
+ var cmp = this.compareAbs(n);
+ return cmp * this._s;
+ }
+ else {
+ return this._s;
+ }
+};
+
+/*
+ Function: isUnit
+ Return true iff *this* is either 1 or -1.
+
+ Returns:
+
+ true if *this* compares equal to or .
+
+ See Also:
+
+ , , , , ,
+ ,
+*/
+BigInteger.prototype.isUnit = function() {
+ return this === ONE ||
+ this === M_ONE ||
+ (this._d.length === 1 && this._d[0] === 1);
+};
+
+/*
+ Function: multiply
+ Multiply two .
+
+ Parameters:
+
+ n - The number to multiply *this* by. Will be converted to a
+ .
+
+ Returns:
+
+ The numbers multiplied together.
+
+ See Also:
+
+ , , ,
+*/
+BigInteger.prototype.multiply = function(n) {
+ // TODO: Consider adding Karatsuba multiplication for large numbers
+ if (this._s === 0) {
+ return ZERO;
+ }
+
+ n = BigInteger(n);
+ if (n._s === 0) {
+ return ZERO;
+ }
+ if (this.isUnit()) {
+ if (this._s < 0) {
+ return n.negate();
+ }
+ return n;
+ }
+ if (n.isUnit()) {
+ if (n._s < 0) {
+ return this.negate();
+ }
+ return this;
+ }
+ if (this === n) {
+ return this.square();
+ }
+
+ var r = (this._d.length >= n._d.length);
+ var a = (r ? this : n)._d; // a will be longer than b
+ var b = (r ? n : this)._d;
+ var al = a.length;
+ var bl = b.length;
+
+ var pl = al + bl;
+ var partial = new Array(pl);
+ var i;
+ for (i = 0; i < pl; i++) {
+ partial[i] = 0;
+ }
+
+ for (i = 0; i < bl; i++) {
+ var carry = 0;
+ var bi = b[i];
+ var jlimit = al + i;
+ var digit;
+ for (var j = i; j < jlimit; j++) {
+ digit = partial[j] + bi * a[j - i] + carry;
+ carry = (digit / BigInteger_base) | 0;
+ partial[j] = (digit % BigInteger_base) | 0;
+ }
+ if (carry) {
+ digit = partial[j] + carry;
+ carry = (digit / BigInteger_base) | 0;
+ partial[j] = digit % BigInteger_base;
+ }
+ }
+ return new BigInteger(partial, this._s * n._s, CONSTRUCT);
+};
+
+// Multiply a BigInteger by a single-digit native number
+// Assumes that this and n are >= 0
+// This is not really intended to be used outside the library itself
+BigInteger.prototype.multiplySingleDigit = function(n) {
+ if (n === 0 || this._s === 0) {
+ return ZERO;
+ }
+ if (n === 1) {
+ return this;
+ }
+
+ var digit;
+ if (this._d.length === 1) {
+ digit = this._d[0] * n;
+ if (digit >= BigInteger_base) {
+ return new BigInteger([(digit % BigInteger_base)|0,
+ (digit / BigInteger_base)|0], 1, CONSTRUCT);
+ }
+ return new BigInteger([digit], 1, CONSTRUCT);
+ }
+
+ if (n === 2) {
+ return this.add(this);
+ }
+ if (this.isUnit()) {
+ return new BigInteger([n], 1, CONSTRUCT);
+ }
+
+ var a = this._d;
+ var al = a.length;
+
+ var pl = al + 1;
+ var partial = new Array(pl);
+ for (var i = 0; i < pl; i++) {
+ partial[i] = 0;
+ }
+
+ var carry = 0;
+ for (var j = 0; j < al; j++) {
+ digit = n * a[j] + carry;
+ carry = (digit / BigInteger_base) | 0;
+ partial[j] = (digit % BigInteger_base) | 0;
+ }
+ if (carry) {
+ partial[j] = carry;
+ }
+
+ return new BigInteger(partial, 1, CONSTRUCT);
+};
+
+/*
+ Function: square
+ Multiply a by itself.
+
+ This is slightly faster than regular multiplication, since it removes the
+ duplicated multiplcations.
+
+ Returns:
+
+ > this.multiply(this)
+
+ See Also:
+
+*/
+BigInteger.prototype.square = function() {
+ // Normally, squaring a 10-digit number would take 100 multiplications.
+ // Of these 10 are unique diagonals, of the remaining 90 (100-10), 45 are repeated.
+ // This procedure saves (N*(N-1))/2 multiplications, (e.g., 45 of 100 multiplies).
+ // Based on code by Gary Darby, Intellitech Systems Inc., www.DelphiForFun.org
+
+ if (this._s === 0) {
+ return ZERO;
+ }
+ if (this.isUnit()) {
+ return ONE;
+ }
+
+ var digits = this._d;
+ var length = digits.length;
+ var imult1 = new Array(length + length + 1);
+ var product, carry, k;
+ var i;
+
+ // Calculate diagonal
+ for (i = 0; i < length; i++) {
+ k = i * 2;
+ product = digits[i] * digits[i];
+ carry = (product / BigInteger_base) | 0;
+ imult1[k] = product % BigInteger_base;
+ imult1[k + 1] = carry;
+ }
+
+ // Calculate repeating part
+ for (i = 0; i < length; i++) {
+ carry = 0;
+ k = i * 2 + 1;
+ for (var j = i + 1; j < length; j++, k++) {
+ product = digits[j] * digits[i] * 2 + imult1[k] + carry;
+ carry = (product / BigInteger_base) | 0;
+ imult1[k] = product % BigInteger_base;
+ }
+ k = length + i;
+ var digit = carry + imult1[k];
+ carry = (digit / BigInteger_base) | 0;
+ imult1[k] = digit % BigInteger_base;
+ imult1[k + 1] += carry;
+ }
+
+ return new BigInteger(imult1, 1, CONSTRUCT);
+};
+
+/*
+ Function: quotient
+ Divide two and truncate towards zero.
+
+ throws an exception if *n* is zero.
+
+ Parameters:
+
+ n - The number to divide *this* by. Will be converted to a .
+
+ Returns:
+
+ The *this* / *n*, truncated to an integer.
+
+ See Also:
+
+ , , , ,
+*/
+BigInteger.prototype.quotient = function(n) {
+ return this.divRem(n)[0];
+};
+
+/*
+ Function: divide
+ Deprecated synonym for .
+*/
+BigInteger.prototype.divide = BigInteger.prototype.quotient;
+
+/*
+ Function: remainder
+ Calculate the remainder of two .
+
+ throws an exception if *n* is zero.
+
+ Parameters:
+
+ n - The remainder after *this* is divided *this* by *n*. Will be
+ converted to a .
+
+ Returns:
+
+ *this* % *n*.
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.remainder = function(n) {
+ return this.divRem(n)[1];
+};
+
+/*
+ Function: divRem
+ Calculate the integer quotient and remainder of two .
+
+ throws an exception if *n* is zero.
+
+ Parameters:
+
+ n - The number to divide *this* by. Will be converted to a .
+
+ Returns:
+
+ A two-element array containing the quotient and the remainder.
+
+ > a.divRem(b)
+
+ is exactly equivalent to
+
+ > [a.quotient(b), a.remainder(b)]
+
+ except it is faster, because they are calculated at the same time.
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.divRem = function(n) {
+ n = BigInteger(n);
+ if (n._s === 0) {
+ throw new Error("Divide by zero");
+ }
+ if (this._s === 0) {
+ return [ZERO, ZERO];
+ }
+ if (n._d.length === 1) {
+ return this.divRemSmall(n._s * n._d[0]);
+ }
+
+ // Test for easy cases -- |n1| <= |n2|
+ switch (this.compareAbs(n)) {
+ case 0: // n1 == n2
+ return [this._s === n._s ? ONE : M_ONE, ZERO];
+ case -1: // |n1| < |n2|
+ return [ZERO, this];
+ }
+
+ var sign = this._s * n._s;
+ var a = n.abs();
+ var b_digits = this._d;
+ var b_index = b_digits.length;
+ var digits = n._d.length;
+ var quot = [];
+ var guess;
+
+ var part = new BigInteger([], 0, CONSTRUCT);
+ part._s = 1;
+
+ while (b_index) {
+ part._d.unshift(b_digits[--b_index]);
+
+ if (part.compareAbs(n) < 0) {
+ quot.push(0);
+ continue;
+ }
+ if (part._s === 0) {
+ guess = 0;
+ }
+ else {
+ var xlen = part._d.length, ylen = a._d.length;
+ var highx = part._d[xlen-1]*BigInteger_base + part._d[xlen-2];
+ var highy = a._d[ylen-1]*BigInteger_base + a._d[ylen-2];
+ if (part._d.length > a._d.length) {
+ // The length of part._d can either match a._d length,
+ // or exceed it by one.
+ highx = (highx+1)*BigInteger_base;
+ }
+ guess = Math.ceil(highx/highy);
+ }
+ do {
+ var check = a.multiplySingleDigit(guess);
+ if (check.compareAbs(part) <= 0) {
+ break;
+ }
+ guess--;
+ } while (guess);
+
+ quot.push(guess);
+ if (!guess) {
+ continue;
+ }
+ var diff = part.subtract(check);
+ part._d = diff._d.slice();
+ if (part._d.length === 0) {
+ part._s = 0;
+ }
+ }
+
+ return [new BigInteger(quot.reverse(), sign, CONSTRUCT),
+ new BigInteger(part._d, this._s, CONSTRUCT)];
+};
+
+// Throws an exception if n is outside of (-BigInteger.base, -1] or
+// [1, BigInteger.base). It's not necessary to call this, since the
+// other division functions will call it if they are able to.
+BigInteger.prototype.divRemSmall = function(n) {
+ var r;
+ n = +n;
+ if (n === 0) {
+ throw new Error("Divide by zero");
+ }
+
+ var n_s = n < 0 ? -1 : 1;
+ var sign = this._s * n_s;
+ n = Math.abs(n);
+
+ if (n < 1 || n >= BigInteger_base) {
+ throw new Error("Argument out of range");
+ }
+
+ if (this._s === 0) {
+ return [ZERO, ZERO];
+ }
+
+ if (n === 1 || n === -1) {
+ return [(sign === 1) ? this.abs() : new BigInteger(this._d, sign, CONSTRUCT), ZERO];
+ }
+
+ // 2 <= n < BigInteger_base
+
+ // divide a single digit by a single digit
+ if (this._d.length === 1) {
+ var q = new BigInteger([(this._d[0] / n) | 0], 1, CONSTRUCT);
+ r = new BigInteger([(this._d[0] % n) | 0], 1, CONSTRUCT);
+ if (sign < 0) {
+ q = q.negate();
+ }
+ if (this._s < 0) {
+ r = r.negate();
+ }
+ return [q, r];
+ }
+
+ var digits = this._d.slice();
+ var quot = new Array(digits.length);
+ var part = 0;
+ var diff = 0;
+ var i = 0;
+ var guess;
+
+ while (digits.length) {
+ part = part * BigInteger_base + digits[digits.length - 1];
+ if (part < n) {
+ quot[i++] = 0;
+ digits.pop();
+ diff = BigInteger_base * diff + part;
+ continue;
+ }
+ if (part === 0) {
+ guess = 0;
+ }
+ else {
+ guess = (part / n) | 0;
+ }
+
+ var check = n * guess;
+ diff = part - check;
+ quot[i++] = guess;
+ if (!guess) {
+ digits.pop();
+ continue;
+ }
+
+ digits.pop();
+ part = diff;
+ }
+
+ r = new BigInteger([diff], 1, CONSTRUCT);
+ if (this._s < 0) {
+ r = r.negate();
+ }
+ return [new BigInteger(quot.reverse(), sign, CONSTRUCT), r];
+};
+
+/*
+ Function: isEven
+ Return true iff *this* is divisible by two.
+
+ Note that is even.
+
+ Returns:
+
+ true if *this* is even, false otherwise.
+
+ See Also:
+
+
+*/
+BigInteger.prototype.isEven = function() {
+ var digits = this._d;
+ return this._s === 0 || digits.length === 0 || (digits[0] % 2) === 0;
+};
+
+/*
+ Function: isOdd
+ Return true iff *this* is not divisible by two.
+
+ Returns:
+
+ true if *this* is odd, false otherwise.
+
+ See Also:
+
+
+*/
+BigInteger.prototype.isOdd = function() {
+ return !this.isEven();
+};
+
+/*
+ Function: sign
+ Get the sign of a .
+
+ Returns:
+
+ * -1 if *this* < 0
+ * 0 if *this* == 0
+ * +1 if *this* > 0
+
+ See Also:
+
+ , , , ,
+*/
+BigInteger.prototype.sign = function() {
+ return this._s;
+};
+
+/*
+ Function: isPositive
+ Return true iff *this* > 0.
+
+ Returns:
+
+ true if *this*.compare() == 1.
+
+ See Also:
+
+ , , , , ,
+*/
+BigInteger.prototype.isPositive = function() {
+ return this._s > 0;
+};
+
+/*
+ Function: isNegative
+ Return true iff *this* < 0.
+
+ Returns:
+
+ true if *this*.compare() == -1.
+
+ See Also:
+
+ , , , , ,
+*/
+BigInteger.prototype.isNegative = function() {
+ return this._s < 0;
+};
+
+/*
+ Function: isZero
+ Return true iff *this* == 0.
+
+ Returns:
+
+ true if *this*.compare() == 0.
+
+ See Also:
+
+ , , , ,
+*/
+BigInteger.prototype.isZero = function() {
+ return this._s === 0;
+};
+
+/*
+ Function: exp10
+ Multiply a by a power of 10.
+
+ This is equivalent to, but faster than
+
+ > if (n >= 0) {
+ > return this.multiply(BigInteger("1e" + n));
+ > }
+ > else { // n <= 0
+ > return this.quotient(BigInteger("1e" + -n));
+ > }
+
+ Parameters:
+
+ n - The power of 10 to multiply *this* by. *n* is converted to a
+ javascipt number and must be no greater than
+ (0x7FFFFFFF), or an exception will be thrown.
+
+ Returns:
+
+ *this* * (10 ** *n*), truncated to an integer if necessary.
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.exp10 = function(n) {
+ n = +n;
+ if (n === 0) {
+ return this;
+ }
+ if (Math.abs(n) > Number(MAX_EXP)) {
+ throw new Error("exponent too large in BigInteger.exp10");
+ }
+ if (n > 0) {
+ var k = new BigInteger(this._d.slice(), this._s, CONSTRUCT);
+
+ for (; n >= BigInteger_base_log10; n -= BigInteger_base_log10) {
+ k._d.unshift(0);
+ }
+ if (n == 0)
+ return k;
+ k._s = 1;
+ k = k.multiplySingleDigit(Math.pow(10, n));
+ return (this._s < 0 ? k.negate() : k);
+ } else if (-n >= this._d.length*BigInteger_base_log10) {
+ return ZERO;
+ } else {
+ var k = new BigInteger(this._d.slice(), this._s, CONSTRUCT);
+
+ for (n = -n; n >= BigInteger_base_log10; n -= BigInteger_base_log10) {
+ k._d.shift();
+ }
+ return (n == 0) ? k : k.divRemSmall(Math.pow(10, n))[0];
+ }
+};
+
+/*
+ Function: pow
+ Raise a to a power.
+
+ In this implementation, 0**0 is 1.
+
+ Parameters:
+
+ n - The exponent to raise *this* by. *n* must be no greater than
+ (0x7FFFFFFF), or an exception will be thrown.
+
+ Returns:
+
+ *this* raised to the *nth* power.
+
+ See Also:
+
+
+*/
+BigInteger.prototype.pow = function(n) {
+ if (this.isUnit()) {
+ if (this._s > 0) {
+ return this;
+ }
+ else {
+ return BigInteger(n).isOdd() ? this : this.negate();
+ }
+ }
+
+ n = BigInteger(n);
+ if (n._s === 0) {
+ return ONE;
+ }
+ else if (n._s < 0) {
+ if (this._s === 0) {
+ throw new Error("Divide by zero");
+ }
+ else {
+ return ZERO;
+ }
+ }
+ if (this._s === 0) {
+ return ZERO;
+ }
+ if (n.isUnit()) {
+ return this;
+ }
+
+ if (n.compareAbs(MAX_EXP) > 0) {
+ throw new Error("exponent too large in BigInteger.pow");
+ }
+ var x = this;
+ var aux = ONE;
+ var two = BigInteger.small[2];
+
+ while (n.isPositive()) {
+ if (n.isOdd()) {
+ aux = aux.multiply(x);
+ if (n.isUnit()) {
+ return aux;
+ }
+ }
+ x = x.square();
+ n = n.quotient(two);
+ }
+
+ return aux;
+};
+
+/*
+ Function: modPow
+ Raise a to a power (mod m).
+
+ Because it is reduced by a modulus, is not limited by
+ like .
+
+ Parameters:
+
+ exponent - The exponent to raise *this* by. Must be positive.
+ modulus - The modulus.
+
+ Returns:
+
+ *this* ^ *exponent* (mod *modulus*).
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.modPow = function(exponent, modulus) {
+ var result = ONE;
+ var base = this;
+
+ while (exponent.isPositive()) {
+ if (exponent.isOdd()) {
+ result = result.multiply(base).remainder(modulus);
+ }
+
+ exponent = exponent.quotient(BigInteger.small[2]);
+ if (exponent.isPositive()) {
+ base = base.square().remainder(modulus);
+ }
+ }
+
+ return result;
+};
+
+/*
+ Function: log
+ Get the natural logarithm of a as a native JavaScript number.
+
+ This is equivalent to
+
+ > Math.log(this.toJSValue())
+
+ but handles values outside of the native number range.
+
+ Returns:
+
+ log( *this* )
+
+ See Also:
+
+
+*/
+BigInteger.prototype.log = function() {
+ switch (this._s) {
+ case 0: return -Infinity;
+ case -1: return NaN;
+ default: // Fall through.
+ }
+
+ var l = this._d.length;
+
+ if (l*BigInteger_base_log10 < 30) {
+ return Math.log(this.valueOf());
+ }
+
+ var N = Math.ceil(30/BigInteger_base_log10);
+ var firstNdigits = this._d.slice(l - N);
+ return Math.log((new BigInteger(firstNdigits, 1, CONSTRUCT)).valueOf()) + (l - N) * Math.log(BigInteger_base);
+};
+
+/*
+ Function: valueOf
+ Convert a to a native JavaScript integer.
+
+ This is called automatically by JavaScipt to convert a to a
+ native value.
+
+ Returns:
+
+ > parseInt(this.toString(), 10)
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.valueOf = function() {
+ return parseInt(this.toString(), 10);
+};
+
+/*
+ Function: toJSValue
+ Convert a to a native JavaScript integer.
+
+ This is the same as valueOf, but more explicitly named.
+
+ Returns:
+
+ > parseInt(this.toString(), 10)
+
+ See Also:
+
+ ,
+*/
+BigInteger.prototype.toJSValue = function() {
+ return parseInt(this.toString(), 10);
+};
+
+var MAX_EXP = BigInteger(0x7FFFFFFF);
+// Constant: MAX_EXP
+// The largest exponent allowed in and (0x7FFFFFFF or 2147483647).
+BigInteger.MAX_EXP = MAX_EXP;
+
+(function() {
+ function makeUnary(fn) {
+ return function(a) {
+ return fn.call(BigInteger(a));
+ };
+ }
+
+ function makeBinary(fn) {
+ return function(a, b) {
+ return fn.call(BigInteger(a), BigInteger(b));
+ };
+ }
+
+ function makeTrinary(fn) {
+ return function(a, b, c) {
+ return fn.call(BigInteger(a), BigInteger(b), BigInteger(c));
+ };
+ }
+
+ (function() {
+ var i, fn;
+ var unary = "toJSValue,isEven,isOdd,sign,isZero,isNegative,abs,isUnit,square,negate,isPositive,toString,next,prev,log".split(",");
+ var binary = "compare,remainder,divRem,subtract,add,quotient,divide,multiply,pow,compareAbs".split(",");
+ var trinary = ["modPow"];
+
+ for (i = 0; i < unary.length; i++) {
+ fn = unary[i];
+ BigInteger[fn] = makeUnary(BigInteger.prototype[fn]);
+ }
+
+ for (i = 0; i < binary.length; i++) {
+ fn = binary[i];
+ BigInteger[fn] = makeBinary(BigInteger.prototype[fn]);
+ }
+
+ for (i = 0; i < trinary.length; i++) {
+ fn = trinary[i];
+ BigInteger[fn] = makeTrinary(BigInteger.prototype[fn]);
+ }
+
+ BigInteger.exp10 = function(x, n) {
+ return BigInteger(x).exp10(n);
+ };
+ })();
+})();
+
+exports.BigInteger = BigInteger;
+})(biginteger);
diff --git a/ui.js b/ui.js
new file mode 100644
index 0000000..adc4275
--- /dev/null
+++ b/ui.js
@@ -0,0 +1,380 @@
+// This code was lifted from http://www.squarefree.com/shell/shell.html
+// Probably originally by Jesse Ruderman.
+
+var
+ histList = [""],
+ histPos = 0,
+ _win, // a top-level context
+ question,
+ _in,
+ _out,
+ tooManyMatches = null,
+ lastError = null;
+
+function refocus() {
+ _in.blur(); // Needed for Mozilla to scroll correctly.
+ _in.focus();
+}
+
+var mode = 'calculator';
+
+function init() {
+ _in = document.getElementById("input");
+ _out = document.getElementById("output");
+ _win = window;
+ _win.Shell = window;
+ recalculateInputHeight();
+ refocus();
+
+ var sourceCode = document.getElementById("sourcecode");
+ function setMode(newMode) {
+ mode = newMode;
+
+ while (sourceCode.lastChild)
+ sourceCode.removeChild(sourceCode.lastChild);
+ sourceCode.appendChild(document.createTextNode(parseModes[mode].toString()));
+ }
+
+ setMode('calc');
+
+ var radios = document.getElementsByTagName("input");
+ for (var i = 0; i < radios.length; i++) {
+ var e = radios[i];
+ if (e.type == "radio") {
+ e.addEventListener("click", function () {
+ setMode(this.value);
+ setTimeout(refocus, 0);
+ return true;
+ });
+ if (e.value == 'calc')
+ e.checked = true;
+ }
+ }
+}
+
+// Unless the user is selected something, refocus the textbox.
+// (requested by caillon, brendan, asa)
+function keepFocusInTextbox(e) {
+ var g = e.srcElement ? e.srcElement : e.target; // IE vs. standard
+
+ while (!g.tagName)
+ g = g.parentNode;
+ var t = g.tagName.toUpperCase();
+ if (t=="A" || t=="INPUT")
+ return;
+
+ if (window.getSelection) {
+ // Mozilla
+ if (String(window.getSelection()))
+ return;
+ } else if (document.getSelection) {
+ // Opera? Netscape 4?
+ if (document.getSelection())
+ return;
+ } else {
+ // IE
+ if ( document.selection.createRange().text )
+ return;
+ }
+
+ refocus();
+}
+
+function inputKeydown(e) {
+ // Use onkeydown because IE doesn't support onkeypress for arrow keys
+
+ //alert(e.keyCode + " ^ " + e.keycode);
+
+ if (e.shiftKey && e.keyCode == 13) { // shift-enter
+ // don't do anything; allow the shift-enter to insert a line break as normal
+ } else if (e.keyCode == 13) { // enter
+ // execute the input on enter
+ try { go(); } catch(er) { alert(er); };
+ setTimeout(function() { _in.value = ""; }, 0); // can't preventDefault on input, so clear it later
+ } else if (e.keyCode == 38) { // up
+ // go up in history if at top or ctrl-up
+ if (e.ctrlKey || caretInFirstLine(_in))
+ hist(true);
+ } else if (e.keyCode == 40) { // down
+ // go down in history if at end or ctrl-down
+ if (e.ctrlKey || caretInLastLine(_in))
+ hist(false);
+ } else if (e.keyCode == 9) { // tab
+ setTimeout(function() { refocus(); }, 0); // refocus because tab was hit
+ } else { }
+
+ setTimeout(recalculateInputHeight, 0);
+
+ //return true;
+}
+
+function caretInFirstLine(textbox) {
+ // IE doesn't support selectionStart/selectionEnd
+ if (textbox.selectionStart == undefined)
+ return true;
+
+ var firstLineBreak = textbox.value.indexOf("\n");
+
+ return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
+}
+
+function caretInLastLine(textbox) {
+ // IE doesn't support selectionStart/selectionEnd
+ if (textbox.selectionEnd == undefined)
+ return true;
+
+ var lastLineBreak = textbox.value.lastIndexOf("\n");
+
+ return (textbox.selectionEnd > lastLineBreak);
+}
+
+function recalculateInputHeight() {
+ var rows = _in.value.split(/\n/).length
+ + 1 // prevent scrollbar flickering in Mozilla
+ + (window.opera ? 1 : 0); // leave room for scrollbar in Opera
+
+ if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
+ _in.rows = rows;
+}
+
+function writeNode(type, node) {
+ var newdiv = document.createElement("div");
+ newdiv.className = type;
+ newdiv.appendChild(node);
+ _out.appendChild(newdiv);
+ return newdiv;
+}
+
+function println(s, type) {
+ s = String(s);
+ if (s)
+ return writeNode(type, document.createTextNode(s));
+}
+
+function printWithRunin(h, s, type) {
+ var div = println(s, type);
+ var head = document.createElement("strong");
+ head.appendChild(document.createTextNode(h + ": "));
+ div.insertBefore(head, div.firstChild);
+}
+
+function hist(up) {
+ // histList[0] = first command entered, [1] = second, etc.
+ // type something, press up --> thing typed is now in "limbo"
+ // (last item in histList) and should be reachable by pressing
+ // down again.
+
+ var L = histList.length;
+
+ if (L == 1)
+ return;
+
+ if (up) {
+ if (histPos == L-1) {
+ // Save this entry in case the user hits the down key.
+ histList[histPos] = _in.value;
+ }
+
+ if (histPos > 0) {
+ histPos--;
+ // Use a timeout to prevent up from moving cursor within new text
+ // Set to nothing first for the same reason
+ setTimeout(
+ function() {
+ _in.value = '';
+ _in.value = histList[histPos];
+ var caretPos = _in.value.length;
+ if (_in.setSelectionRange)
+ _in.setSelectionRange(caretPos, caretPos);
+ },
+ 0
+ );
+ }
+ } else { // down
+ if (histPos < L-1) {
+ histPos++;
+ _in.value = histList[histPos];
+ } else if (histPos == L-1) {
+ // Already on the current entry: clear but save
+ if (_in.value) {
+ histList[histPos] = _in.value;
+ ++histPos;
+ _in.value = "";
+ }
+ }
+ }
+}
+
+function printQuestion(q) {
+ println(q, "input");
+}
+
+function printAnswer(a) {
+ if (a !== undefined)
+ println(a, "normalOutput");
+}
+
+function printError(er) {
+ var lineNumberString;
+
+ lastError = er; // for debugging the shell
+ if (er.name) {
+ // lineNumberString should not be "", to avoid a very wacky bug in IE 6.
+ lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": ";
+ println(er.name + lineNumberString + er.message, "error"); // Because IE doesn't have error.toString.
+ } else {
+ println(er, "error"); // Because security errors in Moz /only/ have toString.
+ }
+}
+
+// Example: Taylor series for sin:
+// x * (1 - x*x/(2*3) * (1 - x*x/(4*5) * (1 - x*x/(6*7) * (1 - x*x/(8*9) * (1 - x*x/(10*11) * (1 - x*x/(12*13)))))))
+function showPlot(fn) {
+ var c = document.createElement('canvas');
+ c.width = 600;
+ c.height = 400;
+ writeNode("normalOutput", c);
+ var ctx = c.getContext("2d");
+
+ var rawData = [], labels = [];
+ for (var i = 0; i <= 100; i++) {
+ var x = (i - 50) / 20;
+ rawData[i] = fn(x);
+ if (i % 10 == 0)
+ labels[i] = x;
+ else
+ labels[i] = "";
+ }
+
+ var chart = new Chart(ctx).Line({
+ labels: labels,
+ datasets: [
+ {
+ fillColor : "rgba(151,187,205,0.5)",
+ strokeColor : "rgba(151,187,205,1)",
+ data : rawData
+ }
+ ]
+ }, {
+ bezierCurve: false,
+ pointDot: false,
+ animation: false
+ });
+}
+
+function showComplexPlot(fn) {
+ var WIDTH = 401, HEIGHT = 401;
+ var c = document.createElement('canvas');
+ c.width = WIDTH;
+ c.height = HEIGHT;
+ writeNode("normalOutput", c);
+ var ctx = c.getContext('2d');
+ var imageData = ctx.getImageData(0, 0, WIDTH, HEIGHT);
+ var arr = imageData.data;
+
+ var i = 0;
+ for (var yi = 0; yi < HEIGHT; yi++) {
+ var z_im = ((HEIGHT - 1) / 2 - yi) * (6 / HEIGHT);
+ for (var xi = 0; xi < WIDTH; xi++) {
+ var z_re = (xi - (WIDTH - 1) / 2) * (6 / WIDTH);
+ var result = fn(z_re, z_im);
+
+ var rabs = Math.sqrt(result.re*result.re + result.im*result.im);
+
+ var h = Math.atan2(result.im, result.re) / (2*Math.PI);
+ var s = 1.0;
+ var l;
+ if (result.re !== result.re || result.im !== result.im) {
+ l = 1;
+ } else {
+ l = 0.5 + Math.log(rabs + 0.5) / 3;
+ if (l > 1)
+ l = 1;
+ else if (l < 0)
+ l = 0;
+ }
+
+ var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
+ var m1 = l * 2 - m2;
+
+ function hue(h) {
+ h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
+ if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
+ else if (h * 2 < 1) return m2;
+ else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
+ else return m1;
+ }
+
+ arr[i] = Math.round(255 * hue(h + 1/3));
+ arr[i + 1] = Math.round(255 * hue(h));
+ arr[i + 2] = Math.round(255 * hue(h - 1/3));
+ arr[i + 3] = 255;
+ i += 4;
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+}
+
+
+var mode = 'calc';
+
+function go() {
+ question = _in.value;
+
+ if (question == "")
+ return;
+
+ histList[histList.length-1] = question;
+ histList[histList.length] = "";
+ histPos = histList.length - 1;
+
+ _in.value='';
+ recalculateInputHeight();
+ printQuestion(question);
+
+ if (_win.closed) {
+ printError("Target window has been closed.");
+ return;
+ }
+
+ if (!("Shell" in _win))
+ initTarget(); // silent
+
+ try {
+ var result = parseModes[mode](question);
+ switch (mode) {
+ case 'calc':
+ Shell.printAnswer(result);
+ break;
+
+ case 'fraction':
+ Shell.printAnswer(result);
+ break;
+
+ case 'blocks':
+ writeNode("normalOutput", result);
+ break;
+
+ case 'json':
+ Shell.printAnswer(JSON.stringify(result, undefined, " "));
+ break;
+
+ case 'mathml':
+ writeNode("normalOutput", result.element);
+ break;
+
+ case 'graph':
+ showPlot(result);
+ break;
+
+ case 'complex':
+ showComplexPlot(result);
+ break;
+ }
+ } catch (exc) {
+ Shell.printError(exc);
+ }
+ setTimeout(Shell.refocus, 0);
+}
+