[bigint] Implement toString(radix) for any radix

Power-of-two radixes were supported already; this adds all others
(with 2 <= radix <= 36).

Bonus: fix digit_div fallback path for divisors with no leading zeros.

Bug: v8:6791
Change-Id: Id472667f057ad13338e0d8257a899490490e6f8f
Reviewed-on: https://chromium-review.googlesource.com/693316
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48323}
This commit is contained in:
Jakob Kummerow 2017-10-05 15:02:21 -07:00 committed by Commit Bot
parent 8fc10b5af7
commit da13b8971d
4 changed files with 325 additions and 36 deletions

View File

@ -77,6 +77,10 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
HeapNumber::cast(this)->HeapNumberPrint(os);
os << ">\n";
break;
case BIGINT_TYPE:
BigInt::cast(this)->BigIntPrint(os);
os << "\n";
break;
case FIXED_DOUBLE_ARRAY_TYPE:
FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(os);
break;

View File

@ -238,9 +238,14 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
}
MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
// TODO(jkummerow): Support non-power-of-two radixes.
if (!base::bits::IsPowerOfTwo(radix)) radix = 16;
return ToStringBasePowerOfTwo(bigint, radix);
Isolate* isolate = bigint->GetIsolate();
if (bigint->is_zero()) {
return isolate->factory()->NewStringFromStaticChars("0");
}
if (base::bits::IsPowerOfTwo(radix)) {
return ToStringBasePowerOfTwo(bigint, radix);
}
return ToStringGeneric(bigint, radix);
}
void BigInt::Initialize(int length, bool zero_initialize) {
@ -927,7 +932,7 @@ Handle<BigInt> BigInt::Copy(Handle<BigInt> source) {
// base-N string representation of a number. To increase accuracy, the array
// value is the actual value multiplied by 32. To generate this table:
// for (var i = 0; i <= 36; i++) { print(Math.ceil(Math.log2(i) * 32) + ","); }
uint8_t kMaxBitsPerChar[] = {
constexpr uint8_t kMaxBitsPerChar[] = {
0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8
102, 107, 111, 115, 119, 122, 126, 128, // 9..16
131, 134, 136, 139, 141, 143, 145, 147, // 17..24
@ -942,25 +947,16 @@ MaybeHandle<BigInt> BigInt::AllocateFor(Isolate* isolate, int radix,
int charcount) {
DCHECK(2 <= radix && radix <= 36);
DCHECK(charcount >= 0);
size_t bits_min;
size_t bits_per_char = kMaxBitsPerChar[radix];
size_t chars = static_cast<size_t>(charcount);
const int roundup = kBitsPerCharTableMultiplier - 1;
if (chars <= 1000000) {
// More precise path: multiply first, then divide.
bits_min = bits_per_char * chars;
// Divide by 32 (see table), rounding up.
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
} else {
// Overflow avoidance path: divide first, then multiply.
// The addition can't overflow because of the int -> size_t cast.
bits_min = ((chars + roundup) >> kBitsPerCharTableShift) * bits_per_char;
// Check if overflow happened.
if (bits_min < chars) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
if ((std::numeric_limits<size_t>::max() - roundup) / bits_per_char < chars) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
size_t bits_min = bits_per_char * chars;
// Divide by 32 (see table), rounding up.
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
if (bits_min > static_cast<size_t>(kMaxInt)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
@ -991,16 +987,13 @@ void BigInt::RightTrim() {
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// TODO(jkummerow): Add more tests for this when we have a way to construct
// multi-digit BigInts.
MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix) {
STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits));
DCHECK(base::bits::IsPowerOfTwo(radix));
DCHECK(radix >= 2 && radix <= 32);
DCHECK(!x->is_zero());
Isolate* isolate = x->GetIsolate();
// TODO(jkummerow): check in caller?
if (x->is_zero()) return isolate->factory()->NewStringFromStaticChars("0");
const int length = x->length();
const bool sign = x->sign();
@ -1022,6 +1015,7 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked();
DisallowHeapAllocation no_gc;
uint8_t* buffer = result->GetChars();
// Print the number into the string, starting from the last position.
int pos = static_cast<int>(chars_required - 1);
@ -1055,6 +1049,127 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
return result;
}
MaybeHandle<String> BigInt::ToStringGeneric(Handle<BigInt> x, int radix) {
DCHECK(radix >= 2 && radix <= 36);
DCHECK(!x->is_zero());
Heap* heap = x->GetHeap();
Isolate* isolate = heap->isolate();
const int length = x->length();
const bool sign = x->sign();
// Compute (an overapproximation of) the length of the resulting string:
// Divide bit length of the BigInt by bits representable per character.
const size_t bit_length =
length * kDigitBits - base::bits::CountLeadingZeros(x->digit(length - 1));
// Maximum number of bits we can represent with one character. We'll use this
// to find an appropriate chunk size below.
const uint8_t max_bits_per_char = kMaxBitsPerChar[radix];
// For estimating result length, we have to be pessimistic and work with
// the minimum number of bits one character can represent.
const uint8_t min_bits_per_char = max_bits_per_char - 1;
// Perform the following computation with uint64_t to avoid overflows.
uint64_t chars_required = bit_length;
chars_required *= kBitsPerCharTableMultiplier;
chars_required += min_bits_per_char - 1; // Round up.
chars_required /= min_bits_per_char;
chars_required += sign;
if (chars_required > String::kMaxLength) {
THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String);
}
Handle<SeqOneByteString> result =
isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked();
#if DEBUG
// Zap the string first.
{
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
for (int i = 0; i < static_cast<int>(chars_required); i++) chars[i] = '?';
}
#endif
// We assemble the result string in reverse order, and then reverse it.
// TODO(jkummerow): Consider building the string from the right, and
// left-shifting it if the length estimate was too large.
int pos = 0;
digit_t last_digit;
if (length == 1) {
last_digit = x->digit(0);
} else {
int chunk_chars =
kDigitBits * kBitsPerCharTableMultiplier / max_bits_per_char;
digit_t chunk_divisor = digit_pow(radix, chunk_chars);
// By construction of chunk_chars, there can't have been overflow.
DCHECK(chunk_divisor != 0);
int nonzero_digit = length - 1;
DCHECK(x->digit(nonzero_digit) != 0);
// {rest} holds the part of the BigInt that we haven't looked at yet.
// Not to be confused with "remainder"!
Handle<BigInt> rest;
// In the first round, divide the input, allocating a new BigInt for
// the result == rest; from then on divide the rest in-place.
Handle<BigInt>* dividend = &x;
do {
digit_t chunk;
AbsoluteDivSmall(*dividend, chunk_divisor, &rest, &chunk);
DCHECK(!rest.is_null());
dividend = &rest;
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
for (int i = 0; i < chunk_chars; i++) {
chars[pos++] = kConversionChars[chunk % radix];
chunk /= radix;
}
DCHECK(chunk == 0);
if (rest->digit(nonzero_digit) == 0) nonzero_digit--;
// We can never clear more than one digit per iteration, because
// chunk_divisor is smaller than max digit value.
DCHECK(rest->digit(nonzero_digit) > 0);
} while (nonzero_digit > 0);
last_digit = rest->digit(0);
}
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
do {
chars[pos++] = kConversionChars[last_digit % radix];
last_digit /= radix;
} while (last_digit > 0);
DCHECK(pos >= 1);
DCHECK(pos <= static_cast<int>(chars_required));
// Remove leading zeroes.
while (pos > 1 && chars[pos - 1] == '0') pos--;
if (sign) chars[pos++] = '-';
// Trim any over-allocation (which can happen due to conservative estimates).
if (pos < static_cast<int>(chars_required)) {
result->synchronized_set_length(pos);
int string_size =
SeqOneByteString::SizeFor(static_cast<int>(chars_required));
int needed_size = SeqOneByteString::SizeFor(pos);
if (needed_size < string_size) {
Address new_end = result->address() + needed_size;
heap->CreateFillerObjectAt(new_end, (string_size - needed_size),
ClearRecordedSlots::kNo);
}
}
// Reverse the string.
for (int i = 0, j = pos - 1; i < j; i++, j--) {
uint8_t tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
#if DEBUG
// Verify that all characters have been written.
DCHECK(result->length() == pos);
for (int i = 0; i < pos; i++) DCHECK(chars[i] != '?');
#endif
return result;
}
// Digit arithmetic helpers.
#if V8_TARGET_ARCH_32_BIT
@ -1152,7 +1267,12 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor,
digit_t vn1 = divisor >> kHalfDigitBits;
digit_t vn0 = divisor & kHalfDigitMask;
digit_t un32 = (high << s) | (low >> (kDigitBits - s));
// {s} can be 0. "low >> kDigitBits == low" on x86, so we "&" it with
// {s_zero_mask} which is 0 if s == 0 and all 1-bits otherwise.
STATIC_ASSERT(sizeof(intptr_t) == sizeof(digit_t));
digit_t s_zero_mask =
static_cast<digit_t>(static_cast<intptr_t>(-s) >> (kDigitBits - 1));
digit_t un32 = (high << s) | ((low >> (kDigitBits - s)) & s_zero_mask);
digit_t un10 = low << s;
digit_t un1 = un10 >> kHalfDigitBits;
digit_t un0 = un10 & kHalfDigitMask;
@ -1180,6 +1300,19 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor,
#endif
}
// Raises {base} to the power of {exponent}. Does not check for overflow.
BigInt::digit_t BigInt::digit_pow(digit_t base, digit_t exponent) {
digit_t result = 1ull;
while (exponent > 0) {
if (exponent & 1) {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
#undef HAVE_TWODIGIT_T
#ifdef OBJECT_PRINT

View File

@ -157,6 +157,7 @@ class BigInt : public HeapObject {
static MaybeHandle<String> ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix);
static MaybeHandle<String> ToStringGeneric(Handle<BigInt> x, int radix);
// Digit arithmetic helpers.
static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry);
@ -164,6 +165,7 @@ class BigInt : public HeapObject {
static inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high);
static inline digit_t digit_div(digit_t high, digit_t low, digit_t divisor,
digit_t* remainder);
static digit_t digit_pow(digit_t base, digit_t exponent);
static inline bool digit_ismax(digit_t x) {
return static_cast<digit_t>(~x) == 0;
}

View File

@ -42,18 +42,168 @@ const six = BigInt(6);
// .toString(radix)
{
// assertEquals(expected, BigInt(input).toString(n)) is generated by:
// input = $(python -c "print(int('expected', n))")
assertEquals("hello", BigInt(18306744).toString(32));
assertEquals("-hello", BigInt(-18306744).toString(32));
assertEquals("abcde", BigInt(0xabcde).toString(16));
assertEquals("-abcde", BigInt(-0xabcde).toString(16));
assertEquals("1234567", BigInt(342391).toString(8));
assertEquals("-1234567", BigInt(-342391).toString(8));
assertEquals("1230123", BigInt(6939).toString(4));
assertEquals("-1230123", BigInt(-6939).toString(4));
assertEquals("1011001110001", BigInt(5745).toString(2));
assertEquals("-1011001110001", BigInt(-5745).toString(2));
// Single-digit BigInts: random-generated inputs close to kMaxInt.
// Expectations computed with the following Python program:
// def Format(x, base):
// s = ""
// while x > 0:
// s = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base] + s
// x = x / base
// return s
assertEquals("10100110000100101000011100101", BigInt(0x14c250e5).toString(2));
assertEquals("-110110100010011111001011111", BigInt(-0x6d13e5f).toString(2));
assertEquals("1001222020000100000", BigInt(0x18c72873).toString(3));
assertEquals("-1212101122110102020", BigInt(-0x2b19aebe).toString(3));
assertEquals("120303133110120", BigInt(0x18cdf518).toString(4));
assertEquals("-113203101020122", BigInt(-0x178d121a).toString(4));
assertEquals("1323302233400", BigInt(0x18de6256).toString(5));
assertEquals("-2301033210212", BigInt(-0x25f7f454).toString(5));
assertEquals("131050115130", BigInt(0x211f0d5e).toString(6));
assertEquals("-104353333321", BigInt(-0x186bbe91).toString(6));
assertEquals("25466260221", BigInt(0x2f69f47e).toString(7));
assertEquals("-31051540346", BigInt(-0x352c7efa).toString(7));
assertEquals("5004630525", BigInt(0x28133155).toString(8));
assertEquals("-7633240703", BigInt(-0x3e6d41c3).toString(8));
assertEquals("705082365", BigInt(0x121f4264).toString(9));
assertEquals("-780654431", BigInt(-0x1443b36e).toString(9));
assertEquals("297019028", BigInt(0x11b42694).toString(10));
assertEquals("-721151126", BigInt(-0x2afbe496).toString(10));
assertEquals("312914074", BigInt(0x27ca6879).toString(11));
assertEquals("-198025592", BigInt(-0x1813d3a7).toString(11));
assertEquals("191370997", BigInt(0x2d14f083).toString(12));
assertEquals("-1b8aab4a2", BigInt(-0x32b52efa).toString(12));
assertEquals("7818062c", BigInt(0x1c84a48c).toString(13));
assertEquals("-7529695b", BigInt(-0x1badffee).toString(13));
assertEquals("6bc929c4", BigInt(0x2b0a91d0).toString(14));
assertEquals("-63042008", BigInt(-0x270dff78).toString(14));
assertEquals("5e8b8dec", BigInt(0x3cd27d7f).toString(15));
assertEquals("-4005433d", BigInt(-0x28c0821a).toString(15));
assertEquals("10b35ca3", BigInt(0x10b35ca3).toString(16));
assertEquals("-23d4d9d6", BigInt(-0x23d4d9d6).toString(16));
assertEquals("28c3d5e3", BigInt(0x3d75d48c).toString(17));
assertEquals("-10c06328", BigInt(-0x1979b7f0).toString(17));
assertEquals("eb8d349", BigInt(0x1dacf0a5).toString(18));
assertEquals("-1217015h", BigInt(-0x28b3c23f).toString(18));
assertEquals("1018520b", BigInt(0x357da01a).toString(19));
assertEquals("-9c64e33", BigInt(-0x1b0e9571).toString(19));
assertEquals("d7bf9ab", BigInt(0x3309daa3).toString(20));
assertEquals("-58h0h9h", BigInt(-0x14c30c55).toString(20));
assertEquals("64igi9h", BigInt(0x1fdd329c).toString(21));
assertEquals("-45cbc4a", BigInt(-0x15cf9682).toString(21));
assertEquals("7bi7d1h", BigInt(0x32f0dfe3).toString(22));
assertEquals("-61j743l", BigInt(-0x291ff61f).toString(22));
assertEquals("5g5gg25", BigInt(0x325a10bd).toString(23));
assertEquals("-3359flb", BigInt(-0x1bb653c9).toString(23));
assertEquals("392f5ec", BigInt(0x267ed69c).toString(24));
assertEquals("-2ab3icb", BigInt(-0x1bbf7bab).toString(24));
assertEquals("3jb2afo", BigInt(0x36f93c24).toString(25));
assertEquals("-30bcheh", BigInt(-0x2bec76fa).toString(25));
assertEquals("3845agk", BigInt(0x3d04bf64).toString(26));
assertEquals("-1gpjl3g", BigInt(-0x1e720b1a).toString(26));
assertEquals("20bpaf0", BigInt(0x2e8ff627).toString(27));
assertEquals("-292i3c2", BigInt(-0x35f751fe).toString(27));
assertEquals("266113k", BigInt(0x3fd26738).toString(28));
assertEquals("-1eh16bo", BigInt(-0x2bb5726c).toString(28));
assertEquals("19gj7qa", BigInt(0x2f28e8d8).toString(29));
assertEquals("-13a0apf", BigInt(-0x278b4588).toString(29));
assertEquals("iasrb8", BigInt(0x1a99b3be).toString(30));
assertEquals("-frlhoc", BigInt(-0x17106f48).toString(30));
assertEquals("bfe4p2", BigInt(0x139f1ea3).toString(31));
assertEquals("-ioal1a", BigInt(-0x200e49fa).toString(31));
assertEquals("m0v0kf", BigInt(0x2c0f828f).toString(32));
assertEquals("-g4bab5", BigInt(-0x2045a965).toString(32));
assertEquals("9i1kit", BigInt(0x16450a9f).toString(33));
assertEquals("-fqb0e7", BigInt(-0x24d9e889).toString(33));
assertEquals("gb9r6m", BigInt(0x2c3acf46).toString(34));
assertEquals("-jcaemv", BigInt(-0x346f72b3).toString(34));
assertEquals("cw4mbk", BigInt(0x2870cdcb).toString(35));
assertEquals("-hw4eki", BigInt(-0x3817c29b).toString(35));
assertEquals("alzwgj", BigInt(0x263e2c13).toString(36));
assertEquals("-bo4ukz", BigInt(-0x2a0f97d3).toString(36));
// Multi-digit BigInts.
// Test parseInt/toString round trip on a list of randomly generated
// string representations of numbers in various bases.
var positive = [0, 0, // Skip base 0 and 1.
"1100110001100010110011110110010010001011100111100101111000111101100001000",
"1001200022210010220101120212021002011002201122200002211102120120021011020",
"1111113020012203332320220022231110130001001320122012131311333110012023232",
"4214313040222110434114402342013144321401424143322013320403411012033300312",
"5025302003542512450341430541203424555035430434034243510233043041501130015",
"6231052230016515343200525230300322104013130605414211331345043144525012021",
"1146340505617030644211355340006353546230356336306352536433054143503442135",
"7262360724624787621528668212168232276348417717770383567066203032200270570",
"7573792356581293501680046955899735043496925151216904903504319328753434194",
"4a627927557579898720a42647639128174a8689889766a219342133671449069a2235011",
"1a574a5848289924996342a32893380690322330393633b587ba5a15b7b82080222400464",
"5163304c74c387b7a443c92466688595b671a3329b42083b1499b0c10a74a9298a06c3a5a",
"4b63c834356a03c80946133284a709cbbc2a75022757207dc31c14abd4c160dc122327c17",
"d8d59cbb4ca2860de7c002eee4ab3c215b90069200d20dbdc0111cb1e1bab97e8c7609670",
"22d4b69398a7f848e6ae36798811cd1a63d90f340d8607f3ce5566c97c18468787eb2b9fd",
"1176gf69afd32cc105fa70c705927a384dbdb1g8d952f28028g31ebdc9e32a89f16e825ee",
"5d64b74f4d70632h4ee07h7c1e2da9125c42g2727f4b6d95e5cec6ga49566hh731ab5f544",
"7ff8cg7f05dd72916a09a4761ii7b0ibcg68ba39b10436f14efg76ge817317badcbi4gffc",
"6d7c4hci6cd72e4ja26j354i12i71gb0cbj12gi145j91h02hde3b72c65geb7ff9bi9d0c2b",
"c96997f50abe425d13a53kk4af631kg7db208ka5j5bfg8ca5f9c0bjf69j5kgg4jb5h7hi86",
"3g5fd800d9ib9j0i8all5jgb23dh9483ab6le5ad9g4kja8a0b3j5jbjfge7k5fffg2kbheee",
"9j1119d1cd61kmdm7kma105cki313f678fc3h25f4664281bbmg3fk97kfbh7d48j89j178ch",
"d2933cdc9jfe4hl3794kb3e13dg2lihad968ib9jg19dgf1fi482b27ji0d10c6kfkdge5764",
"bf6o0njkm1ij5in5nh7h94584bd80el02b07el5ojk9k9g0gn906do70gbbnckl048c0kdmao",
"8gb7jnge9p9cdgigo394oa33gfaenc3gnb53eceg4b8511gkkm88b0dod85e5bggpc861d7d5",
"qbbnqhkpleb4o8ndaddpc34h5b2iljn3jgnjdn5k57bi3n9i09hjle9hqgqdpgbnk499mak56",
"akg7e2976arn8i2m53gif0dp59bmfd7mk9erlg2qm3fc76da9glf397eh4ooij9il0nfl9gac",
"mehpbfrj5ah2ef3p2hl637gjp1pm5grqn4037pm1qfgfpr9cfljfc145hljehjjb48bb1n6en",
"rg6ik3agnb3p6t2rtja9h4il76i8fkqlt6gplap3fq6pfr7bbcfcp5ffncf3nm4kamap39hse",
"bk8rp9r9r8pltdqpb7euc6s9rcm33969pcq6uk3mtfoktt86di8589oacbam5tn29b9b6dq3j",
"npth8juld44rss3e57iigjg65po3d1h02heo4r103jmg3ocv89buqtgiov35k39rdf8j9t4ca",
"vrmqlwrrrd0uml1womae49jpa9tadh44fw7mucgk06l0uk4uqwuo37t6kwn7wwrm3a6oq081s",
"n5cft6gvufqd8iksquu2amghokk17gbtpguidc290af634p7k7rhmfu7bf1s62ej4megoa1j4",
"3v3gcrmlfc2tl0tefgkiogj41f6y2tmj9w5bxke8y03xqf49ox8gh9wbrhycrkluicqajtnur",
"z2m7b0sy2tzergtkqts5yj0dkrlfkxls81ijgxgfequizpntcwggv2d4rdzcncd0kj9mrmnrb",
];
var negative = [0, 0, // Skip base 0 and 1.
"-100010011110111010111111110001100100111010101000001011010010101100101000",
"-110012122000122102021210112200001000122011010120101201001122000002022102",
"-203210320111001002200122200001312300221100221321010300023323201113122333",
"-133042441230110320040323303341320302144241224443231311022240124413104131",
"-311325230504055004330150145105331121322231155401110315251422505233103112",
"-643153641664240231336166403516403454646560261062114326443664602606315326",
"-200057252627665476551635525303641543165622340301637556323453513664337277",
"-826688166214270516331644053744613530235020517172322840763172114078364165",
"-743042397390679269240157150971957535458122650450558451124173993544604852",
"-73528688500003573942a56a504a2996a1384129563098512a63196697975038692aaa63",
"-616576a2948a9029316290168b71137b027851639a0283150b125b664b74b767a3597805",
"-b875467540719b371b7a36047a7886872a5399c4c630c37149bc3182917a7a7c124475bb",
"-3860411b61d35977721bc81bd715c386c9b70a752940913d265505d8c7c5dd2624b591d7",
"-bad5dd79b083ee0da9a6296664e72c246d827762357116ae7076a22bb369acbc3a201d03",
"-f9b37352aff265124303942a463917a252ff1a2ff4a33777f490b4c103bdcd1a655dbe2c",
"-805fg8c74125214g383a8d8g573c49fa7c4035fbc6db61g5gb5g6beb8f90dae4a9a5g7cc",
"-70aae113459d3h5084b1gg209g3695d20e78d01gcbb71bh1bd4gdge31haf5hc02dghf14e",
"-c55a57haf47b7ih2gh6ea93098ig02b42icga6ead254e0aeeic7g53h5fd6637ge03b2e20",
"-e32f7204624ie596j731g72136cejc25ebbgb0140i4997fcdf477f021d86ci4e10db543a",
"-i7f32c817i3cac1c24c7786k6ig185f47cj1471ki6bb7agiae838027gjge9g59if9f88g6",
"-i30aha2030a9605c270h92e1ca3i02j996hl918gh52fbhb7i16ik1i919ieak3cj384kb61",
"-58jmem8e59li67aellid2083dabh4kh51ci1jg7c6a3k4l1hdgfkdha0fglfm4805kida5b9",
"-cl9iecjg9ak087cad4151lll44296heae2349g70fbjj37998m2ddn6427fgcl2aknhgn1a1",
"-alfjfhho4gf8bi4j2bi3743mhg2aache4c6jcinkmf5ddm7kf9gg350hlja16ealbdlk201j",
"-bhh1146ho3o2m3b839c565hbgjnhjh96oofbmdl7gn8h4f94kli94hkk180o79pc4d2l0721",
"-p00gknh7e05k6a3apg6i9lb46f4a9qeeiq1778ak8il5dcponk5gl2fiednb4pmo1agmoqph",
"-4j8lo4d4p508fnd2hkfb76e8ri81k6hq0op3pr14ca0cn96pccplk7rbahc9cdkdce1q16dn",
"-ednlo3ogf2i8annrel9rm323bpf00meed3oi47n0qrdgnd2n3il4bnsc9s2jd7loh44im8ra",
"-bjjg6fsbpcc2tc1o09m9r6fd6eoq5480har62a5offn9thcfahbno9kf9magl2akl0jgncj9",
"-sonuhat2h60glpbpej9jjado2s5l86122d26tudoc1d6aic2oitu793gk0mlac3dk1dufp1q",
"-i9pbvm53ubh8jqifuarauch8cbgk9cjsl6rlioka1phs1lskg1oosll23hjoli2subgr1rto",
"-w1ncn5t60b5dv669ekwnvk8n2g7djrsl8cdkwun8o3m5divc3jhnkp2381rhj70gc71a6wff",
"-buiq8v33p5ex44ps4s45enj6lrluivm19lcowkvntu72u0xguw13bxgxxe7mdlwt1a4qksae",
"-woiycfmea6i12r2yai49mf4lbd7w2jdoebiogfhnh1i4rwgox57obci8qbsfpb4w07nu19m5",
"-tbttuip1r6ioca6g6dw354o4m78qep9yh03nojx47yq29fqime6zstwllb74501qct8eskxn",
];
for (var base = 2; base <= 36; base++) {
var input = positive[base];
assertEquals(input, BigInt.parseInt(input, base).toString(base));
input = negative[base];
assertEquals(input, BigInt.parseInt(input, base).toString(base));
}
}
// .parseInt