Skip to content

Commit

Permalink
allow custom alphabet
Browse files Browse the repository at this point in the history
  • Loading branch information
peterolson committed Dec 3, 2018
1 parent e713654 commit 00b4bcb
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 42 deletions.
15 changes: 9 additions & 6 deletions BigInteger.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
*/
Expand Down
67 changes: 34 additions & 33 deletions BigInteger.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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");
Expand All @@ -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 + ">";
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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]);
Expand All @@ -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);
};

Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:

Expand Down Expand Up @@ -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"`
Expand Down
7 changes: 7 additions & 0 deletions spec/spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 00b4bcb

Please sign in to comment.