MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor bigint implementation to use limb-based arithmetic

+1038 -418
+17 -1
examples/spec/bigint.js
··· 1 - import { test, summary } from './helpers.js'; 1 + import { test, testThrows, summary } from './helpers.js'; 2 2 3 3 console.log('BigInt Tests\n'); 4 4 ··· 25 25 test('bigint negation', -5n, -5n); 26 26 27 27 test('bigint toString', (255n).toString(16), 'ff'); 28 + test('bigint toString radix 2 suffix', ((1n << 64n) + 255n).toString(2).slice(-8), '11111111'); 29 + test('bigint toString radix 10', ((1n << 128n) + 1n).toString(10), '340282366920938463463374607431768211457'); 30 + 31 + test('bigint shift left', 1n << 130n, 1361129467683753853853498429727072845824n); 32 + test('bigint shift right positive', 8n >> 1n, 4n); 33 + test('bigint shift right negative floor -3n', -3n >> 1n, -2n); 34 + test('bigint shift right negative floor -5n', -5n >> 1n, -3n); 35 + test('bigint shift right huge negative', -1n >> 100n, -1n); 36 + testThrows('bigint unsigned right shift throws', () => (1n >>> 0n)); 37 + 38 + const limbA = (1n << 200n) + (1n << 129n) + 12345678901234567890n; 39 + const limbB = (1n << 73n) + 12345n; 40 + test('bigint multi-limb division', limbA / limbB, 170141183460469231509371611710366941474n); 41 + test('bigint multi-limb modulo', limbA % limbB, 5527003422616403339840n); 42 + test('bigint division truncates toward zero', -19n / 4n, -4n); 43 + test('bigint modulo keeps dividend sign', -19n % 4n, -3n); 28 44 29 45 test('BigInt.asUintN 0 bits', BigInt.asUintN(0, 123n), 0n); 30 46 test('BigInt.asUintN wrap', BigInt.asUintN(8, 256n), 0n);
+80 -32
src/gc.c
··· 124 124 return n + 1; 125 125 } 126 126 127 + static inline bool gc_stack_word_readable(uintptr_t addr) { 128 + #if ANT_HAS_ASAN 129 + return __asan_region_is_poisoned((void *)addr, sizeof(uint64_t)) == NULL; 130 + #else 131 + return true; 132 + #endif 133 + } 134 + 135 + static inline bool gc_off_has_bytes(ant_offset_t off, ant_offset_t need, ant_offset_t brk) { 136 + return off <= brk && need <= brk - off; 137 + } 138 + 127 139 static void gc_update_func_constants(gc_ctx_t *ctx, sv_func_t *func, int depth) { 128 140 if (!func || depth > 1024) return; 129 141 if (func->gc_epoch == gc_epoch_counter) return; ··· 344 356 return off; 345 357 } 346 358 359 + 347 360 static ant_offset_t gc_copy_string(gc_ctx_t *ctx, ant_offset_t old_off) { 348 361 if (old_off >= ctx->js->brk) return old_off; 349 362 ··· 387 400 return new_off; 388 401 } 389 402 403 + typedef struct { 404 + ant_offset_t total_aligned; 405 + ant_offset_t limbs_off; 406 + uint32_t limb_count; 407 + uint8_t sign; 408 + } gc_bigint_meta_t; 409 + 410 + static bool gc_read_bigint_meta( 411 + gc_ctx_t *ctx, 412 + ant_offset_t old_off, 413 + ant_offset_t old_brk, 414 + gc_bigint_meta_t *meta 415 + ) { 416 + ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); 417 + if ((header & GC_BIGINT_HEADER_LOW_MASK) != 0) return false; 418 + 419 + ant_offset_t payload = header >> GC_BIGINT_HEADER_SHIFT; 420 + const ant_offset_t fixed_payload = (ant_offset_t)(sizeof(uint32_t) + sizeof(uint32_t)); 421 + if (payload < fixed_payload + (ant_offset_t)sizeof(uint32_t)) return false; 422 + 423 + ant_offset_t total = payload + (ant_offset_t)sizeof(ant_offset_t); 424 + total = (total + 7) & ~(ant_offset_t)7; 425 + if (!gc_off_has_bytes(old_off, total, old_brk)) return false; 426 + 427 + ant_offset_t sign_off = old_off + (ant_offset_t)sizeof(ant_offset_t); 428 + uint8_t sign = ctx->js->mem[sign_off]; 429 + if (sign > 1) return false; 430 + 431 + ant_offset_t count_off = old_off + (ant_offset_t)sizeof(ant_offset_t) + (ant_offset_t)sizeof(uint32_t); 432 + uint32_t limb_count = 0; 433 + memcpy(&limb_count, &ctx->js->mem[count_off], sizeof(limb_count)); 434 + if (limb_count == 0) return false; 435 + 436 + ant_offset_t expected_payload = fixed_payload + (ant_offset_t)limb_count * (ant_offset_t)sizeof(uint32_t); 437 + if (expected_payload != payload) return false; 438 + 439 + ant_offset_t limbs_off = count_off + (ant_offset_t)sizeof(uint32_t); 440 + uint32_t top_limb = 0; 441 + memcpy( 442 + &top_limb, 443 + &ctx->js->mem[limbs_off + (ant_offset_t)(limb_count - 1) * (ant_offset_t)sizeof(uint32_t)], 444 + sizeof(top_limb) 445 + ); 446 + if (top_limb == 0 && limb_count > 1) return false; 447 + 448 + if (limb_count == 1) { 449 + uint32_t limb0 = 0; 450 + memcpy(&limb0, &ctx->js->mem[limbs_off], sizeof(limb0)); 451 + if (limb0 == 0 && sign != 0) return false; 452 + } 453 + 454 + if (meta) { 455 + meta->total_aligned = total; 456 + meta->limbs_off = limbs_off; 457 + meta->limb_count = limb_count; 458 + meta->sign = sign; 459 + } 460 + 461 + return true; 462 + } 463 + 390 464 static ant_offset_t gc_copy_bigint(gc_ctx_t *ctx, ant_offset_t old_off) { 391 465 if (old_off >= ctx->js->brk) return old_off; 392 466 393 467 ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); 394 468 if (new_off != (ant_offset_t)~0) return new_off; 395 - 396 - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); 397 - size_t total = (header >> 4) + sizeof(ant_offset_t); 398 - total = (total + 7) / 8 * 8; 469 + 470 + gc_bigint_meta_t meta; 471 + if (!gc_read_bigint_meta(ctx, old_off, ctx->js->brk, &meta)) return old_off; 399 472 400 - new_off = gc_alloc(ctx, total); 473 + new_off = gc_alloc(ctx, (size_t)meta.total_aligned); 401 474 if (new_off == (ant_offset_t)~0) return old_off; 402 475 403 - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], total); 476 + memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], (size_t)meta.total_aligned); 404 477 if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; 405 478 mark_set(ctx, old_off); 406 479 ··· 713 786 return true; 714 787 } 715 788 716 - static inline bool gc_stack_word_readable(uintptr_t addr) { 717 - #if ANT_HAS_ASAN 718 - return __asan_region_is_poisoned((void *)addr, sizeof(uint64_t)) == NULL; 719 - #else 720 - return true; 721 - #endif 722 - } 723 - 724 - static inline bool gc_off_has_bytes(ant_offset_t off, ant_offset_t need, ant_offset_t brk) { 725 - return off <= brk && need <= brk - off; 726 - } 727 - 728 789 static inline bool gc_stack_word_valid(gc_ctx_t *ctx, uint8_t type, ant_offset_t old_off, ant_offset_t old_brk) { 729 790 if (old_off == 0 || old_off >= old_brk) return false; 730 791 if (!gc_off_has_bytes(old_off, (ant_offset_t)sizeof(ant_offset_t), old_brk)) return false; ··· 753 814 return size != (ant_offset_t)~0 && gc_off_has_bytes(old_off, size, old_brk); 754 815 } 755 816 case T_BIGINT: { 756 - if ((header & GC_BIGINT_HEADER_LOW_MASK) != 0) return false; 757 - ant_offset_t payload = header >> GC_BIGINT_HEADER_SHIFT; 758 - if (payload < 2) return false; 759 - 760 - ant_offset_t total = payload + (ant_offset_t)sizeof(ant_offset_t); 761 - total = (total + 7) & ~(ant_offset_t)7; 762 - if (!gc_off_has_bytes(old_off, total, old_brk)) return false; 763 - 764 - ant_offset_t sign_off = old_off + (ant_offset_t)sizeof(ant_offset_t); 765 - uint8_t sign = ctx->js->mem[sign_off]; 766 - if (sign > 1) return false; 767 - 768 - ant_offset_t nul_off = sign_off + payload - 1; 769 - return ctx->js->mem[nul_off] == 0; 817 + return gc_read_bigint_meta(ctx, old_off, old_brk, NULL); 770 818 } 771 819 case T_SYMBOL: { 772 820 if ((header & GC_SYM_HEADER_LOW_MASK) != 0) return false;
+879 -385
src/modules/bigint.c
··· 4 4 #include "ant.h" 5 5 #include "internal.h" 6 6 #include "errors.h" 7 - #include "arena.h" 8 7 #include "runtime.h" 9 8 9 + #define BIGINT_BASE ((uint64_t)0x100000000ULL) 10 + #define BIGINT_DEC_GROUP_BASE 1000000000U 11 + 12 + typedef struct { 13 + uint8_t sign; 14 + uint8_t pad[3]; 15 + uint32_t limb_count; 16 + uint32_t limbs[]; 17 + } bigint_payload_t; 18 + 10 19 static inline bool is_decimal_digit(char c) { 11 20 return c >= '0' && c <= '9'; 12 21 } 13 22 14 - bool bigint_is_negative(ant_t *js, ant_value_t v) { 23 + static bool checked_add_size(size_t a, size_t b, size_t *out) { 24 + if (a > SIZE_MAX - b) return false; 25 + *out = a + b; 26 + return true; 27 + } 28 + 29 + static bool checked_mul_size(size_t a, size_t b, size_t *out) { 30 + if (a != 0 && b > SIZE_MAX / a) return false; 31 + *out = a * b; 32 + return true; 33 + } 34 + 35 + static uint32_t *limb_alloc(size_t count) { 36 + if (count == 0) count = 1; 37 + return (uint32_t *)calloc(count, sizeof(uint32_t)); 38 + } 39 + 40 + static uint32_t *limb_dup(const uint32_t *limbs, size_t count) { 41 + if (count == 0) count = 1; 42 + uint32_t *copy = limb_alloc(count); 43 + if (!copy) return NULL; 44 + if (limbs && count > 0) memcpy(copy, limbs, count * sizeof(uint32_t)); 45 + return copy; 46 + } 47 + 48 + static void bigint_normalize_limbs(uint32_t *limbs, size_t *count) { 49 + while (*count > 1 && limbs[*count - 1] == 0) (*count)--; 50 + } 51 + 52 + static inline const bigint_payload_t *bigint_payload(ant_t *js, ant_value_t v) { 15 53 ant_offset_t ofs = (ant_offset_t)vdata(v); 16 - return js->mem[ofs + sizeof(ant_offset_t)] == 1; 54 + return (const bigint_payload_t *)&js->mem[ofs + sizeof(ant_offset_t)]; 17 55 } 18 56 19 - size_t bigint_digits_len(ant_t *js, ant_value_t v) { 20 - ant_offset_t ofs = (ant_offset_t)vdata(v); 21 - ant_offset_t header = loadoff(js, ofs); 22 - return (size_t)((header >> 4) - 2); 57 + static inline bool limbs_is_zero(const uint32_t *limbs, size_t count) { 58 + return count == 1 && limbs[0] == 0; 23 59 } 24 60 25 - static const char *bigint_digits(ant_t *js, ant_value_t v, size_t *len) { 26 - ant_offset_t ofs = (ant_offset_t)vdata(v); 27 - size_t total = bigint_digits_len(js, v); 28 - if (len) *len = total; 29 - return (const char *)&js->mem[ofs + sizeof(ant_offset_t) + 1]; 61 + bool bigint_is_negative(ant_t *js, ant_value_t v) { 62 + return bigint_payload(js, v)->sign == 1; 63 + } 64 + 65 + static const uint32_t *bigint_limbs(ant_t *js, ant_value_t v, size_t *count) { 66 + const bigint_payload_t *payload = bigint_payload(js, v); 67 + size_t limb_count = payload->limb_count; 68 + if (limb_count == 0) limb_count = 1; 69 + if (count) *count = limb_count; 70 + return payload->limbs; 71 + } 72 + 73 + static ant_value_t js_mkbigint_limbs(ant_t *js, const uint32_t *limbs, size_t count, bool negative) { 74 + uint32_t zero = 0; 75 + 76 + if (!limbs || count == 0) { 77 + limbs = &zero; 78 + count = 1; 79 + } 80 + 81 + while (count > 1 && limbs[count - 1] == 0) count--; 82 + if (count == 1 && limbs[0] == 0) negative = false; 83 + 84 + if (count > UINT32_MAX) return js_mkerr(js, "oom"); 85 + 86 + size_t limbs_bytes; 87 + if (!checked_mul_size(count, sizeof(uint32_t), &limbs_bytes)) return js_mkerr(js, "oom"); 88 + 89 + size_t payload_size; 90 + if (!checked_add_size(offsetof(bigint_payload_t, limbs), limbs_bytes, &payload_size)) { 91 + return js_mkerr(js, "oom"); 92 + } 93 + 94 + size_t total_size; 95 + if (!checked_add_size(sizeof(ant_offset_t), payload_size, &total_size)) return js_mkerr(js, "oom"); 96 + 97 + ant_offset_t ofs = js_alloc(js, total_size); 98 + if (ofs == (ant_offset_t)~0) return js_mkerr(js, "oom"); 99 + 100 + ant_offset_t header = (ant_offset_t)(payload_size << 4); 101 + memcpy(&js->mem[ofs], &header, sizeof(header)); 102 + 103 + bigint_payload_t *payload = (bigint_payload_t *)&js->mem[ofs + sizeof(ant_offset_t)]; 104 + payload->sign = negative ? 1 : 0; 105 + payload->pad[0] = 0; 106 + payload->pad[1] = 0; 107 + payload->pad[2] = 0; 108 + payload->limb_count = (uint32_t)count; 109 + memcpy(payload->limbs, limbs, limbs_bytes); 110 + 111 + return mkval(T_BIGINT, ofs); 30 112 } 31 113 32 114 static ant_value_t bigint_from_u64(ant_t *js, uint64_t value) { 33 - char buf[32]; 34 - size_t len = uint_to_str(buf, sizeof(buf), value); 35 - return js_mkbigint(js, buf, len, false); 115 + uint32_t limbs[2] = { 116 + (uint32_t)(value & 0xffffffffu), 117 + (uint32_t)(value >> 32) 118 + }; 119 + 120 + size_t count = limbs[1] == 0 ? 1 : 2; 121 + return js_mkbigint_limbs(js, limbs, count, false); 36 122 } 37 123 38 124 static bool bigint_parse_abs_u64(ant_t *js, ant_value_t value, uint64_t *out) { 39 - size_t len = 0; 40 - const char *digits = bigint_digits(js, value, &len); 41 - uint64_t acc = 0; 125 + size_t count = 0; 126 + const uint32_t *limbs = bigint_limbs(js, value, &count); 127 + 128 + if (count > 2) return false; 42 129 43 - for (size_t i = 0; i < len; i++) { 44 - char c = digits[i]; 45 - if (!is_decimal_digit(c)) return false; 46 - uint64_t digit = (uint64_t)(c - '0'); 47 - if (acc > UINT64_MAX / 10 || (acc == UINT64_MAX / 10 && digit > (UINT64_MAX % 10))) { 48 - return false; 49 - } 50 - acc = acc * 10 + digit; 51 - } 130 + uint64_t acc = limbs[0]; 131 + if (count == 2) acc |= ((uint64_t)limbs[1] << 32); 52 132 53 133 *out = acc; 54 134 return true; ··· 59 139 return bigint_parse_abs_u64(js, value, out); 60 140 } 61 141 62 - ant_value_t js_mkbigint(ant_t *js, const char *digits, size_t len, bool negative) { 63 - size_t total = len + 2; 64 - ant_offset_t ofs = js_alloc(js, total + sizeof(ant_offset_t)); 65 - if (ofs == (ant_offset_t)~0) return js_mkerr(js, "oom"); 142 + static int bigint_cmp_abs_limbs(const uint32_t *a, size_t alen, const uint32_t *b, size_t blen) { 143 + while (alen > 1 && a[alen - 1] == 0) alen--; 144 + while (blen > 1 && b[blen - 1] == 0) blen--; 66 145 67 - ant_offset_t header = (ant_offset_t)(total << 4); 68 - memcpy(&js->mem[ofs], &header, sizeof(header)); 69 - js->mem[ofs + sizeof(header)] = negative ? 1 : 0; 70 - if (digits) memcpy(&js->mem[ofs + sizeof(header) + 1], digits, len); 71 - js->mem[ofs + sizeof(header) + 1 + len] = 0; 72 - return mkval(T_BIGINT, ofs); 73 - } 146 + if (alen != blen) return alen > blen ? 1 : -1; 74 147 75 - static int bigint_cmp_abs(const char *a, size_t alen, const char *b, size_t blen) { 76 - while (alen > 1 && a[0] == '0') { a++; alen--; } 77 - while (blen > 1 && b[0] == '0') { b++; blen--; } 78 - if (alen != blen) return alen > blen ? 1 : -1; 79 - for (size_t i = 0; i < alen; i++) { 148 + for (size_t i = alen; i-- > 0;) { 80 149 if (a[i] != b[i]) return a[i] > b[i] ? 1 : -1; 81 150 } 151 + 82 152 return 0; 83 153 } 84 154 85 - static char *bigint_add_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { 86 - size_t maxlen = (alen > blen ? alen : blen) + 1; 87 - char *result = (char *)malloc(maxlen + 1); 155 + static uint32_t *bigint_add_abs_limbs(const uint32_t *a, size_t alen, const uint32_t *b, size_t blen, size_t *rlen) { 156 + size_t maxlen = alen > blen ? alen : blen; 157 + 158 + uint32_t *result = limb_alloc(maxlen + 1); 88 159 if (!result) return NULL; 89 160 90 - int carry = 0; 91 - size_t ri = 0; 161 + uint64_t carry = 0; 92 162 for (size_t i = 0; i < maxlen; i++) { 93 - int da = (i < alen) ? (a[alen - 1 - i] - '0') : 0; 94 - int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; 95 - int sum = da + db + carry; 96 - carry = sum / 10; 97 - result[ri++] = (char)('0' + (sum % 10)); 163 + uint64_t da = i < alen ? a[i] : 0; 164 + uint64_t db = i < blen ? b[i] : 0; 165 + uint64_t sum = da + db + carry; 166 + result[i] = (uint32_t)sum; 167 + carry = sum >> 32; 168 + } 169 + 170 + result[maxlen] = (uint32_t)carry; 171 + *rlen = maxlen + (carry ? 1 : 0); 172 + if (*rlen == 0) *rlen = 1; 173 + bigint_normalize_limbs(result, rlen); 174 + return result; 175 + } 176 + 177 + static uint32_t *bigint_add_u32(const uint32_t *a, size_t alen, uint32_t value, size_t *rlen) { 178 + uint32_t *result = limb_dup(a, alen); 179 + if (!result) return NULL; 180 + 181 + uint64_t carry = value; 182 + size_t i = 0; 183 + 184 + while (carry != 0 && i < alen) { 185 + uint64_t sum = (uint64_t)result[i] + carry; 186 + result[i] = (uint32_t)sum; 187 + carry = sum >> 32; 188 + i++; 189 + } 190 + 191 + if (carry == 0) { 192 + *rlen = alen; 193 + bigint_normalize_limbs(result, rlen); 194 + return result; 98 195 } 99 196 100 - while (ri > 1 && result[ri - 1] == '0') ri--; 101 - for (size_t i = 0; i < ri / 2; i++) { 102 - char tmp = result[i]; 103 - result[i] = result[ri - 1 - i]; 104 - result[ri - 1 - i] = tmp; 197 + uint32_t *grown = (uint32_t *)realloc(result, (alen + 1) * sizeof(uint32_t)); 198 + if (!grown) { 199 + free(result); 200 + return NULL; 105 201 } 106 202 107 - result[ri] = 0; 108 - *rlen = ri; 109 - return result; 203 + grown[alen] = (uint32_t)carry; 204 + *rlen = alen + 1; 205 + return grown; 110 206 } 111 207 112 - static char *bigint_sub_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { 113 - char *result = (char *)malloc(alen + 1); 208 + static uint32_t *bigint_sub_abs_limbs(const uint32_t *a, size_t alen, const uint32_t *b, size_t blen, size_t *rlen) { 209 + uint32_t *result = limb_alloc(alen); 114 210 if (!result) return NULL; 115 211 116 - int borrow = 0; 117 - size_t ri = 0; 212 + uint64_t borrow = 0; 213 + 118 214 for (size_t i = 0; i < alen; i++) { 119 - int da = a[alen - 1 - i] - '0'; 120 - int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; 121 - int diff = da - db - borrow; 122 - if (diff < 0) { 123 - diff += 10; 215 + uint64_t da = a[i]; 216 + uint64_t db = i < blen ? b[i] : 0; 217 + uint64_t subtrahend = db + borrow; 218 + 219 + if (da < subtrahend) { 220 + result[i] = (uint32_t)(BIGINT_BASE + da - subtrahend); 124 221 borrow = 1; 125 222 } else { 223 + result[i] = (uint32_t)(da - subtrahend); 126 224 borrow = 0; 127 225 } 128 - result[ri++] = (char)('0' + diff); 129 226 } 130 227 131 - while (ri > 1 && result[ri - 1] == '0') ri--; 132 - for (size_t i = 0; i < ri / 2; i++) { 133 - char tmp = result[i]; 134 - result[i] = result[ri - 1 - i]; 135 - result[ri - 1 - i] = tmp; 136 - } 137 - 138 - result[ri] = 0; 139 - *rlen = ri; 228 + *rlen = alen; 229 + bigint_normalize_limbs(result, rlen); 140 230 return result; 141 231 } 142 232 143 - static char *bigint_mul_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { 144 - size_t reslen = alen + blen; 145 - int *temp = (int *)calloc(reslen, sizeof(int)); 146 - if (!temp) return NULL; 233 + static uint32_t *bigint_mul_abs_limbs(const uint32_t *a, size_t alen, const uint32_t *b, size_t blen, size_t *rlen) { 234 + uint32_t *result = limb_alloc(alen + blen + 1); 235 + if (!result) return NULL; 147 236 148 237 for (size_t i = 0; i < alen; i++) { 238 + uint64_t carry = 0; 239 + 149 240 for (size_t j = 0; j < blen; j++) { 150 - temp[i + j] += (a[alen - 1 - i] - '0') * (b[blen - 1 - j] - '0'); 241 + uint64_t prod = (uint64_t)a[i] * (uint64_t)b[j]; 242 + uint64_t lo = (uint64_t)result[i + j] + (prod & 0xffffffffu) + (carry & 0xffffffffu); 243 + result[i + j] = (uint32_t)lo; 244 + carry = (prod >> 32) + (carry >> 32) + (lo >> 32); 245 + } 246 + 247 + size_t k = i + blen; 248 + while (carry != 0) { 249 + uint64_t cur = (uint64_t)result[k] + (carry & 0xffffffffu); 250 + result[k] = (uint32_t)cur; 251 + carry = (carry >> 32) + (cur >> 32); 252 + k++; 151 253 } 152 254 } 153 255 154 - for (size_t i = 0; i < reslen - 1; i++) { 155 - temp[i + 1] += temp[i] / 10; 156 - temp[i] %= 10; 157 - } 256 + *rlen = alen + blen + 1; 257 + bigint_normalize_limbs(result, rlen); 258 + return result; 259 + } 260 + 261 + static uint32_t *bigint_shift_left_abs(const uint32_t *limbs, size_t count, uint64_t shift, size_t *rlen) { 262 + size_t limb_shift = (size_t)(shift >> 5); 263 + unsigned bit_shift = (unsigned)(shift & 31u); 158 264 159 - size_t start = reslen - 1; 160 - while (start > 0 && temp[start] == 0) start--; 265 + size_t out_count = count + limb_shift + 1; 266 + uint32_t *out = limb_alloc(out_count); 267 + if (!out) return NULL; 161 268 162 - char *result = (char *)malloc(start + 2); 163 - if (!result) { 164 - free(temp); 165 - return NULL; 269 + if (bit_shift == 0) { 270 + memcpy(out + limb_shift, limbs, count * sizeof(uint32_t)); 271 + *rlen = count + limb_shift; 272 + bigint_normalize_limbs(out, rlen); 273 + return out; 166 274 } 167 275 168 - for (size_t i = 0; i <= start; i++) result[i] = (char)('0' + temp[start - i]); 169 - result[start + 1] = 0; 170 - *rlen = start + 1; 276 + uint32_t carry = 0; 277 + for (size_t i = 0; i < count; i++) { 278 + uint64_t cur = ((uint64_t)limbs[i] << bit_shift) | carry; 279 + out[i + limb_shift] = (uint32_t)cur; 280 + carry = (uint32_t)(cur >> 32); 281 + } 171 282 172 - free(temp); 173 - return result; 283 + out[count + limb_shift] = carry; 284 + *rlen = out_count; 285 + bigint_normalize_limbs(out, rlen); 286 + return out; 174 287 } 175 288 176 - static char *bigint_div_abs( 177 - const char *a, 178 - size_t alen, 179 - const char *b, 180 - size_t blen, 181 - size_t *rlen, 182 - char **rem, 183 - size_t *remlen 289 + static uint32_t *bigint_shift_right_abs( 290 + const uint32_t *limbs, 291 + size_t count, 292 + uint64_t shift, 293 + bool *truncated, 294 + size_t *rlen 184 295 ) { 185 - if (blen == 1 && b[0] == '0') return NULL; 296 + size_t limb_shift = (size_t)(shift >> 5); 297 + unsigned bit_shift = (unsigned)(shift & 31u); 186 298 187 - if (bigint_cmp_abs(a, alen, b, blen) < 0) { 188 - char *result = (char *)malloc(2); 189 - result[0] = '0'; 190 - result[1] = 0; 299 + bool lost = false; 300 + 301 + if (limb_shift >= count) { 302 + for (size_t i = 0; i < count; i++) { 303 + if (limbs[i] != 0) { lost = true; break; } 304 + } 305 + 306 + uint32_t *zero = limb_alloc(1); 307 + if (!zero) return NULL; 308 + zero[0] = 0; 309 + if (truncated) *truncated = lost; 191 310 *rlen = 1; 192 - if (rem) { 193 - *rem = (char *)malloc(alen + 1); 194 - memcpy(*rem, a, alen); 195 - (*rem)[alen] = 0; 196 - *remlen = alen; 311 + return zero; 312 + } 313 + 314 + for (size_t i = 0; i < limb_shift; i++) { 315 + if (limbs[i] != 0) { lost = true; break; } 316 + } 317 + 318 + size_t out_count = count - limb_shift; 319 + uint32_t *out = limb_alloc(out_count); 320 + if (!out) return NULL; 321 + 322 + if (bit_shift == 0) { 323 + memcpy(out, limbs + limb_shift, out_count * sizeof(uint32_t)); 324 + } else { 325 + uint32_t carry = 0; 326 + uint32_t mask = (1u << bit_shift) - 1u; 327 + 328 + for (size_t src = count; src-- > limb_shift;) { 329 + uint32_t cur = limbs[src]; 330 + size_t dst = src - limb_shift; 331 + out[dst] = (cur >> bit_shift) | (carry << (32u - bit_shift)); 332 + carry = cur & mask; 197 333 } 198 - return result; 334 + 335 + if ((limbs[limb_shift] & mask) != 0) lost = true; 199 336 } 200 337 201 - char *current = (char *)calloc(alen + 1, 1); 202 - char *result = (char *)calloc(alen + 1, 1); 203 - if (!current || !result) { 204 - free(current); 205 - free(result); 206 - return NULL; 338 + *rlen = out_count; 339 + bigint_normalize_limbs(out, rlen); 340 + if (truncated) *truncated = lost; 341 + return out; 342 + } 343 + 344 + static uint32_t bigint_div_small_inplace(uint32_t *limbs, size_t count, uint32_t divisor) { 345 + uint64_t rem = 0; 346 + 347 + for (size_t i = count; i-- > 0;) { 348 + uint64_t cur = (rem << 32) | limbs[i]; 349 + limbs[i] = (uint32_t)(cur / divisor); 350 + rem = cur % divisor; 207 351 } 208 352 209 - size_t curlen = 0, reslen = 0; 210 - for (size_t i = 0; i < alen; i++) { 211 - if (curlen == 1 && current[0] == '0') curlen = 0; 212 - current[curlen++] = a[i]; 213 - current[curlen] = 0; 353 + return (uint32_t)rem; 354 + } 355 + 356 + static inline unsigned clz32_nonzero(uint32_t v) { 357 + #if defined(__GNUC__) || defined(__clang__) 358 + return (unsigned)__builtin_clz(v); 359 + #else 360 + unsigned n = 0; 361 + while ((v & 0x80000000u) == 0) { 362 + v <<= 1; 363 + n++; 364 + } 365 + return n; 366 + #endif 367 + } 368 + 369 + static bool bigint_divmod_abs_limbs( 370 + const uint32_t *num, 371 + size_t num_count, 372 + const uint32_t *den, 373 + size_t den_count, 374 + uint32_t **q_out, 375 + size_t *q_count_out, 376 + uint32_t **r_out, 377 + size_t *r_count_out 378 + ) { 379 + if (den_count == 1 && den[0] == 0) return false; 380 + 381 + int cmp = bigint_cmp_abs_limbs(num, num_count, den, den_count); 382 + if (cmp < 0) { 383 + if (q_out) { 384 + uint32_t *q = limb_alloc(1); 385 + if (!q) return false; 386 + q[0] = 0; 387 + *q_out = q; 388 + if (q_count_out) *q_count_out = 1; 389 + } 214 390 215 - int count = 0; 216 - while (bigint_cmp_abs(current, curlen, b, blen) >= 0) { 217 - size_t sublen; 218 - char *sub = bigint_sub_abs(current, curlen, b, blen, &sublen); 219 - if (!sub) break; 220 - memcpy(current, sub, sublen + 1); 221 - curlen = sublen; 222 - free(sub); 223 - count++; 391 + if (r_out) { 392 + uint32_t *r = limb_dup(num, num_count); 393 + if (!r) { 394 + if (q_out && *q_out) { 395 + free(*q_out); 396 + *q_out = NULL; 397 + } 398 + return false; 399 + } 400 + size_t rcount = num_count; 401 + bigint_normalize_limbs(r, &rcount); 402 + *r_out = r; 403 + if (r_count_out) *r_count_out = rcount; 224 404 } 225 405 226 - result[reslen++] = (char)('0' + count); 406 + return true; 227 407 } 228 408 229 - size_t start = 0; 230 - while (start < reslen - 1 && result[start] == '0') start++; 231 - memmove(result, result + start, reslen - start + 1); 409 + if (den_count == 1) { 410 + uint32_t divisor = den[0]; 411 + uint32_t *q = limb_dup(num, num_count); 412 + if (!q) return false; 232 413 233 - *rlen = reslen - start; 234 - if (rem) { 235 - *rem = current; 236 - *remlen = curlen; 414 + uint64_t rem = 0; 415 + for (size_t i = num_count; i-- > 0;) { 416 + uint64_t cur = (rem << 32) | q[i]; 417 + q[i] = (uint32_t)(cur / divisor); 418 + rem = cur % divisor; 419 + } 420 + 421 + size_t qcount = num_count; 422 + bigint_normalize_limbs(q, &qcount); 423 + 424 + if (q_out) { 425 + *q_out = q; 426 + if (q_count_out) *q_count_out = qcount; 427 + } else free(q); 428 + 429 + if (r_out) { 430 + uint32_t *r = limb_alloc(1); 431 + if (!r) { 432 + if (q_out && *q_out) { 433 + free(*q_out); 434 + *q_out = NULL; 435 + } 436 + return false; 437 + } 438 + r[0] = (uint32_t)rem; 439 + *r_out = r; 440 + if (r_count_out) *r_count_out = 1; 441 + } 442 + 443 + return true; 444 + } 445 + 446 + size_t m = num_count - den_count; 447 + uint32_t *vn = limb_alloc(den_count); 448 + uint32_t *un = limb_alloc(num_count + 1); 449 + uint32_t *q = limb_alloc(m + 1); 450 + 451 + if (!vn || !un || !q) { 452 + free(vn); 453 + free(un); 454 + free(q); 455 + return false; 456 + } 457 + 458 + unsigned shift = clz32_nonzero(den[den_count - 1]); 459 + 460 + if (shift == 0) { 461 + memcpy(vn, den, den_count * sizeof(uint32_t)); 462 + memcpy(un, num, num_count * sizeof(uint32_t)); 463 + un[num_count] = 0; 237 464 } else { 238 - free(current); 465 + uint32_t carry = 0; 466 + for (size_t i = 0; i < den_count; i++) { 467 + uint64_t cur = ((uint64_t)den[i] << shift) | carry; 468 + vn[i] = (uint32_t)cur; 469 + carry = (uint32_t)(cur >> 32); 470 + } 471 + 472 + carry = 0; 473 + for (size_t i = 0; i < num_count; i++) { 474 + uint64_t cur = ((uint64_t)num[i] << shift) | carry; 475 + un[i] = (uint32_t)cur; 476 + carry = (uint32_t)(cur >> 32); 477 + } 478 + un[num_count] = carry; 239 479 } 240 480 481 + for (size_t j = m + 1; j-- > 0;) { 482 + uint64_t numerator = ((uint64_t)un[j + den_count] << 32) | un[j + den_count - 1]; 483 + uint64_t qhat = numerator / vn[den_count - 1]; 484 + uint64_t rhat = numerator % vn[den_count - 1]; 485 + 486 + if (qhat >= BIGINT_BASE) { 487 + qhat = BIGINT_BASE - 1; 488 + rhat = numerator - qhat * vn[den_count - 1]; 489 + } 490 + 491 + if (den_count > 1) { 492 + while (qhat * (uint64_t)vn[den_count - 2] > ((rhat << 32) | un[j + den_count - 2])) { 493 + qhat--; 494 + rhat += vn[den_count - 1]; 495 + if (rhat >= BIGINT_BASE) break; 496 + } 497 + } 498 + 499 + uint64_t k = 0; 500 + for (size_t i = 0; i < den_count; i++) { 501 + uint64_t p = qhat * (uint64_t)vn[i] + k; 502 + k = p >> 32; 503 + uint32_t plow = (uint32_t)p; 504 + 505 + if (un[j + i] < plow) { 506 + un[j + i] = (uint32_t)((uint64_t)un[j + i] + BIGINT_BASE - plow); 507 + k += 1; 508 + } else { 509 + un[j + i] -= plow; 510 + } 511 + } 512 + 513 + bool borrow = un[j + den_count] < k; 514 + un[j + den_count] = (uint32_t)(un[j + den_count] - k); 515 + 516 + if (borrow) { 517 + qhat--; 518 + uint64_t carry = 0; 519 + for (size_t i = 0; i < den_count; i++) { 520 + uint64_t sum = (uint64_t)un[j + i] + vn[i] + carry; 521 + un[j + i] = (uint32_t)sum; 522 + carry = sum >> 32; 523 + } 524 + un[j + den_count] = (uint32_t)((uint64_t)un[j + den_count] + carry); 525 + } 526 + 527 + q[j] = (uint32_t)qhat; 528 + } 529 + 530 + size_t qcount = m + 1; 531 + bigint_normalize_limbs(q, &qcount); 532 + 533 + if (q_out) { 534 + *q_out = q; 535 + if (q_count_out) *q_count_out = qcount; 536 + } else free(q); 537 + 538 + if (r_out) { 539 + uint32_t *r = limb_alloc(den_count); 540 + if (!r) { 541 + free(vn); 542 + free(un); 543 + if (q_out && *q_out) { 544 + free(*q_out); 545 + *q_out = NULL; 546 + } 547 + return false; 548 + } 549 + 550 + if (shift == 0) { 551 + memcpy(r, un, den_count * sizeof(uint32_t)); 552 + } else { 553 + uint32_t carry = 0; 554 + for (size_t i = den_count; i-- > 0;) { 555 + uint32_t cur = un[i]; 556 + r[i] = (cur >> shift) | (carry << (32u - shift)); 557 + carry = cur & ((1u << shift) - 1u); 558 + } 559 + } 560 + 561 + size_t rcount = den_count; 562 + bigint_normalize_limbs(r, &rcount); 563 + *r_out = r; 564 + if (r_count_out) *r_count_out = rcount; 565 + } 566 + 567 + free(vn); 568 + free(un); 569 + return true; 570 + } 571 + 572 + static ant_value_t bigint_from_decimal_digits(ant_t *js, const char *digits, size_t len, bool negative) { 573 + if (!digits || len == 0) { 574 + uint32_t zero = 0; 575 + return js_mkbigint_limbs(js, &zero, 1, false); 576 + } 577 + 578 + while (len > 1 && *digits == '0') { 579 + digits++; 580 + len--; 581 + } 582 + 583 + size_t cap = len / 9 + 2; 584 + uint32_t *limbs = limb_alloc(cap); 585 + if (!limbs) return js_mkerr(js, "oom"); 586 + 587 + size_t count = 1; 588 + 589 + for (size_t i = 0; i < len; i++) { 590 + if (!is_decimal_digit(digits[i])) { 591 + free(limbs); 592 + return js_mkerr(js, "Cannot convert string to BigInt"); 593 + } 594 + 595 + uint64_t carry = (uint64_t)(digits[i] - '0'); 596 + 597 + for (size_t j = 0; j < count; j++) { 598 + uint64_t cur = (uint64_t)limbs[j] * 10 + carry; 599 + limbs[j] = (uint32_t)cur; 600 + carry = cur >> 32; 601 + } 602 + 603 + if (carry != 0) { 604 + if (count == cap) { 605 + size_t new_cap = cap * 2; 606 + uint32_t *new_limbs = (uint32_t *)realloc(limbs, new_cap * sizeof(uint32_t)); 607 + if (!new_limbs) { 608 + free(limbs); 609 + return js_mkerr(js, "oom"); 610 + } 611 + 612 + memset(new_limbs + cap, 0, (new_cap - cap) * sizeof(uint32_t)); 613 + limbs = new_limbs; 614 + cap = new_cap; 615 + } 616 + 617 + limbs[count++] = (uint32_t)carry; 618 + } 619 + } 620 + 621 + ant_value_t result = js_mkbigint_limbs(js, limbs, count, negative); 622 + free(limbs); 241 623 return result; 242 624 } 243 625 626 + static size_t u32_dec_len(uint32_t v) { 627 + if (v >= 1000000000u) return 10; 628 + if (v >= 100000000u) return 9; 629 + if (v >= 10000000u) return 8; 630 + if (v >= 1000000u) return 7; 631 + if (v >= 100000u) return 6; 632 + if (v >= 10000u) return 5; 633 + if (v >= 1000u) return 4; 634 + if (v >= 100u) return 3; 635 + if (v >= 10u) return 2; 636 + return 1; 637 + } 638 + 639 + static char *bigint_abs_to_decimal_string(const uint32_t *limbs, size_t count, size_t *out_len) { 640 + if (limbs_is_zero(limbs, count)) { 641 + char *z = (char *)malloc(2); 642 + if (!z) return NULL; 643 + z[0] = '0'; 644 + z[1] = '\0'; 645 + if (out_len) *out_len = 1; 646 + return z; 647 + } 648 + 649 + uint32_t *tmp = limb_dup(limbs, count); 650 + if (!tmp) return NULL; 651 + 652 + size_t tmp_count = count; 653 + size_t groups_cap = count * 2 + 1; 654 + uint32_t *groups = (uint32_t *)malloc(groups_cap * sizeof(uint32_t)); 655 + if (!groups) { 656 + free(tmp); 657 + return NULL; 658 + } 659 + 660 + size_t groups_len = 0; 661 + 662 + while (!(tmp_count == 1 && tmp[0] == 0)) { 663 + if (groups_len == groups_cap) { 664 + size_t new_cap = groups_cap * 2; 665 + uint32_t *new_groups = (uint32_t *)realloc(groups, new_cap * sizeof(uint32_t)); 666 + if (!new_groups) { 667 + free(tmp); 668 + free(groups); 669 + return NULL; 670 + } 671 + groups = new_groups; 672 + groups_cap = new_cap; 673 + } 674 + 675 + uint32_t rem = bigint_div_small_inplace(tmp, tmp_count, BIGINT_DEC_GROUP_BASE); 676 + groups[groups_len++] = rem; 677 + bigint_normalize_limbs(tmp, &tmp_count); 678 + } 679 + 680 + free(tmp); 681 + 682 + size_t len = u32_dec_len(groups[groups_len - 1]) + (groups_len - 1) * 9; 683 + char *out = (char *)malloc(len + 1); 684 + if (!out) { 685 + free(groups); 686 + return NULL; 687 + } 688 + 689 + size_t pos = 0; 690 + pos += (size_t)snprintf(out + pos, len + 1 - pos, "%u", groups[groups_len - 1]); 691 + 692 + for (size_t i = groups_len - 1; i-- > 0;) { 693 + pos += (size_t)snprintf(out + pos, len + 1 - pos, "%09u", groups[i]); 694 + } 695 + 696 + out[len] = '\0'; 697 + free(groups); 698 + 699 + if (out_len) *out_len = len; 700 + return out; 701 + } 702 + 703 + static char *bigint_abs_to_radix_string(const uint32_t *limbs, size_t count, uint32_t radix, size_t *out_len) { 704 + static const char digit_map[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 705 + 706 + if (limbs_is_zero(limbs, count)) { 707 + char *z = (char *)malloc(2); 708 + if (!z) return NULL; 709 + z[0] = '0'; 710 + z[1] = '\0'; 711 + if (out_len) *out_len = 1; 712 + return z; 713 + } 714 + 715 + uint32_t *tmp = limb_dup(limbs, count); 716 + if (!tmp) return NULL; 717 + 718 + size_t tmp_count = count; 719 + size_t out_cap = count * 32 + 2; 720 + char *out = (char *)malloc(out_cap); 721 + if (!out) { 722 + free(tmp); 723 + return NULL; 724 + } 725 + 726 + size_t out_pos = 0; 727 + 728 + while (!(tmp_count == 1 && tmp[0] == 0)) { 729 + if (out_pos + 1 >= out_cap) { 730 + size_t new_cap = out_cap * 2; 731 + char *new_out = (char *)realloc(out, new_cap); 732 + if (!new_out) { 733 + free(tmp); 734 + free(out); 735 + return NULL; 736 + } 737 + out = new_out; 738 + out_cap = new_cap; 739 + } 740 + 741 + uint32_t rem = bigint_div_small_inplace(tmp, tmp_count, radix); 742 + out[out_pos++] = digit_map[rem]; 743 + bigint_normalize_limbs(tmp, &tmp_count); 744 + } 745 + 746 + for (size_t i = 0; i < out_pos / 2; i++) { 747 + char t = out[i]; 748 + out[i] = out[out_pos - 1 - i]; 749 + out[out_pos - 1 - i] = t; 750 + } 751 + 752 + out[out_pos] = '\0'; 753 + free(tmp); 754 + 755 + if (out_len) *out_len = out_pos; 756 + return out; 757 + } 758 + 759 + ant_value_t js_mkbigint(ant_t *js, const char *digits, size_t len, bool negative) { 760 + return bigint_from_decimal_digits(js, digits, len, negative); 761 + } 762 + 244 763 ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b) { 245 764 bool aneg = bigint_is_negative(js, a); 246 765 bool bneg = bigint_is_negative(js, b); 247 - size_t alen, blen; 248 - const char *ad = bigint_digits(js, a, &alen); 249 - const char *bd = bigint_digits(js, b, &blen); 250 766 251 - char *result; 252 - size_t rlen; 253 - bool rneg; 767 + size_t alen = 0, blen = 0; 768 + const uint32_t *ad = bigint_limbs(js, a, &alen); 769 + const uint32_t *bd = bigint_limbs(js, b, &blen); 770 + 771 + uint32_t *result = NULL; 772 + size_t rlen = 0; 773 + bool rneg = false; 254 774 255 775 if (aneg == bneg) { 256 - result = bigint_add_abs(ad, alen, bd, blen, &rlen); 776 + result = bigint_add_abs_limbs(ad, alen, bd, blen, &rlen); 257 777 rneg = aneg; 258 778 } else { 259 - int cmp = bigint_cmp_abs(ad, alen, bd, blen); 779 + int cmp = bigint_cmp_abs_limbs(ad, alen, bd, blen); 260 780 if (cmp >= 0) { 261 - result = bigint_sub_abs(ad, alen, bd, blen, &rlen); 781 + result = bigint_sub_abs_limbs(ad, alen, bd, blen, &rlen); 262 782 rneg = aneg; 263 783 } else { 264 - result = bigint_sub_abs(bd, blen, ad, alen, &rlen); 784 + result = bigint_sub_abs_limbs(bd, blen, ad, alen, &rlen); 265 785 rneg = bneg; 266 786 } 267 787 } 268 788 269 789 if (!result) return js_mkerr(js, "oom"); 270 - if (rlen == 1 && result[0] == '0') rneg = false; 271 790 272 - ant_value_t r = js_mkbigint(js, result, rlen, rneg); 791 + ant_value_t out = js_mkbigint_limbs(js, result, rlen, rneg); 273 792 free(result); 274 - return r; 793 + return out; 275 794 } 276 795 277 796 ant_value_t bigint_sub(ant_t *js, ant_value_t a, ant_value_t b) { 278 797 bool aneg = bigint_is_negative(js, a); 279 798 bool bneg = bigint_is_negative(js, b); 280 - size_t alen, blen; 281 - const char *ad = bigint_digits(js, a, &alen); 282 - const char *bd = bigint_digits(js, b, &blen); 283 799 284 - char *result; 285 - size_t rlen; 286 - bool rneg; 800 + size_t alen = 0, blen = 0; 801 + const uint32_t *ad = bigint_limbs(js, a, &alen); 802 + const uint32_t *bd = bigint_limbs(js, b, &blen); 803 + 804 + uint32_t *result = NULL; 805 + size_t rlen = 0; 806 + bool rneg = false; 287 807 288 808 if (aneg != bneg) { 289 - result = bigint_add_abs(ad, alen, bd, blen, &rlen); 809 + result = bigint_add_abs_limbs(ad, alen, bd, blen, &rlen); 290 810 rneg = aneg; 291 811 } else { 292 - int cmp = bigint_cmp_abs(ad, alen, bd, blen); 812 + int cmp = bigint_cmp_abs_limbs(ad, alen, bd, blen); 293 813 if (cmp >= 0) { 294 - result = bigint_sub_abs(ad, alen, bd, blen, &rlen); 814 + result = bigint_sub_abs_limbs(ad, alen, bd, blen, &rlen); 295 815 rneg = aneg; 296 816 } else { 297 - result = bigint_sub_abs(bd, blen, ad, alen, &rlen); 817 + result = bigint_sub_abs_limbs(bd, blen, ad, alen, &rlen); 298 818 rneg = !aneg; 299 819 } 300 820 } 301 821 302 822 if (!result) return js_mkerr(js, "oom"); 303 - if (rlen == 1 && result[0] == '0') rneg = false; 304 823 305 - ant_value_t r = js_mkbigint(js, result, rlen, rneg); 824 + ant_value_t out = js_mkbigint_limbs(js, result, rlen, rneg); 306 825 free(result); 307 - return r; 826 + return out; 308 827 } 309 828 310 829 ant_value_t bigint_mul(ant_t *js, ant_value_t a, ant_value_t b) { 311 830 bool aneg = bigint_is_negative(js, a); 312 831 bool bneg = bigint_is_negative(js, b); 313 - size_t alen, blen; 314 - const char *ad = bigint_digits(js, a, &alen); 315 - const char *bd = bigint_digits(js, b, &blen); 316 832 317 - size_t rlen; 318 - char *result = bigint_mul_abs(ad, alen, bd, blen, &rlen); 833 + size_t alen = 0, blen = 0; 834 + const uint32_t *ad = bigint_limbs(js, a, &alen); 835 + const uint32_t *bd = bigint_limbs(js, b, &blen); 836 + 837 + uint32_t *result = bigint_mul_abs_limbs(ad, alen, bd, blen, &alen); 319 838 if (!result) return js_mkerr(js, "oom"); 320 839 321 - bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); 322 - ant_value_t r = js_mkbigint(js, result, rlen, rneg); 840 + bool rneg = (aneg != bneg) && !(alen == 1 && result[0] == 0); 841 + ant_value_t out = js_mkbigint_limbs(js, result, alen, rneg); 323 842 free(result); 324 - return r; 843 + return out; 325 844 } 326 845 327 846 ant_value_t bigint_div(ant_t *js, ant_value_t a, ant_value_t b) { 328 847 bool aneg = bigint_is_negative(js, a); 329 848 bool bneg = bigint_is_negative(js, b); 330 - size_t alen, blen; 331 - const char *ad = bigint_digits(js, a, &alen); 332 - const char *bd = bigint_digits(js, b, &blen); 333 849 334 - if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); 850 + size_t alen = 0, blen = 0; 851 + const uint32_t *ad = bigint_limbs(js, a, &alen); 852 + const uint32_t *bd = bigint_limbs(js, b, &blen); 335 853 336 - size_t rlen; 337 - char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, NULL, NULL); 338 - if (!result) return js_mkerr(js, "oom"); 854 + if (blen == 1 && bd[0] == 0) return js_mkerr(js, "Division by zero"); 339 855 340 - bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); 341 - ant_value_t r = js_mkbigint(js, result, rlen, rneg); 342 - free(result); 343 - return r; 856 + uint32_t *quot = NULL; 857 + size_t qlen = 0; 858 + 859 + if (!bigint_divmod_abs_limbs(ad, alen, bd, blen, &quot, &qlen, NULL, NULL)) { 860 + return js_mkerr(js, "oom"); 861 + } 862 + 863 + bool qneg = (aneg != bneg) && !(qlen == 1 && quot[0] == 0); 864 + ant_value_t out = js_mkbigint_limbs(js, quot, qlen, qneg); 865 + free(quot); 866 + return out; 344 867 } 345 868 346 869 ant_value_t bigint_mod(ant_t *js, ant_value_t a, ant_value_t b) { 347 870 bool aneg = bigint_is_negative(js, a); 348 - size_t alen, blen; 349 - const char *ad = bigint_digits(js, a, &alen); 350 - const char *bd = bigint_digits(js, b, &blen); 351 871 352 - if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); 872 + size_t alen = 0, blen = 0; 873 + const uint32_t *ad = bigint_limbs(js, a, &alen); 874 + const uint32_t *bd = bigint_limbs(js, b, &blen); 353 875 354 - size_t rlen, remlen; 355 - char *rem; 356 - char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, &rem, &remlen); 357 - if (!result) return js_mkerr(js, "oom"); 358 - free(result); 876 + if (blen == 1 && bd[0] == 0) return js_mkerr(js, "Division by zero"); 877 + 878 + uint32_t *rem = NULL; 879 + size_t rlen = 0; 880 + 881 + if (!bigint_divmod_abs_limbs(ad, alen, bd, blen, NULL, NULL, &rem, &rlen)) { 882 + return js_mkerr(js, "oom"); 883 + } 359 884 360 - bool rneg = aneg && !(remlen == 1 && rem[0] == '0'); 361 - ant_value_t r = js_mkbigint(js, rem, remlen, rneg); 885 + bool rneg = aneg && !(rlen == 1 && rem[0] == 0); 886 + ant_value_t out = js_mkbigint_limbs(js, rem, rlen, rneg); 362 887 free(rem); 363 - return r; 888 + return out; 364 889 } 365 890 366 891 ant_value_t bigint_neg(ant_t *js, ant_value_t a) { 367 - size_t len; 368 - const char *digits = bigint_digits(js, a, &len); 892 + size_t len = 0; 893 + const uint32_t *limbs = bigint_limbs(js, a, &len); 369 894 bool neg = bigint_is_negative(js, a); 370 - if (len == 1 && digits[0] == '0') return js_mkbigint(js, digits, len, false); 371 - return js_mkbigint(js, digits, len, !neg); 372 - } 373 895 374 - ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp) { 375 - if (bigint_is_negative(js, exp)) return js_mkerr(js, "Exponent must be positive"); 376 - 377 - size_t explen; 378 - const char *expd = bigint_digits(js, exp, &explen); 379 - if (explen == 1 && expd[0] == '0') return js_mkbigint(js, "1", 1, false); 380 - 381 - ant_value_t result = js_mkbigint(js, "1", 1, false); 382 - ant_value_t b = base; 383 - ant_value_t e = exp; 384 - ant_value_t two = js_mkbigint(js, "2", 1, false); 385 - 386 - while (true) { 387 - size_t elen; 388 - const char *ed = bigint_digits(js, e, &elen); 389 - if (elen == 1 && ed[0] == '0') break; 390 - 391 - int last_digit = ed[elen - 1] - '0'; 392 - if (last_digit % 2 == 1) { 393 - result = bigint_mul(js, result, b); 394 - if (is_err(result)) return result; 395 - } 396 - 397 - b = bigint_mul(js, b, b); 398 - if (is_err(b)) return b; 399 - 400 - e = bigint_div(js, e, two); 401 - if (is_err(e)) return e; 402 - } 896 + if (limbs_is_zero(limbs, len)) return js_mkbigint_limbs(js, limbs, len, false); 897 + return js_mkbigint_limbs(js, limbs, len, !neg); 898 + } 403 899 404 - return result; 900 + static inline bool bigint_is_odd(ant_t *js, ant_value_t v) { 901 + size_t count = 0; 902 + const uint32_t *limbs = bigint_limbs(js, v, &count); 903 + (void)count; 904 + return (limbs[0] & 1u) != 0; 405 905 } 406 906 407 907 static inline ant_value_t bigint_pow2(ant_t *js, uint64_t bits) { 408 - ant_value_t two = js_mkbigint(js, "2", 1, false); 409 - if (is_err(two)) return two; 908 + uint64_t limb_index_u64 = bits >> 5; 909 + if (limb_index_u64 > SIZE_MAX - 1) return js_mkerr(js, "oom"); 410 910 411 - ant_value_t exp = bigint_from_u64(js, bits); 412 - if (is_err(exp)) return exp; 911 + size_t count = (size_t)limb_index_u64 + 1; 912 + uint32_t *limbs = limb_alloc(count); 913 + if (!limbs) return js_mkerr(js, "oom"); 413 914 414 - return bigint_exp(js, two, exp); 915 + limbs[(size_t)limb_index_u64] = 1u << (bits & 31u); 916 + ant_value_t out = js_mkbigint_limbs(js, limbs, count, false); 917 + free(limbs); 918 + return out; 415 919 } 416 920 417 921 ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift) { 418 922 if (shift == 0) return value; 419 - if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); 420 923 421 - size_t digits_len; 422 - const char *digits = bigint_digits(js, value, &digits_len); 423 - if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); 924 + size_t count = 0; 925 + const uint32_t *limbs = bigint_limbs(js, value, &count); 424 926 425 - uint64_t u64 = 0; 426 - if (!bigint_is_negative(js, value) && shift < 64 && bigint_parse_u64(js, value, &u64)) { 427 - if (u64 <= (UINT64_MAX >> shift)) return bigint_from_u64(js, u64 << shift); 428 - } 927 + if (limbs_is_zero(limbs, count)) return js_mkbigint(js, "0", 1, false); 928 + 929 + uint32_t *result = NULL; 930 + size_t rlen = 0; 931 + result = bigint_shift_left_abs(limbs, count, shift, &rlen); 932 + if (!result) return js_mkerr(js, "oom"); 429 933 430 - ant_value_t pow = bigint_pow2(js, shift); 431 - if (is_err(pow)) return pow; 432 - return bigint_mul(js, value, pow); 934 + ant_value_t out = js_mkbigint_limbs(js, result, rlen, bigint_is_negative(js, value)); 935 + free(result); 936 + return out; 433 937 } 434 938 435 939 ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift) { 436 940 if (shift == 0) return value; 437 - if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); 438 941 439 - size_t digits_len; 440 - const char *digits = bigint_digits(js, value, &digits_len); 441 - if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); 942 + size_t count = 0; 943 + const uint32_t *limbs = bigint_limbs(js, value, &count); 442 944 443 - uint64_t u64 = 0; 444 - if (!bigint_is_negative(js, value) && bigint_parse_u64(js, value, &u64)) { 445 - if (shift >= 64) return js_mkbigint(js, "0", 1, false); 446 - return bigint_from_u64(js, u64 >> shift); 447 - } 945 + if (limbs_is_zero(limbs, count)) return js_mkbigint(js, "0", 1, false); 448 946 449 - if (bigint_parse_abs_u64(js, value, &u64)) { 450 - if (shift >= 64) { 451 - return js_mkbigint(js, bigint_is_negative(js, value) ? "1" : "0", 1, bigint_is_negative(js, value)); 452 - } 947 + bool neg = bigint_is_negative(js, value); 948 + bool truncated = false; 453 949 454 - uint64_t shifted = u64 >> shift; 455 - if (bigint_is_negative(js, value)) { 456 - if ((u64 & ((1ULL << shift) - 1)) != 0) shifted += 1; 457 - ant_value_t pos = bigint_from_u64(js, shifted); 458 - if (is_err(pos)) return pos; 459 - return bigint_neg(js, pos); 460 - } 950 + size_t qlen = 0; 951 + uint32_t *q = bigint_shift_right_abs(limbs, count, shift, &truncated, &qlen); 952 + if (!q) return js_mkerr(js, "oom"); 461 953 462 - return bigint_from_u64(js, shifted); 954 + if (!neg) { 955 + ant_value_t out = js_mkbigint_limbs(js, q, qlen, false); 956 + free(q); 957 + return out; 463 958 } 464 959 465 - ant_value_t pow = bigint_pow2(js, shift); 466 - if (is_err(pow)) return pow; 467 - return bigint_div(js, value, pow); 960 + if (truncated) { 961 + size_t new_len = 0; 962 + uint32_t *adj = bigint_add_u32(q, qlen, 1, &new_len); 963 + free(q); 964 + if (!adj) return js_mkerr(js, "oom"); 965 + q = adj; 966 + qlen = new_len; 967 + } 968 + 969 + ant_value_t out = js_mkbigint_limbs(js, q, qlen, !limbs_is_zero(q, qlen)); 970 + free(q); 971 + return out; 468 972 } 469 973 470 974 ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift) { 975 + (void)value; 976 + (void)shift; 471 977 return js_mkerr_typed(js, JS_ERR_TYPE, "BigInts have no unsigned right shift, use >> instead"); 472 978 } 473 979 474 980 int bigint_compare(ant_t *js, ant_value_t a, ant_value_t b) { 475 981 bool aneg = bigint_is_negative(js, a); 476 982 bool bneg = bigint_is_negative(js, b); 477 - 478 - size_t alen, blen; 479 - const char *ad = bigint_digits(js, a, &alen); 480 - const char *bd = bigint_digits(js, b, &blen); 481 983 482 984 if (aneg && !bneg) return -1; 483 985 if (!aneg && bneg) return 1; 484 986 485 - int cmp = bigint_cmp_abs(ad, alen, bd, blen); 987 + size_t alen = 0, blen = 0; 988 + const uint32_t *ad = bigint_limbs(js, a, &alen); 989 + const uint32_t *bd = bigint_limbs(js, b, &blen); 990 + 991 + int cmp = bigint_cmp_abs_limbs(ad, alen, bd, blen); 486 992 return aneg ? -cmp : cmp; 487 993 } 488 994 489 995 bool bigint_is_zero(ant_t *js, ant_value_t v) { 490 - size_t len; 491 - const char *digits = bigint_digits(js, v, &len); 492 - return len == 1 && digits[0] == '0'; 996 + size_t count = 0; 997 + const uint32_t *limbs = bigint_limbs(js, v, &count); 998 + return limbs_is_zero(limbs, count); 999 + } 1000 + 1001 + size_t bigint_digits_len(ant_t *js, ant_value_t v) { 1002 + size_t count = 0; 1003 + const uint32_t *limbs = bigint_limbs(js, v, &count); 1004 + 1005 + size_t len = 0; 1006 + char *digits = bigint_abs_to_decimal_string(limbs, count, &len); 1007 + if (!digits) return 0; 1008 + 1009 + free(digits); 1010 + return len; 1011 + } 1012 + 1013 + ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp) { 1014 + if (bigint_is_negative(js, exp)) return js_mkerr(js, "Exponent must be positive"); 1015 + if (bigint_is_zero(js, exp)) return js_mkbigint(js, "1", 1, false); 1016 + 1017 + ant_value_t result = js_mkbigint(js, "1", 1, false); 1018 + ant_value_t b = base; 1019 + ant_value_t e = exp; 1020 + 1021 + while (!bigint_is_zero(js, e)) { 1022 + if (bigint_is_odd(js, e)) { 1023 + result = bigint_mul(js, result, b); 1024 + if (is_err(result)) return result; 1025 + } 1026 + 1027 + b = bigint_mul(js, b, b); 1028 + if (is_err(b)) return b; 1029 + 1030 + e = bigint_shift_right(js, e, 1); 1031 + if (is_err(e)) return e; 1032 + } 1033 + 1034 + return result; 493 1035 } 494 1036 495 1037 size_t strbigint(ant_t *js, ant_value_t value, char *buf, size_t len) { 496 1038 bool neg = bigint_is_negative(js, value); 497 - size_t dlen; 498 - const char *digits = bigint_digits(js, value, &dlen); 1039 + 1040 + size_t count = 0; 1041 + const uint32_t *limbs = bigint_limbs(js, value, &count); 1042 + 1043 + size_t dlen = 0; 1044 + char *digits = bigint_abs_to_decimal_string(limbs, count, &dlen); 1045 + if (!digits) return 0; 1046 + 499 1047 size_t total = dlen + (neg ? 1 : 0); 500 - 501 - if (len == 0) return total; 1048 + if (len == 0) { 1049 + free(digits); 1050 + return total; 1051 + } 502 1052 503 1053 size_t n = 0; 1054 + 504 1055 if (neg && n < len - 1) buf[n] = '-'; 505 1056 if (neg) n++; 506 1057 ··· 511 1062 size_t term = n + copy_len; 512 1063 if (term >= len) term = len - 1; 513 1064 buf[term] = '\0'; 1065 + 1066 + free(digits); 514 1067 return total; 515 1068 } 516 1069 ··· 529 1082 bool neg = d < 0; 530 1083 if (neg) d = -d; 531 1084 532 - char buf[64]; 533 - snprintf(buf, sizeof(buf), "%.0f", d); 534 - return js_mkbigint(js, buf, strlen(buf), neg); 1085 + int need = snprintf(NULL, 0, "%.0f", d); 1086 + if (need < 0) return js_mkerr(js, "Cannot convert to BigInt"); 1087 + 1088 + char *buf = (char *)malloc((size_t)need + 1); 1089 + if (!buf) return js_mkerr(js, "oom"); 1090 + 1091 + snprintf(buf, (size_t)need + 1, "%.0f", d); 1092 + ant_value_t out = js_mkbigint(js, buf, (size_t)need, neg); 1093 + free(buf); 1094 + return out; 535 1095 } 536 1096 537 1097 if (vtype(arg) == T_STR) { ··· 541 1101 542 1102 bool neg = false; 543 1103 size_t i = 0; 1104 + 544 1105 if (slen > 0 && str[0] == '-') { 545 1106 neg = true; 546 1107 i++; ··· 656 1217 } 657 1218 658 1219 bool neg = bigint_is_negative(js, val); 659 - size_t dlen; 660 - const char *digits = bigint_digits(js, val, &dlen); 661 - 662 - if (radix == 10) { 663 - size_t buflen = dlen + 2; 664 - char *buf = (char *)ant_calloc(buflen); 665 - if (!buf) return js_mkerr(js, "oom"); 666 - 667 - size_t n = 0; 668 - if (neg) buf[n++] = '-'; 669 - memcpy(buf + n, digits, dlen); 670 - n += dlen; 671 - 672 - ant_value_t ret = js_mkstr(js, buf, n); 673 - free(buf); 674 - return ret; 675 - } 1220 + size_t count = 0; 1221 + const uint32_t *limbs = bigint_limbs(js, val, &count); 676 1222 677 - const uint32_t base = 1000000000U; 678 - size_t result_cap = dlen * 4 + 16; 679 - char *result = (char *)ant_calloc(result_cap); 680 - if (!result) return js_mkerr(js, "oom"); 1223 + size_t dlen = 0; 1224 + char *digits = NULL; 681 1225 682 - size_t rpos = result_cap - 1; 683 - result[rpos] = '\0'; 684 - 685 - size_t limb_cap = (dlen + 8) / 9 + 1; 686 - uint32_t *limbs = (uint32_t *)ant_calloc(limb_cap * sizeof(uint32_t)); 687 - if (!limbs) { 688 - free(result); 689 - return js_mkerr(js, "oom"); 690 - } 691 - size_t limb_len = 1; 1226 + if (radix == 10) digits = bigint_abs_to_decimal_string(limbs, count, &dlen); 1227 + else digits = bigint_abs_to_radix_string(limbs, count, (uint32_t)radix, &dlen); 692 1228 693 - for (size_t i = 0; i < dlen; i++) { 694 - uint64_t carry = (uint64_t)(digits[i] - '0'); 695 - for (size_t j = 0; j < limb_len; j++) { 696 - uint64_t cur = (uint64_t)limbs[j] * 10 + carry; 697 - limbs[j] = (uint32_t)(cur % base); 698 - carry = cur / base; 699 - } 1229 + if (!digits) return js_mkerr(js, "oom"); 700 1230 701 - if (carry != 0) { 702 - if (limb_len == limb_cap) { 703 - size_t new_cap = limb_cap * 2; 704 - uint32_t *new_limbs = (uint32_t *)ant_realloc(limbs, new_cap * sizeof(uint32_t)); 705 - if (!new_limbs) { 706 - free(limbs); 707 - free(result); 708 - return js_mkerr(js, "oom"); 709 - } 710 - limbs = new_limbs; 711 - limb_cap = new_cap; 712 - } 713 - limbs[limb_len++] = (uint32_t)carry; 714 - } 1231 + if (!neg) { 1232 + ant_value_t out = js_mkstr(js, digits, dlen); 1233 + free(digits); 1234 + return out; 715 1235 } 716 1236 717 - static const char digit_map[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 718 - while (limb_len > 0 && !(limb_len == 1 && limbs[0] == 0)) { 719 - uint64_t remainder = 0; 720 - for (size_t i = limb_len; i-- > 0;) { 721 - uint64_t cur = (uint64_t)limbs[i] + remainder * base; 722 - limbs[i] = (uint32_t)(cur / (uint64_t)radix); 723 - remainder = cur % (uint64_t)radix; 724 - } 725 - 726 - while (limb_len > 0 && limbs[limb_len - 1] == 0) limb_len--; 727 - 728 - if (rpos == 0) { 729 - size_t new_cap = result_cap * 2; 730 - char *new_result = (char *)ant_calloc(new_cap); 731 - if (!new_result) { 732 - free(limbs); 733 - free(result); 734 - return js_mkerr(js, "oom"); 735 - } 736 - 737 - size_t used = result_cap - rpos; 738 - memcpy(new_result + new_cap - used, result + rpos, used); 739 - free(result); 740 - 741 - result = new_result; 742 - rpos = new_cap - used; 743 - result_cap = new_cap; 744 - } 745 - 746 - result[--rpos] = digit_map[remainder]; 1237 + char *full = (char *)malloc(dlen + 2); 1238 + if (!full) { 1239 + free(digits); 1240 + return js_mkerr(js, "oom"); 747 1241 } 748 1242 749 - free(limbs); 1243 + full[0] = '-'; 1244 + memcpy(full + 1, digits, dlen); 1245 + full[dlen + 1] = '\0'; 750 1246 751 - if (rpos == result_cap - 1) result[--rpos] = '0'; 752 - if (neg) result[--rpos] = '-'; 753 - 754 - ant_value_t ret = js_mkstr(js, result + rpos, result_cap - 1 - rpos); 755 - free(result); 756 - return ret; 1247 + ant_value_t out = js_mkstr(js, full, dlen + 1); 1248 + free(digits); 1249 + free(full); 1250 + return out; 757 1251 } 758 1252 759 1253 void init_bigint_module(void) { 760 1254 ant_t *js = rt->js; 761 - 1255 + 762 1256 ant_value_t glob = js_glob(js); 763 1257 ant_value_t object_proto = js->object; 764 1258 ant_value_t function_proto = js_get_slot(js, glob, SLOT_FUNC_PROTO);
+16
tests/bench.js
··· 567 567 return bigint_arith(n, 256); 568 568 } 569 569 570 + function bigint_shift_floor(n) { 571 + var i, j, x, y; 572 + x = (BigInt(1) << BigInt(260)) + (BigInt(1) << BigInt(131)) + BigInt(123456789); 573 + y = BigInt(0); 574 + for (j = 0; j < n; j++) { 575 + for (i = 0; i < 500; i++) { 576 + x = (x << BigInt(3)) - BigInt(7); 577 + y += x >> BigInt(5); 578 + y += (-x) >> BigInt(7); 579 + } 580 + } 581 + global_res = x + y; 582 + return n * 1000; 583 + } 584 + 570 585 function set_collection_add(n) { 571 586 var s, 572 587 i, ··· 970 985 /* BigInt test */ 971 986 test_list.push(bigint64_arith); 972 987 test_list.push(bigint256_arith); 988 + test_list.push(bigint_shift_floor); 973 989 } 974 990 975 991 for (i = 1; i < argc; ) {
+46
tests/test_bigint.js
··· 13 13 } 14 14 } 15 15 16 + function testThrows(name, fn, expectedMessagePart) { 17 + try { 18 + fn(); 19 + console.log('FAIL:', name, '- expected throw'); 20 + failed++; 21 + } catch (err) { 22 + const msg = String(err); 23 + if (msg.indexOf(expectedMessagePart) !== -1) { 24 + console.log('PASS:', name); 25 + passed++; 26 + } else { 27 + console.log('FAIL:', name, '- wrong error:', msg); 28 + failed++; 29 + } 30 + } 31 + } 32 + 16 33 console.log('=== BigInt Literals ==='); 17 34 test('123n', 123n, '123'); 18 35 test('0n', 0n, '0'); ··· 98 115 test('big1 + big2', big1 + big2, '111111111011111111101111111110'); 99 116 test('big2 - big1', big2 - big1, '86419753208641975320864197532'); 100 117 test('big1 * 2n', big1 * 2n, '24691357802469135780246913578'); 118 + 119 + console.log('\n=== BigInt Shift Semantics ==='); 120 + test('8n >> 1n', 8n >> 1n, '4'); 121 + test('-3n >> 1n (floor)', -3n >> 1n, '-2'); 122 + test('-5n >> 1n (floor)', -5n >> 1n, '-3'); 123 + test('-1n >> 100n', -1n >> 100n, '-1'); 124 + test('1n << 130n', 1n << 130n, '1361129467683753853853498429727072845824'); 125 + test('(-1n) << 65n', (-1n) << 65n, '-36893488147419103232'); 126 + testThrows('1n >>> 0n throws TypeError', () => (1n >>> 0n), 'BigInts have no unsigned right shift'); 127 + 128 + console.log('\n=== BigInt Division and Mod Edge Cases ==='); 129 + const limbA = (1n << 200n) + (1n << 129n) + 12345678901234567890n; 130 + const limbB = (1n << 73n) + 12345n; 131 + test('multi-limb divide', limbA / limbB, '170141183460469231509371611710366941474'); 132 + test('multi-limb modulo', limbA % limbB, '5527003422616403339840'); 133 + test('negative divide truncates toward zero', -19n / 4n, '-4'); 134 + test('negative modulo keeps dividend sign', -19n % 4n, '-3'); 135 + 136 + console.log('\n=== BigInt toString(radix) ==='); 137 + const rad = (1n << 128n) + (1n << 64n) + 255n; 138 + test('radix 10', rad.toString(10), '340282366920938463481821351505477763327'); 139 + test('radix 16', rad.toString(16), '1000000000000000100000000000000ff'); 140 + test('radix 2 suffix', rad.toString(2).slice(-8), '11111111'); 141 + 142 + console.log('\n=== BigInt.asIntN / asUintN ==='); 143 + test('BigInt.asIntN(8, 255n)', BigInt.asIntN(8, 255n), '-1'); 144 + test('BigInt.asIntN(8, 128n)', BigInt.asIntN(8, 128n), '-128'); 145 + test('BigInt.asUintN(8, -1n)', BigInt.asUintN(8, -1n), '255'); 146 + test('BigInt.asUintN(5, -3n)', BigInt.asUintN(5, -3n), '29'); 101 147 102 148 console.log('\n=== Summary ==='); 103 149 console.log('Passed:', passed);