diff --git a/BigInteger.d.ts b/BigInteger.d.ts index 4709e0a..76b8409 100644 --- a/BigInteger.d.ts +++ b/BigInteger.d.ts @@ -23,8 +23,11 @@ declare namespace bigInt { /** * Parse a string into a bigInt. + * Default base is 10. + * Default alphabet is "0123456789abcdefghijklmnopqrstuvwxyz". + * caseSensitive defaults to false. */ - (string: string, base?: BigNumber): BigInteger; + (string: string, base?: BigNumber, alphabet?: string, caseSensitive?: boolean): BigInteger; /** * no-op. @@ -66,12 +69,12 @@ declare namespace bigInt { /** * Equivalent to bigInt(-1). */ - minusOne: BigInteger; + minusOne: BigInteger; /** * Equivalent to bigInt(1). */ - one: BigInteger; + one: BigInteger; /** * Returns a random number between min and max. @@ -130,7 +133,7 @@ declare namespace bigInt { * Performs division and returns an object with two properties: quotient and remainder. * The sign of the remainder will match the sign of the dividend. */ - divmod(number: BigNumber): {quotient: BigInteger, remainder: BigInteger}; + divmod(number: BigNumber): { quotient: BigInteger, remainder: BigInteger }; /** * Alias for the equals method. @@ -346,7 +349,7 @@ declare namespace bigInt { * * Converts a bigInt to an object representing it as an array of integers module the given radix. */ - toArray(radix: number): BaseArray; + toArray(radix: number): BaseArray; /** * Converts a bigInt into a native Javascript number. Loses precision for numbers outside the range. @@ -357,7 +360,7 @@ declare namespace bigInt { * Converts a bigInt to a string. */ toString(radix?: number): string; - + /** * Converts a bigInt to a string. This method is called behind the scenes in JSON.stringify. */ diff --git a/BigInteger.js b/BigInteger.js index 46a7bcc..1fa9c23 100644 --- a/BigInteger.js +++ b/BigInteger.js @@ -5,13 +5,13 @@ var bigInt = (function (undefined) { LOG_BASE = 7, MAX_INT = 9007199254740992, MAX_INT_ARR = smallToArray(MAX_INT), - LOG_MAX_INT = Math.log(MAX_INT); + DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; var supportsNativeBigInt = typeof BigInt === "function"; - function Integer(v, radix) { + function Integer(v, radix, alphabet, caseSensitive) { if (typeof v === "undefined") return Integer[0]; - if (typeof radix !== "undefined") return +radix === 10 ? parseValue(v) : parseBase(v, radix); + if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); return parseValue(v); } @@ -1164,42 +1164,40 @@ var bigInt = (function (undefined) { } return low.add(Integer.fromArray(result, BASE, false)); } - var parseBase = function (text, base) { + + var parseBase = function (text, base, alphabet, caseSensitive) { + alphabet = alphabet || DEFAULT_ALPHABET; + text = String(text); + if (!caseSensitive) { + text = text.toLowerCase(); + alphabet = alphabet.toLowerCase(); + } var length = text.length; var i; var absBase = Math.abs(base); - for (var i = 0; i < length; i++) { - var c = text[i].toLowerCase(); + var alphabetValues = {}; + for (i = 0; i < alphabet.length; i++) { + alphabetValues[alphabet[i]] = i; + } + for (i = 0; i < length; i++) { + var c = text[i]; if (c === "-") continue; - if (/[a-z0-9]/.test(c)) { - if (/[0-9]/.test(c) && +c >= absBase) { + if (c in alphabetValues) { + if (alphabetValues[c] >= absBase) { if (c === "1" && absBase === 1) continue; throw new Error(c + " is not a valid digit in base " + base + "."); - } else if (c.charCodeAt(0) - 87 >= absBase) { - throw new Error(c + " is not a valid digit in base " + base + "."); - } - } - } - if (2 <= base && base <= 36) { - if (length <= LOG_MAX_INT / Math.log(base)) { - var result = parseInt(text, base); - if (isNaN(result)) { - throw new Error(c + " is not a valid digit in base " + base + "."); } - return parseValue(parseInt(text, base)); } } base = parseValue(base); var digits = []; var isNegative = text[0] === "-"; for (i = isNegative ? 1 : 0; i < text.length; i++) { - var c = text[i].toLowerCase(), - charCode = c.charCodeAt(0); - if (48 <= charCode && charCode <= 57) digits.push(parseValue(c)); - else if (97 <= charCode && charCode <= 122) digits.push(parseValue(c.charCodeAt(0) - 87)); + var c = text[i]; + if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); else if (c === "<") { var start = i; - do { i++; } while (text[i] !== ">"); + do { i++; } while (text[i] !== ">" && i < text.length); digits.push(parseValue(text.slice(start + 1, i))); } else throw new Error(c + " is not a valid character"); @@ -1216,9 +1214,10 @@ var bigInt = (function (undefined) { return isNegative ? val.negate() : val; } - function stringify(digit) { - if (digit <= 35) { - return "0123456789abcdefghijklmnopqrstuvwxyz".charAt(digit); + function stringify(digit, alphabet) { + alphabet = alphabet || DEFAULT_ALPHABET; + if (digit < alphabet.length) { + return alphabet[digit]; } return "<" + digit + ">"; } @@ -1278,9 +1277,11 @@ var bigInt = (function (undefined) { return { value: out.reverse(), isNegative: neg }; } - function toBaseString(n, base) { + function toBaseString(n, base, alphabet) { var arr = toBase(n, base); - return (arr.isNegative ? "-" : "") + arr.value.map(stringify).join(''); + return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { + return stringify(x, alphabet); + }).join(''); } BigInteger.prototype.toArray = function (radix) { @@ -1295,9 +1296,9 @@ var bigInt = (function (undefined) { return toBase(this, radix); }; - BigInteger.prototype.toString = function (radix) { + BigInteger.prototype.toString = function (radix, alphabet) { if (radix === undefined) radix = 10; - if (radix !== 10) return toBaseString(this, radix); + if (radix !== 10) return toBaseString(this, radix, alphabet); var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; while (--l >= 0) { digit = String(v[l]); @@ -1307,9 +1308,9 @@ var bigInt = (function (undefined) { return sign + str; }; - SmallInteger.prototype.toString = function (radix) { + SmallInteger.prototype.toString = function (radix, alphabet) { if (radix === undefined) radix = 10; - if (radix != 10) return toBaseString(this, radix); + if (radix != 10) return toBaseString(this, radix, alphabet); return String(this.value); }; diff --git a/README.md b/README.md index fe1507a..0901973 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Then you can include it in your code: ## Usage -### `bigInt(number, [base])` +### `bigInt(number, [base], [alphabet], [caseSensitive])` You can create a bigInt by calling the `bigInt` function. You can pass in @@ -36,7 +36,11 @@ You can create a bigInt by calling the `bigInt` function. You can pass in - another bigInt. - nothing, and it will return `bigInt.zero`. - If you provide a second parameter, then it will parse `number` as a number in base `base`. Note that `base` can be any bigInt (even negative or zero). The letters "a-z" and "A-Z" will be interpreted as the numbers 10 to 35. Higher digits can be specified in angle brackets (`<` and `>`). + If you provide a second parameter, then it will parse `number` as a number in base `base`. Note that `base` can be any bigInt (even negative or zero). The letters "a-z" and "A-Z" will be interpreted as the numbers 10 to 35. Higher digits can be specified in angle brackets (`<` and `>`). The default `base` is `10`. + + You can specify a custom alphabet for base conversion with the third parameter. The default `alphabet` is `"0123456789abcdefghijklmnopqrstuvwxyz"`. + + The fourth parameter specifies whether or not the number string should be case-sensitive, i.e. whether `a` and `A` should be treated as different digits. By default `caseSensitive` is `false`. Examples: @@ -520,13 +524,17 @@ Returns a random number between `min` and `max`. ### Override Methods -#### `toString(radix = 10)` +#### `toString(radix = 10, [alphabet])` Converts a bigInt to a string. There is an optional radix parameter (which defaults to 10) that converts the number to the given radix. Digits in the range `10-35` will use the letters `a-z`. - `bigInt("1e9").toString()` => `"1000000000"` - `bigInt("1e9").toString(16)` => `"3b9aca00"` + You can use a custom base alphabet with the second parameter. The default `alphabet` is `"0123456789abcdefghijklmnopqrstuvwxyz"`. + + - `bigInt("5").toString(2, "aA")` => `"AaA"` + **Note that arithmetical operators will trigger the `valueOf` function rather than the `toString` function.** When converting a bigInteger to a string, you should use the `toString` method or the `String` function instead of adding the empty string. - `bigInt("999999999999999999").toString()` => `"999999999999999999"` diff --git a/spec/spec.js b/spec/spec.js index d0071dd..cea9885 100644 --- a/spec/spec.js +++ b/spec/spec.js @@ -1066,6 +1066,13 @@ describe("BigInteger", function () { expect(function () { return bigInt(1).toArray(0); }).toThrow(); }); + + it("allows custom alphabet", function () { + expect(bigInt("9786534201", 10, "9786534201")).toEqualBigInt("0123456789"); + expect(bigInt("bC", 3, "abc")).toEqualBigInt("5"); + expect(bigInt("AAa", 2, "aA", true)).toEqualBigInt("6"); + expect(bigInt("10").toString(2, "Aa")).toEqual("aAaA"); + }); }); describe("Bitwise operations", function () {