From 5ae15d357a998f9aa141b99615cf5c5f94207bdc Mon Sep 17 00:00:00 2001 From: theMackabu Date: Fri, 6 Mar 2026 17:31:35 -0800 Subject: [PATCH] full limb string migration support --- examples/spec/bigint.js | 13 ++ include/modules/bigint.h | 4 + src/modules/bigint.c | 279 ++++++++++++++++++++++++++++++++++----- src/silver/engine.c | 2 +- src/silver/ops/bitwise.h | 24 +++- tests/bench_bigint.js | 97 ++++++++++++++ tests/test_bigint.js | 21 +++ 7 files changed, 399 insertions(+), 41 deletions(-) create mode 100644 tests/bench_bigint.js diff --git a/examples/spec/bigint.js b/examples/spec/bigint.js index 0230027..77bd874 100644 --- a/examples/spec/bigint.js +++ b/examples/spec/bigint.js @@ -5,13 +5,26 @@ console.log('BigInt Tests\n'); test('bigint literal', 123n, 123n); test('BigInt()', BigInt(456), 456n); test('BigInt from string', BigInt('789'), 789n); +test('BigInt from hex string', BigInt('0xff'), 255n); +test('BigInt from binary string', BigInt('0b1010'), 10n); +test('BigInt from octal string', BigInt('0o77'), 63n); +test('BigInt from trimmed string', BigInt(' 42 '), 42n); +test('BigInt from empty string', BigInt(''), 0n); +testThrows('BigInt invalid prefixed string', () => BigInt('0x')); +testThrows('BigInt invalid underscore string', () => BigInt('1_2')); test('bigint addition', 1n + 2n, 3n); +test('bigint prefixed literal addition', 0xffn + 1n, 256n); test('bigint subtraction', 10n - 3n, 7n); test('bigint multiplication', 4n * 5n, 20n); test('bigint division', 10n / 3n, 3n); test('bigint modulo', 10n % 3n, 1n); test('bigint exponentiation', 2n ** 10n, 1024n); +test('bigint bitwise and', 5n & 3n, 1n); +test('bigint bitwise or', 5n | 2n, 7n); +test('bigint bitwise xor', 5n ^ 1n, 4n); +test('bigint bitwise not', ~0n, -1n); +test('bigint bitwise negative mix', (-8n) | 3n, -5n); test('bigint comparison <', 1n < 2n, true); test('bigint comparison >', 5n > 3n, true); diff --git a/include/modules/bigint.h b/include/modules/bigint.h index 46fee7f..14cf6d8 100644 --- a/include/modules/bigint.h +++ b/include/modules/bigint.h @@ -16,6 +16,10 @@ ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp); ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift); ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift); ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift); +ant_value_t bigint_bitand(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_bitor(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_bitxor(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_bitnot(ant_t *js, ant_value_t value); ant_value_t bigint_asint_bits(ant_t *js, ant_value_t arg, uint64_t *bits_out); bool bigint_is_negative(ant_t *js, ant_value_t v); diff --git a/src/modules/bigint.c b/src/modules/bigint.c index bea44ae..508e27d 100644 --- a/src/modules/bigint.c +++ b/src/modules/bigint.c @@ -5,6 +5,8 @@ #include "internal.h" #include "errors.h" #include "runtime.h" +#include "utils.h" +#include "silver/lexer.h" #define BIGINT_BASE ((uint64_t)0x100000000ULL) #define BIGINT_DEC_GROUP_BASE 1000000000U @@ -353,6 +355,168 @@ static uint32_t bigint_div_small_inplace(uint32_t *limbs, size_t count, uint32_t return (uint32_t)rem; } +static size_t bigint_abs_bitlen_limbs(const uint32_t *limbs, size_t count) { + while (count > 1 && limbs[count - 1] == 0) count--; + if (count == 1 && limbs[0] == 0) return 0; + + uint32_t top = limbs[count - 1]; +#if defined(__GNUC__) || defined(__clang__) + unsigned lead = (unsigned)__builtin_clz(top); +#else + unsigned lead = 0; + while ((top & 0x80000000u) == 0) { + top <<= 1; + lead++; + } +#endif + return (count - 1) * 32u + (32u - (size_t)lead); +} + +static uint32_t *bigint_to_twos_complement_limbs( + const uint32_t *limbs, + size_t count, + bool negative, + size_t width +) { + if (width == 0) width = 1; + + uint32_t *out = limb_alloc(width); + if (!out) return NULL; + + size_t copy_count = count < width ? count : width; + if (copy_count > 0) memcpy(out, limbs, copy_count * sizeof(uint32_t)); + + if (!negative) return out; + + for (size_t i = 0; i < width; i++) out[i] = ~out[i]; + + uint64_t carry = 1; + for (size_t i = 0; i < width && carry != 0; i++) { + uint64_t cur = (uint64_t)out[i] + carry; + out[i] = (uint32_t)cur; + carry = cur >> 32; + } + + return out; +} + +static uint32_t *bigint_from_twos_complement_limbs( + const uint32_t *twos, + size_t width, + bool *negative_out, + size_t *count_out +) { + if (width == 0) width = 1; + + bool negative = (twos[width - 1] & 0x80000000u) != 0; + uint32_t *mag = limb_dup(twos, width); + if (!mag) return NULL; + + if (negative) { + for (size_t i = 0; i < width; i++) mag[i] = ~mag[i]; + uint64_t carry = 1; + for (size_t i = 0; i < width && carry != 0; i++) { + uint64_t cur = (uint64_t)mag[i] + carry; + mag[i] = (uint32_t)cur; + carry = cur >> 32; + } + } + + size_t mcount = width; + bigint_normalize_limbs(mag, &mcount); + if (mcount == 1 && mag[0] == 0) negative = false; + + if (negative_out) *negative_out = negative; + if (count_out) *count_out = mcount; + return mag; +} + +typedef enum { + BIGINT_BAND = 0, + BIGINT_BOR, + BIGINT_BXOR +} bigint_bitop_t; + +static ant_value_t bigint_bitwise_binary(ant_t *js, ant_value_t a, ant_value_t b, bigint_bitop_t op) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + + size_t alen = 0, blen = 0; + const uint32_t *ad = bigint_limbs(js, a, &alen); + const uint32_t *bd = bigint_limbs(js, b, &blen); + + size_t abit = bigint_abs_bitlen_limbs(ad, alen); + size_t bbit = bigint_abs_bitlen_limbs(bd, blen); + size_t width_bits = (abit > bbit ? abit : bbit) + 1; + size_t width = (width_bits + 31u) / 32u; + if (width == 0) width = 1; + + uint32_t *at = bigint_to_twos_complement_limbs(ad, alen, aneg, width); + uint32_t *bt = bigint_to_twos_complement_limbs(bd, blen, bneg, width); + if (!at || !bt) { + free(at); + free(bt); + return js_mkerr(js, "oom"); + } + + for (size_t i = 0; i < width; i++) { + switch (op) { + case BIGINT_BAND: at[i] &= bt[i]; break; + case BIGINT_BOR: at[i] |= bt[i]; break; + case BIGINT_BXOR: at[i] ^= bt[i]; break; + } + } + + bool negative = false; + size_t mcount = 0; + uint32_t *mag = bigint_from_twos_complement_limbs(at, width, &negative, &mcount); + free(at); + free(bt); + if (!mag) return js_mkerr(js, "oom"); + + ant_value_t out = js_mkbigint_limbs(js, mag, mcount, negative); + free(mag); + return out; +} + +ant_value_t bigint_bitand(ant_t *js, ant_value_t a, ant_value_t b) { + return bigint_bitwise_binary(js, a, b, BIGINT_BAND); +} + +ant_value_t bigint_bitor(ant_t *js, ant_value_t a, ant_value_t b) { + return bigint_bitwise_binary(js, a, b, BIGINT_BOR); +} + +ant_value_t bigint_bitxor(ant_t *js, ant_value_t a, ant_value_t b) { + return bigint_bitwise_binary(js, a, b, BIGINT_BXOR); +} + +ant_value_t bigint_bitnot(ant_t *js, ant_value_t value) { + bool neg = bigint_is_negative(js, value); + size_t count = 0; + const uint32_t *limbs = bigint_limbs(js, value, &count); + + size_t bits = bigint_abs_bitlen_limbs(limbs, count); + size_t width_bits = bits + 1; + size_t width = (width_bits + 31u) / 32u; + if (width == 0) width = 1; + + uint32_t *twos = bigint_to_twos_complement_limbs(limbs, count, neg, width); + if (!twos) return js_mkerr(js, "oom"); + for (size_t i = 0; i < width; i++) twos[i] = ~twos[i]; + + bool out_neg = false; + size_t out_count = 0; + + uint32_t *mag = bigint_from_twos_complement_limbs(twos, width, &out_neg, &out_count); + free(twos); if (!mag) return js_mkerr(js, "oom"); + + ant_value_t out = js_mkbigint_limbs(js, mag, out_count, out_neg); + free(mag); + + return out; +} + static inline unsigned clz32_nonzero(uint32_t v) { #if defined(__GNUC__) || defined(__clang__) return (unsigned)__builtin_clz(v); @@ -569,55 +733,101 @@ static bool bigint_divmod_abs_limbs( return true; } -static ant_value_t bigint_from_decimal_digits(ant_t *js, const char *digits, size_t len, bool negative) { +static ant_value_t bigint_from_string_digits( + ant_t *js, + const char *digits, + size_t len, + bool negative, + bool allow_separators +) { if (!digits || len == 0) { uint32_t zero = 0; return js_mkbigint_limbs(js, &zero, 1, false); } - while (len > 1 && *digits == '0') { - digits++; - len--; + uint32_t base = 10; + size_t start = 0; + + if (len >= 2 && digits[0] == '0') { + char p = (char)(digits[1] | 0x20); + if (p == 'x') { + base = 16; + start = 2; + } else if (p == 'o') { + base = 8; + start = 2; + } else if (p == 'b') { + base = 2; + start = 2; + } } - size_t cap = len / 9 + 2; + if (start >= len) return js_mkerr(js, "Cannot convert string to BigInt"); + + size_t cap = len / 8 + 2; + if (cap < 4) cap = 4; uint32_t *limbs = limb_alloc(cap); if (!limbs) return js_mkerr(js, "oom"); size_t count = 1; + bool has_digit = false; + bool prev_sep = false; + + for (size_t i = start; i < len; i++) { + char ch = digits[i]; + + if (ch == '_') { + if (!allow_separators || !has_digit || prev_sep) { + free(limbs); + return js_mkerr(js, "Cannot convert string to BigInt"); + } + prev_sep = true; + continue; + } - for (size_t i = 0; i < len; i++) { - if (!is_decimal_digit(digits[i])) { + int digit = hex_digit(ch); + if (digit < 0 || (uint32_t)digit >= base) { free(limbs); return js_mkerr(js, "Cannot convert string to BigInt"); } - uint64_t carry = (uint64_t)(digits[i] - '0'); + has_digit = true; + prev_sep = false; + + uint64_t carry = (uint64_t)digit; for (size_t j = 0; j < count; j++) { - uint64_t cur = (uint64_t)limbs[j] * 10 + carry; + uint64_t cur = (uint64_t)limbs[j] * base + carry; limbs[j] = (uint32_t)cur; carry = cur >> 32; } if (carry != 0) { - if (count == cap) { - size_t new_cap = cap * 2; - uint32_t *new_limbs = (uint32_t *)realloc(limbs, new_cap * sizeof(uint32_t)); - if (!new_limbs) { - free(limbs); - return js_mkerr(js, "oom"); + while (carry != 0) { + if (count == cap) { + size_t new_cap = cap * 2; + uint32_t *new_limbs = (uint32_t *)realloc(limbs, new_cap * sizeof(uint32_t)); + if (!new_limbs) { + free(limbs); + return js_mkerr(js, "oom"); + } + + memset(new_limbs + cap, 0, (new_cap - cap) * sizeof(uint32_t)); + limbs = new_limbs; + cap = new_cap; } - memset(new_limbs + cap, 0, (new_cap - cap) * sizeof(uint32_t)); - limbs = new_limbs; - cap = new_cap; + limbs[count++] = (uint32_t)carry; + carry >>= 32; } - - limbs[count++] = (uint32_t)carry; } } + if (!has_digit || prev_sep) { + free(limbs); + return js_mkerr(js, "Cannot convert string to BigInt"); + } + ant_value_t result = js_mkbigint_limbs(js, limbs, count, negative); free(limbs); return result; @@ -757,7 +967,7 @@ static char *bigint_abs_to_radix_string(const uint32_t *limbs, size_t count, uin } ant_value_t js_mkbigint(ant_t *js, const char *digits, size_t len, bool negative) { - return bigint_from_decimal_digits(js, digits, len, negative); + return bigint_from_string_digits(js, digits, len, negative, true); } ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b) { @@ -1099,24 +1309,21 @@ static ant_value_t builtin_BigInt(ant_t *js, ant_value_t *args, int nargs) { ant_offset_t off = vstr(js, arg, &slen); const char *str = (const char *)&js->mem[off]; - bool neg = false; - size_t i = 0; + size_t start = 0; + size_t end = slen; + + while (start < end && is_space((unsigned char)str[start])) start++; + while (end > start && is_space((unsigned char)str[end - 1])) end--; + if (start >= end) return js_mkbigint(js, "0", 1, false); - if (slen > 0 && str[0] == '-') { + bool neg = false; + if (str[start] == '-') { neg = true; - i++; - } else if (slen > 0 && str[0] == '+') { - i++; - } - - while (i < slen && str[i] == '0') i++; - if (i >= slen) return js_mkbigint(js, "0", 1, false); - - for (size_t j = i; j < slen; j++) { - if (!is_decimal_digit(str[j])) return js_mkerr(js, "Cannot convert string to BigInt"); - } + start++; + } else if (str[start] == '+') start++; - return js_mkbigint(js, str + i, slen - i, neg); + if (start >= end) return js_mkerr(js, "Cannot convert string to BigInt"); + return bigint_from_string_digits(js, str + start, end - start, neg, false); } if (vtype(arg) == T_BOOL) return js_mkbigint(js, vdata(arg) ? "1" : "0", 1, false); diff --git a/src/silver/engine.c b/src/silver/engine.c index 4a9aa3c..7ecb915 100644 --- a/src/silver/engine.c +++ b/src/silver/engine.c @@ -656,7 +656,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_BAND: { VM_CHECK(sv_op_band(vm, js)); NEXT(1); } L_BOR: { VM_CHECK(sv_op_bor(vm, js)); NEXT(1); } L_BXOR: { VM_CHECK(sv_op_bxor(vm, js)); NEXT(1); } - L_BNOT: { sv_op_bnot(vm, js); NEXT(1); } + L_BNOT: { VM_CHECK(sv_op_bnot(vm, js)); NEXT(1); } L_SHL: { VM_CHECK(sv_op_shl(vm, js)); NEXT(1); } L_SHR: { VM_CHECK(sv_op_shr(vm, js)); NEXT(1); } L_USHR: { VM_CHECK(sv_op_ushr(vm, js)); NEXT(1); } diff --git a/src/silver/ops/bitwise.h b/src/silver/ops/bitwise.h index fa7c66f..6a83d53 100644 --- a/src/silver/ops/bitwise.h +++ b/src/silver/ops/bitwise.h @@ -12,7 +12,10 @@ static inline ant_value_t sv_op_band(sv_vm_t *vm, ant_t *js) { if (lt == T_BIGINT || rt == T_BIGINT) { if (lt != T_BIGINT || rt != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - return js_mkerr_typed(js, JS_ERR_TYPE, "BigInt does not support bitwise ops"); + ant_value_t res = bigint_bitand(js, l, r); + if (is_err(res)) return res; + vm->stack[vm->sp++] = res; + return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); @@ -27,7 +30,10 @@ static inline ant_value_t sv_op_bor(sv_vm_t *vm, ant_t *js) { if (lt == T_BIGINT || rt == T_BIGINT) { if (lt != T_BIGINT || rt != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - return js_mkerr_typed(js, JS_ERR_TYPE, "BigInt does not support bitwise ops"); + ant_value_t res = bigint_bitor(js, l, r); + if (is_err(res)) return res; + vm->stack[vm->sp++] = res; + return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); @@ -42,7 +48,10 @@ static inline ant_value_t sv_op_bxor(sv_vm_t *vm, ant_t *js) { if (lt == T_BIGINT || rt == T_BIGINT) { if (lt != T_BIGINT || rt != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - return js_mkerr_typed(js, JS_ERR_TYPE, "BigInt does not support bitwise ops"); + ant_value_t res = bigint_bitxor(js, l, r); + if (is_err(res)) return res; + vm->stack[vm->sp++] = res; + return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); @@ -50,9 +59,16 @@ static inline ant_value_t sv_op_bxor(sv_vm_t *vm, ant_t *js) { return tov(0); } -static inline void sv_op_bnot(sv_vm_t *vm, ant_t *js) { +static inline ant_value_t sv_op_bnot(sv_vm_t *vm, ant_t *js) { ant_value_t a = vm->stack[--vm->sp]; + if (vtype(a) == T_BIGINT) { + ant_value_t res = bigint_bitnot(js, a); + if (is_err(res)) return res; + vm->stack[vm->sp++] = res; + return tov(0); + } vm->stack[vm->sp++] = tov((double)(~js_to_int32(js_to_number(js, a)))); + return tov(0); } static inline ant_value_t sv_op_shl(sv_vm_t *vm, ant_t *js) { diff --git a/tests/bench_bigint.js b/tests/bench_bigint.js new file mode 100644 index 0000000..b94ecf7 --- /dev/null +++ b/tests/bench_bigint.js @@ -0,0 +1,97 @@ +const now = () => (typeof performance !== 'undefined' && typeof performance.now === 'function' ? performance.now() : Date.now()); + +if (typeof BigInt !== 'function') { + console.log('BigInt is not available in this runtime.'); +} else { + console.log('BigInt Bench (new feature coverage)'); + + let sinkBig = 0n; + let sinkNum = 0; + const HASH_BIAS = 11400714819323198485n; + + const mixBig = x => { + sinkBig ^= x + HASH_BIAS; + }; + + const mixNum = x => { + sinkNum = (sinkNum * 1664525 + (x >>> 0) + 1013904223) >>> 0; + }; + + function bench(name, iterations, fn) { + const start = now(); + for (let i = 0; i < iterations; i++) fn(i); + const ms = now() - start; + const opsPerSec = ms > 0 ? Math.round((iterations * 1000) / ms) : 0; + console.log(`${name}: ${ms.toFixed(2)}ms (${opsPerSec} ops/s)`); + } + + const prefHex = BigInt('0xff'); + const prefBin = BigInt('0b1010101010101010'); + const prefOct = BigInt('0o777777'); + const trimmed = BigInt(' 123456789 '); + const literalSeed = 0xffn + 0b1010n + 0o77n + trimmed; + + const a0 = (1n << 260n) + (1n << 129n) + 12345678901234567890123n + literalSeed; + const b0 = (1n << 192n) + (1n << 67n) + 987654321123456789n + prefHex + prefBin; + const c0 = (1n << 73n) + 12345n + prefOct; + + bench('parse prefixed strings x120000', 120000, i => { + const v = BigInt('0xff') + BigInt('0b1011') + BigInt('0o77') + BigInt(' 42 ') + BigInt(''); + mixBig(v + BigInt(i & 31)); + }); + + bench('prefixed literal arith x180000', 180000, i => { + const v = (0xffn << 12n) + 0b101010n + 0o777n + BigInt(i); + mixBig(v ^ literalSeed); + }); + + bench('add/sub x300000', 300000, i => { + const ai = a0 + BigInt(i); + const bi = b0 - BigInt(i); + mixBig(ai + bi - c0); + }); + + bench('mul x70000', 70000, i => { + const x = (a0 + BigInt(i)) * (c0 + BigInt((i & 31) + 1)); + mixBig(x >> 11n); + }); + + bench('div/mod x70000', 70000, i => { + const x = (a0 << 17n) + BigInt(i); + const q = x / c0; + const r = x % c0; + mixBig(q ^ r); + }); + + bench('shift x250000', 250000, i => { + const s1 = BigInt(i & 63); + const s2 = BigInt((i + 7) & 63); + const x = (a0 << s1) >> s2; + mixBig(x); + }); + + bench('toString(10) x4000', 4000, i => { + const s = (a0 + BigInt(i)).toString(10); + mixNum(s.length + s.charCodeAt(0)); + }); + + bench('toString(16) x6000', 6000, i => { + const s = (b0 + BigInt(i)).toString(16); + mixNum(s.length + s.charCodeAt(s.length - 1)); + }); + + bench('toString(2) x1200', 1200, i => { + const s = (c0 << BigInt((i & 255) + 128)).toString(2); + mixNum(s.length); + }); + + bench('asIntN/asUintN x250000', 250000, i => { + const x = a0 + BigInt(i); + const y = BigInt.asIntN(127, x); + const z = BigInt.asUintN(127, -x); + mixBig(y ^ z); + }); + + console.log('sinkBig:', sinkBig.toString(16)); + console.log('sinkNum:', sinkNum); +} diff --git a/tests/test_bigint.js b/tests/test_bigint.js index 4c0f88a..a1654d1 100644 --- a/tests/test_bigint.js +++ b/tests/test_bigint.js @@ -41,10 +41,19 @@ test('BigInt(0)', BigInt(0), '0'); test('BigInt(-5)', BigInt(-5), '-5'); test('BigInt("123")', BigInt("123"), '123'); test('BigInt("-456")', BigInt("-456"), '-456'); +test('BigInt("0xff")', BigInt("0xff"), '255'); +test('BigInt("0b1010")', BigInt("0b1010"), '10'); +test('BigInt("0o77")', BigInt("0o77"), '63'); +test('BigInt(" 42 ")', BigInt(" 42 "), '42'); +test('BigInt("")', BigInt(""), '0'); test('BigInt(true)', BigInt(true), '1'); test('BigInt(false)', BigInt(false), '0'); +testThrows('BigInt("0x") throws', () => BigInt("0x"), 'Cannot convert string to BigInt'); +testThrows('BigInt("1_2") throws', () => BigInt("1_2"), 'Cannot convert string to BigInt'); console.log('\n=== BigInt Addition ==='); +test('0xffn + 1n', 0xffn + 1n, '256'); +test('0b1010n + 0o7n', 0b1010n + 0o7n, '17'); test('1n + 2n', 1n + 2n, '3'); test('100n + 200n', 100n + 200n, '300'); test('999999999999999999n + 1n', 999999999999999999n + 1n, '1000000000000000000'); @@ -80,6 +89,18 @@ test('100n % 7n', 100n % 7n, '2'); test('-10n % 3n', -10n % 3n, '-1'); test('10n % (-3n)', 10n % (-3n), '1'); +console.log('\n=== BigInt Bitwise ==='); +test('5n & 3n', 5n & 3n, '1'); +test('5n | 2n', 5n | 2n, '7'); +test('5n ^ 1n', 5n ^ 1n, '4'); +test('~0n', ~0n, '-1'); +test('~(-1n)', ~(-1n), '0'); +test('(-5n) & 3n', (-5n) & 3n, '3'); +test('(-8n) | 3n', (-8n) | 3n, '-5'); +test('(-8n) ^ 3n', (-8n) ^ 3n, '-5'); +test('0xffn ^ 0b1010n', 0xffn ^ 0b1010n, '245'); +testThrows('1n & 1 throws mixed types', () => (1n & 1), 'Cannot mix BigInt value and other types'); + console.log('\n=== BigInt Comparison ==='); test('5n == 5n', 5n == 5n, true); test('5n == 6n', 5n == 6n, false); -- 2.51.2