Created
November 18, 2015 19:59
-
-
Save zootella/36a5d2d71d0ac8183773 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // A 0+ integer of unlimited size | |
| function Int(p) { // Takes a number like 5, a string of numerals like "789", a bignumber.js BigNumber, or another Int | |
| if (isType(p, "Int")) return p; // Return the given Int instead of making a new one, the value inside an Int can't change | |
| var o = {}; | |
| o.v = _3type(p); // Parse the given parameter, keeping together v.s numerals, and v.n number and v.b BigNumber once we have them or they are necessary | |
| o._ = function(c, q) { return _calculate(o.v, c, _3type(q)); } // Who says JavaScript can't do operator overloading? | |
| o.text = o.v.s(); | |
| o.hasNumber = function() { return o.v.fit; } // True if our value is small enough it will fit in a number as an integer, not a floating point number | |
| o.toNumber = function() { return o.v.n(); } // Throws if too big | |
| o.type = "Int"; | |
| return freeze(o); | |
| } | |
| function _3type(p) { // Parse the parameter given to Int or a method on Int, keeping the same integer value in up to 3 different types | |
| var type = getType(p); | |
| if (type == "Int") return p.v; // We got an Int, return the value inside instead of making a new one | |
| // Hold the same integer value 1, 2 or 3 different ways, keeping the type we were given, only converting when necessary, and checking everything we can with the types we have | |
| var b = "none"; // Our integer value in a BigNumber object, or "none" before we have one | |
| var n = "none"; // Our integer value in a number type variable, or "none" before we have one, or if our value won't fit | |
| var s = "none"; // Our integer value as a string of numerals, we always have this type | |
| if (hasMethod(p, "dividedToIntegerBy")) { b = p; s = p.toFixed(0); checkNumerals(s); } // Given a BigNumber, make and check numerals | |
| else if (type == "number") { n = p; checkNumberMath(n); s = n+""; checkNumerals(s); checkNumeralsFit(s); } // Given a number, check it, make numerals, and check them | |
| else if (type == "string") { s = p; checkNumerals(s); } // Given numerals, check them | |
| else { toss("type"); } // Int(p).method(p) only works accepts p as an Int, BigNumber, number, or string | |
| var o = {}; // Return, or make, check, keep, and return, our value in a BigNumber, number, or string ♫ There's three ways of saying, the very same thing | |
| o.b = function() { if (b !== "none") return b; b = new platformBigNumber(s); checkSame(s, b.toFixed(0)); return b; } // BigNumber, make it from numerals rather than a number to avoid a separate lower size limit | |
| o.n = function() { if (n !== "none") return n; n = numeralsToNumber(s); return n; } // number | |
| o.s = function() { return s; } // string of numerals | |
| o.fit = numeralsFit(s); // True if you can call o.n() to get our value as a number, because it's small enough to fit | |
| o.bs = function() { return b !== "none" ? b : s; } // Our value in a BigNumber if we have one, numerals otherwise | |
| return o; | |
| } | |
| function _calculate(v, c, w) { | |
| // For small values, use number for speed For potentially large values, use BigNumber instead | |
| if (c == "+") { if (_bothFitProduct(v, w)) return Int(v.n() + w.n()); else return Int(v.b().plus( w.bs())); } | |
| else if (c == "-") { _checkSubtract(v, w); if (_bothFit(v, w)) return Int(v.n() - w.n()); else return Int(v.b().minus( w.bs())); } | |
| else if (c == "*") { if (_bothFitProduct(v, w)) return Int(v.n() * w.n()); else return Int(v.b().times( w.bs())); } | |
| else if (c == "/") { _checkDivide(v, w); if (_bothFit(v, w)) return Int(Math.floor(v.n() / w.n())); else return Int(v.b().dividedToIntegerBy( w.bs())); } | |
| else if (c == "%") { _checkDivide(v, w); if (_bothFit(v, w)) return Int(v.n() % w.n()); else return Int(v.b().mod( w.bs())); } | |
| else if (c == "==") { if (_bothFit(v, w)) return v.n() == w.n(); else return v.b().equals( w.bs()); } | |
| else if (c == ">") { if (_bothFit(v, w)) return v.n() > w.n(); else return v.b().greaterThan( w.bs()); } | |
| else if (c == ">=") { if (_bothFit(v, w)) return v.n() >= w.n(); else return v.b().greaterThanOrEqualTo( w.bs()); } | |
| else if (c == "<") { if (_bothFit(v, w)) return v.n() < w.n(); else return v.b().lessThan( w.bs()); } | |
| else if (c == "<=") { if (_bothFit(v, w)) return v.n() <= w.n(); else return v.b().lessThanOrEqualTo( w.bs()); } | |
| else { toss("code"); } | |
| } | |
| function _checkSubtract(v, w) { if (compareCheckedNumerals(v.s(), w.s()) < 0) toss("bounds"); } // Make sure v - w will be 0+, as negative values aren't allowed | |
| function _checkDivide(v, w) { if (w.s() == "0") toss("math"); } // Who says you can't divide by zero? OH SHI- | |
| function _bothFit(v, w) { return v.fit && w.fit; } // True if both values will fit in numbers, so we can use number operators that produce smaller values like minus, divide, and modulo | |
| function _bothFitProduct(v, w) { // True if adding or multipling the given two numbers can't produce an answer that's too big | |
| return _bothFit(v, w) && v.s().length + w.s().length < (Number.MAX_SAFE_INTEGER+"").length; // Even if v and w are all 9s, a*b will still be a digit shorter than max safe integer | |
| } | |
| exports.Int = Int; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment