]> git.kaiwu.me - quickjs.git/commitdiff
removed bignum support and qjscalc - added optimized BigInt implementation
authorFabrice Bellard <fabrice@bellard.org>
Tue, 18 Mar 2025 17:29:10 +0000 (18:29 +0100)
committerFabrice Bellard <fabrice@bellard.org>
Tue, 18 Mar 2025 17:29:10 +0000 (18:29 +0100)
20 files changed:
Makefile
TODO
cutils.h
examples/pi_bigdecimal.js [deleted file]
examples/pi_bigfloat.js [deleted file]
libbf.c [deleted file]
libbf.h [deleted file]
qjs.c
qjsc.c
qjscalc.js [deleted file]
quickjs-atom.h
quickjs-opcode.h
quickjs.c
quickjs.h
tests/microbench.js
tests/test_bigfloat.js [deleted file]
tests/test_bigint.js [new file with mode: 0644]
tests/test_bignum.js [deleted file]
tests/test_op_overloading.js [deleted file]
tests/test_qjscalc.js [deleted file]

index cf88a723a27a5f5ccd1956271b2a1a981a85f4e6..a309cb42e25ba9305462565d33b7c8e6baf0c3c7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -51,9 +51,6 @@ PREFIX?=/usr/local
 # use UB sanitizer
 #CONFIG_UBSAN=y
 
-# include the code for BigFloat/BigDecimal and math mode
-CONFIG_BIGNUM=y
-
 OBJDIR=.obj
 
 ifdef CONFIG_ASAN
@@ -137,9 +134,6 @@ ifdef CONFIG_WERROR
 CFLAGS+=-Werror
 endif
 DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
-ifdef CONFIG_BIGNUM
-DEFINES+=-DCONFIG_BIGNUM
-endif
 ifdef CONFIG_WIN32
 DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
 endif
@@ -201,9 +195,6 @@ else
 QJSC_CC=$(CC)
 QJSC=./qjsc$(EXE)
 endif
-ifndef CONFIG_WIN32
-PROGS+=qjscalc
-endif
 ifdef CONFIG_M32
 PROGS+=qjs32 qjs32_s
 endif
@@ -228,12 +219,9 @@ endif
 
 all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
 
-QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o $(OBJDIR)/libbf.o
+QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
 
 QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
-ifdef CONFIG_BIGNUM
-QJS_OBJS+=$(OBJDIR)/qjscalc.o
-endif
 
 HOST_LIBS=-lm -ldl -lpthread
 LIBS=-lm
@@ -289,9 +277,6 @@ qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
        $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
        @size $@
 
-qjscalc: qjs
-       ln -sf $< $@
-
 ifdef CONFIG_LTO
 LTOEXT=.lto
 else
@@ -312,9 +297,6 @@ libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
 repl.c: $(QJSC) repl.js
        $(QJSC) -c -o $@ -m repl.js
 
-qjscalc.c: $(QJSC) qjscalc.js
-       $(QJSC) -fbignum -c -o $@ qjscalc.js
-
 ifneq ($(wildcard unicode/UnicodeData.txt),)
 $(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \
     $(OBJDIR)/libunicode.nolto.o: libunicode-table.h
@@ -371,7 +353,7 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
        $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o
 
 clean:
-       rm -f repl.c qjscalc.c out.c
+       rm -f repl.c out.c
        rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
        rm -f hello.c test_fib.c
        rm -f examples/*.so tests/*.so
@@ -383,7 +365,6 @@ install: all
        mkdir -p "$(DESTDIR)$(PREFIX)/bin"
        $(STRIP) qjs$(EXE) qjsc$(EXE)
        install -m755 qjs$(EXE) qjsc$(EXE) "$(DESTDIR)$(PREFIX)/bin"
-       ln -sf qjs$(EXE) "$(DESTDIR)$(PREFIX)/bin/qjscalc$(EXE)"
        mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
        install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
 ifdef CONFIG_LTO
@@ -468,35 +449,21 @@ test: qjs
        ./qjs tests/test_language.js
        ./qjs --std tests/test_builtin.js
        ./qjs tests/test_loop.js
-       ./qjs tests/test_bignum.js
+       ./qjs tests/test_bigint.js
        ./qjs tests/test_std.js
        ./qjs tests/test_worker.js
 ifdef CONFIG_SHARED_LIBS
-ifdef CONFIG_BIGNUM
-       ./qjs --bignum tests/test_bjson.js
-else
        ./qjs tests/test_bjson.js
-endif
        ./qjs examples/test_point.js
 endif
-ifdef CONFIG_BIGNUM
-       ./qjs --bignum tests/test_op_overloading.js
-       ./qjs --bignum tests/test_bigfloat.js
-       ./qjs --qjscalc tests/test_qjscalc.js
-endif
 ifdef CONFIG_M32
        ./qjs32 tests/test_closure.js
        ./qjs32 tests/test_language.js
        ./qjs32 --std tests/test_builtin.js
        ./qjs32 tests/test_loop.js
-       ./qjs32 tests/test_bignum.js
+       ./qjs32 tests/test_bigint.js
        ./qjs32 tests/test_std.js
        ./qjs32 tests/test_worker.js
-ifdef CONFIG_BIGNUM
-       ./qjs32 --bignum tests/test_op_overloading.js
-       ./qjs32 --bignum tests/test_bigfloat.js
-       ./qjs32 --qjscalc tests/test_qjscalc.js
-endif
 endif
 
 stats: qjs qjs32
@@ -556,7 +523,7 @@ node-test:
        node tests/test_language.js
        node tests/test_builtin.js
        node tests/test_loop.js
-       node tests/test_bignum.js
+       node tests/test_bigint.js
 
 node-microbench:
        node tests/microbench.js -s microbench-node.txt
diff --git a/TODO b/TODO
index f243dee4a1ccbc18f4e9a549bdaf59e72cac0577..dcf0bcffdadf96055c9682aed340f613c30eda86 100644 (file)
--- a/TODO
+++ b/TODO
@@ -38,7 +38,6 @@ REPL:
 
 Optimization ideas:
 - 64-bit atoms in 64-bit mode ?
-- 64-bit small bigint in 64-bit mode ?
 - reuse stack slots for disjoint scopes, if strip
 - add heuristic to avoid some cycles in closures
 - small String (0-2 charcodes) with immediate storage
index f079e5c5ea57c892f9386e7dfc20041233d82bf5..32b97579db695e8873e3a3b69060c60c8e1c7dd5 100644 (file)
--- a/cutils.h
+++ b/cutils.h
@@ -344,4 +344,24 @@ void rqsort(void *base, size_t nmemb, size_t size,
             int (*cmp)(const void *, const void *, void *),
             void *arg);
 
+static inline uint64_t float64_as_uint64(double d)
+{
+    union {
+        double d;
+        uint64_t u64;
+    } u;
+    u.d = d;
+    return u.u64;
+}
+
+static inline double uint64_as_float64(uint64_t u64)
+{
+    union {
+        double d;
+        uint64_t u64;
+    } u;
+    u.u64 = u64;
+    return u.d;
+}
+
 #endif  /* CUTILS_H */
diff --git a/examples/pi_bigdecimal.js b/examples/pi_bigdecimal.js
deleted file mode 100644 (file)
index 7cb7ad6..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * PI computation in Javascript using the QuickJS bigdecimal type
- * (decimal floating point)
- */
-"use strict";
-
-/* compute PI with a precision of 'prec' digits */
-function calc_pi(prec) {
-    const CHUD_A = 13591409m;
-    const CHUD_B = 545140134m;
-    const CHUD_C = 640320m;
-    const CHUD_C3 = 10939058860032000m; /* C^3/24 */
-    const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
-
-    /* return [P, Q, G] */
-    function chud_bs(a, b, need_G) {
-        var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
-        if (a == (b - 1n)) {
-            b1 = BigDecimal(b);
-            G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
-            P = G * (CHUD_B * b1 + CHUD_A);
-            if (b & 1n)
-                P = -P;
-            G = G;
-            Q = b1 * b1 * b1 * CHUD_C3;
-        } else {
-            c = (a + b) >> 1n;
-            [P1, Q1, G1] = chud_bs(a, c, true);
-            [P2, Q2, G2] = chud_bs(c, b, need_G);
-            P = P1 * Q2 + P2 * G1;
-            Q = Q1 * Q2;
-            if (need_G)
-                G = G1 * G2;
-            else
-                G = 0m;
-        }
-        return [P, Q, G];
-    }
-
-    var n, P, Q, G;
-    /* number of serie terms */
-    n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n;
-    [P, Q, G] = chud_bs(0n, n, false);
-    Q = BigDecimal.div(Q, (P + Q * CHUD_A),
-                       { roundingMode: "half-even",
-                         maximumSignificantDigits: prec });
-    G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
-                                         { roundingMode: "half-even",
-                                           maximumSignificantDigits: prec });
-    return Q * G;
-}
-
-(function() {
-    var r, n_digits, n_bits;
-    if (typeof scriptArgs != "undefined") {
-        if (scriptArgs.length < 2) {
-            print("usage: pi n_digits");
-            return;
-        }
-        n_digits = scriptArgs[1] | 0;
-    } else {
-        n_digits = 1000;
-    }
-    /* we add more digits to reduce the probability of bad rounding for
-       the last digits */
-    r = calc_pi(n_digits + 20);
-    print(r.toFixed(n_digits, "down"));
-})();
diff --git a/examples/pi_bigfloat.js b/examples/pi_bigfloat.js
deleted file mode 100644 (file)
index 8372379..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * PI computation in Javascript using the QuickJS bigfloat type
- * (binary floating point)
- */
-"use strict";
-
-/* compute PI with a precision of 'prec' bits */
-function calc_pi() {
-    const CHUD_A = 13591409n;
-    const CHUD_B = 545140134n;
-    const CHUD_C = 640320n;
-    const CHUD_C3 = 10939058860032000n; /* C^3/24 */
-    const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
-
-    /* return [P, Q, G] */
-    function chud_bs(a, b, need_G) {
-        var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
-        if (a == (b - 1n)) {
-            G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
-            P = BigFloat(G * (CHUD_B * b + CHUD_A));
-            if (b & 1n)
-                P = -P;
-            G = BigFloat(G);
-            Q = BigFloat(b * b * b * CHUD_C3);
-        } else {
-            c = (a + b) >> 1n;
-            [P1, Q1, G1] = chud_bs(a, c, true);
-            [P2, Q2, G2] = chud_bs(c, b, need_G);
-            P = P1 * Q2 + P2 * G1;
-            Q = Q1 * Q2;
-            if (need_G)
-                G = G1 * G2;
-            else
-                G = 0l;
-        }
-        return [P, Q, G];
-    }
-
-    var n, P, Q, G;
-    /* number of serie terms */
-    n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n;
-    [P, Q, G] = chud_bs(0n, n, false);
-    Q = Q / (P + Q * BigFloat(CHUD_A));
-    G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C));
-    return Q * G;
-}
-
-(function() {
-    var r, n_digits, n_bits;
-    if (typeof scriptArgs != "undefined") {
-        if (scriptArgs.length < 2) {
-            print("usage: pi n_digits");
-            return;
-        }
-        n_digits = scriptArgs[1];
-    } else {
-        n_digits = 1000;
-    }
-    n_bits = Math.ceil(n_digits * Math.log2(10));
-    /* we add more bits to reduce the probability of bad rounding for
-       the last digits */
-    BigFloatEnv.setPrec( () => {
-        r = calc_pi();
-        print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
-    }, n_bits + 32);
-})();
diff --git a/libbf.c b/libbf.c
deleted file mode 100644 (file)
index 05d62ed..0000000
--- a/libbf.c
+++ /dev/null
@@ -1,8475 +0,0 @@
-/*
- * Tiny arbitrary precision floating point library
- *
- * Copyright (c) 2017-2021 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <math.h>
-#include <string.h>
-#include <assert.h>
-
-#ifdef __AVX2__
-#include <immintrin.h>
-#endif
-
-#include "cutils.h"
-#include "libbf.h"
-
-/* enable it to check the multiplication result */
-//#define USE_MUL_CHECK
-#ifdef CONFIG_BIGNUM
-/* enable it to use FFT/NTT multiplication */
-#define USE_FFT_MUL
-/* enable decimal floating point support */
-#define USE_BF_DEC
-#endif
-
-//#define inline __attribute__((always_inline))
-
-#ifdef __AVX2__
-#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */
-#else
-#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */
-#endif
-
-/* XXX: adjust */
-#define DIVNORM_LARGE_THRESHOLD 50
-#define UDIV1NORM_THRESHOLD 3
-
-#if LIMB_BITS == 64
-#define FMT_LIMB1 "%" PRIx64
-#define FMT_LIMB "%016" PRIx64
-#define PRId_LIMB PRId64
-#define PRIu_LIMB PRIu64
-
-#else
-
-#define FMT_LIMB1 "%x"
-#define FMT_LIMB "%08x"
-#define PRId_LIMB "d"
-#define PRIu_LIMB "u"
-
-#endif
-
-typedef intptr_t mp_size_t;
-
-typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                          bf_flags_t flags);
-
-#ifdef USE_FFT_MUL
-
-#define FFT_MUL_R_OVERLAP_A (1 << 0)
-#define FFT_MUL_R_OVERLAP_B (1 << 1)
-#define FFT_MUL_R_NORESIZE  (1 << 2)
-
-static no_inline int fft_mul(bf_context_t *s,
-                             bf_t *res, limb_t *a_tab, limb_t a_len,
-                             limb_t *b_tab, limb_t b_len, int mul_flags);
-static void fft_clear_cache(bf_context_t *s);
-#endif
-#ifdef USE_BF_DEC
-static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos);
-#endif
-
-
-/* could leading zeros */
-static inline int clz(limb_t a)
-{
-    if (a == 0) {
-        return LIMB_BITS;
-    } else {
-#if LIMB_BITS == 64
-        return clz64(a);
-#else
-        return clz32(a);
-#endif
-    }
-}
-
-static inline int ctz(limb_t a)
-{
-    if (a == 0) {
-        return LIMB_BITS;
-    } else {
-#if LIMB_BITS == 64
-        return ctz64(a);
-#else
-        return ctz32(a);
-#endif
-    }
-}
-
-static inline int ceil_log2(limb_t a)
-{
-    if (a <= 1)
-        return 0;
-    else
-        return LIMB_BITS - clz(a - 1);
-}
-
-/* b must be >= 1 */
-static inline slimb_t ceil_div(slimb_t a, slimb_t b)
-{
-    if (a >= 0)
-        return (a + b - 1) / b;
-    else
-        return a / b;
-}
-
-#ifdef USE_BF_DEC
-/* b must be >= 1 */
-static inline slimb_t floor_div(slimb_t a, slimb_t b)
-{
-    if (a >= 0) {
-        return a / b;
-    } else {
-        return (a - b + 1) / b;
-    }
-}
-#endif
-
-/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */
-static inline limb_t smod(slimb_t a, slimb_t b)
-{
-    a = a % (slimb_t)b;
-    if (a < 0)
-        a += b;
-    return a;
-}
-
-/* signed addition with saturation */
-static inline slimb_t sat_add(slimb_t a, slimb_t b)
-{
-    slimb_t r;
-    r = a + b;
-    /* overflow ? */
-    if (((a ^ r) & (b ^ r)) < 0)
-        r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1);
-    return r;
-}
-
-static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift)
-{
-    if (shift != 0)
-        low = (low >> shift) | (high << (LIMB_BITS - shift));
-    return low;
-}
-
-static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift)
-{
-    if (shift != 0)
-        return (a1 << shift) | (a0 >> (LIMB_BITS - shift));
-    else
-        return a1;
-}
-
-#define malloc(s) malloc_is_forbidden(s)
-#define free(p) free_is_forbidden(p)
-#define realloc(p, s) realloc_is_forbidden(p, s)
-
-void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
-                     void *realloc_opaque)
-{
-    memset(s, 0, sizeof(*s));
-    s->realloc_func = realloc_func;
-    s->realloc_opaque = realloc_opaque;
-}
-
-void bf_context_end(bf_context_t *s)
-{
-    bf_clear_cache(s);
-}
-
-void bf_init(bf_context_t *s, bf_t *r)
-{
-    r->ctx = s;
-    r->sign = 0;
-    r->expn = BF_EXP_ZERO;
-    r->len = 0;
-    r->tab = NULL;
-}
-
-/* return 0 if OK, -1 if alloc error */
-int bf_resize(bf_t *r, limb_t len)
-{
-    limb_t *tab;
-
-    if (len != r->len) {
-        tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t));
-        if (!tab && len != 0)
-            return -1;
-        r->tab = tab;
-        r->len = len;
-    }
-    return 0;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set_ui(bf_t *r, uint64_t a)
-{
-    r->sign = 0;
-    if (a == 0) {
-        r->expn = BF_EXP_ZERO;
-        bf_resize(r, 0); /* cannot fail */
-    }
-#if LIMB_BITS == 32
-    else if (a <= 0xffffffff)
-#else
-    else
-#endif
-    {
-        int shift;
-        if (bf_resize(r, 1))
-            goto fail;
-        shift = clz(a);
-        r->tab[0] = a << shift;
-        r->expn = LIMB_BITS - shift;
-    }
-#if LIMB_BITS == 32
-    else {
-        uint32_t a1, a0;
-        int shift;
-        if (bf_resize(r, 2))
-            goto fail;
-        a0 = a;
-        a1 = a >> 32;
-        shift = clz(a1);
-        r->tab[0] = a0 << shift;
-        r->tab[1] = shld(a1, a0, shift);
-        r->expn = 2 * LIMB_BITS - shift;
-    }
-#endif
-    return 0;
- fail:
-    bf_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set_si(bf_t *r, int64_t a)
-{
-    int ret;
-
-    if (a < 0) {
-        ret = bf_set_ui(r, -a);
-        r->sign = 1;
-    } else {
-        ret = bf_set_ui(r, a);
-    }
-    return ret;
-}
-
-void bf_set_nan(bf_t *r)
-{
-    bf_resize(r, 0); /* cannot fail */
-    r->expn = BF_EXP_NAN;
-    r->sign = 0;
-}
-
-void bf_set_zero(bf_t *r, int is_neg)
-{
-    bf_resize(r, 0); /* cannot fail */
-    r->expn = BF_EXP_ZERO;
-    r->sign = is_neg;
-}
-
-void bf_set_inf(bf_t *r, int is_neg)
-{
-    bf_resize(r, 0); /* cannot fail */
-    r->expn = BF_EXP_INF;
-    r->sign = is_neg;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set(bf_t *r, const bf_t *a)
-{
-    if (r == a)
-        return 0;
-    if (bf_resize(r, a->len)) {
-        bf_set_nan(r);
-        return BF_ST_MEM_ERROR;
-    }
-    r->sign = a->sign;
-    r->expn = a->expn;
-    memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t));
-    return 0;
-}
-
-/* equivalent to bf_set(r, a); bf_delete(a) */
-void bf_move(bf_t *r, bf_t *a)
-{
-    bf_context_t *s = r->ctx;
-    if (r == a)
-        return;
-    bf_free(s, r->tab);
-    *r = *a;
-}
-
-static limb_t get_limbz(const bf_t *a, limb_t idx)
-{
-    if (idx >= a->len)
-        return 0;
-    else
-        return a->tab[idx];
-}
-
-/* get LIMB_BITS at bit position 'pos' in tab */
-static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos)
-{
-    limb_t i, a0, a1;
-    int p;
-
-    i = pos >> LIMB_LOG2_BITS;
-    p = pos & (LIMB_BITS - 1);
-    if (i < len)
-        a0 = tab[i];
-    else
-        a0 = 0;
-    if (p == 0) {
-        return a0;
-    } else {
-        i++;
-        if (i < len)
-            a1 = tab[i];
-        else
-            a1 = 0;
-        return (a0 >> p) | (a1 << (LIMB_BITS - p));
-    }
-}
-
-static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos)
-{
-    slimb_t i;
-    i = pos >> LIMB_LOG2_BITS;
-    if (i < 0 || i >= len)
-        return 0;
-    return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1;
-}
-
-static inline limb_t limb_mask(int start, int last)
-{
-    limb_t v;
-    int n;
-    n = last - start + 1;
-    if (n == LIMB_BITS)
-        v = -1;
-    else
-        v = (((limb_t)1 << n) - 1) << start;
-    return v;
-}
-
-static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n)
-{
-    mp_size_t i;
-    for(i = 0; i < n; i++) {
-        if (tab[i] != 0)
-            return 1;
-    }
-    return 0;
-}
-
-/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */
-static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos)
-{
-    slimb_t pos;
-    limb_t v;
-
-    pos = bit_pos >> LIMB_LOG2_BITS;
-    if (pos < 0)
-        return 0;
-    v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1));
-    if (v != 0)
-        return 1;
-    pos--;
-    while (pos >= 0) {
-        if (r->tab[pos] != 0)
-            return 1;
-        pos--;
-    }
-    return 0;
-}
-
-/* return the addend for rounding. Note that prec can be <= 0 (for
-   BF_FLAG_RADPNT_PREC) */
-static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l,
-                          slimb_t prec, int rnd_mode)
-{
-    int add_one, inexact;
-    limb_t bit1, bit0;
-
-    if (rnd_mode == BF_RNDF) {
-        bit0 = 1; /* faithful rounding does not honor the INEXACT flag */
-    } else {
-        /* starting limb for bit 'prec + 1' */
-        bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1));
-    }
-
-    /* get the bit at 'prec' */
-    bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec);
-    inexact = (bit1 | bit0) != 0;
-
-    add_one = 0;
-    switch(rnd_mode) {
-    case BF_RNDZ:
-        break;
-    case BF_RNDN:
-        if (bit1) {
-            if (bit0) {
-                add_one = 1;
-            } else {
-                /* round to even */
-                add_one =
-                    get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1));
-            }
-        }
-        break;
-    case BF_RNDD:
-    case BF_RNDU:
-        if (r->sign == (rnd_mode == BF_RNDD))
-            add_one = inexact;
-        break;
-    case BF_RNDA:
-        add_one = inexact;
-        break;
-    case BF_RNDNA:
-    case BF_RNDF:
-        add_one = bit1;
-        break;
-    default:
-        abort();
-    }
-
-    if (inexact)
-        *pret |= BF_ST_INEXACT;
-    return add_one;
-}
-
-static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags)
-{
-    slimb_t i, l, e_max;
-    int rnd_mode;
-
-    rnd_mode = flags & BF_RND_MASK;
-    if (prec == BF_PREC_INF ||
-        rnd_mode == BF_RNDN ||
-        rnd_mode == BF_RNDNA ||
-        rnd_mode == BF_RNDA ||
-        (rnd_mode == BF_RNDD && sign == 1) ||
-        (rnd_mode == BF_RNDU && sign == 0)) {
-        bf_set_inf(r, sign);
-    } else {
-        /* set to maximum finite number */
-        l = (prec + LIMB_BITS - 1) / LIMB_BITS;
-        if (bf_resize(r, l)) {
-            bf_set_nan(r);
-            return BF_ST_MEM_ERROR;
-        }
-        r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1),
-                              LIMB_BITS - 1);
-        for(i = 1; i < l; i++)
-            r->tab[i] = (limb_t)-1;
-        e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
-        r->expn = e_max;
-        r->sign = sign;
-    }
-    return BF_ST_OVERFLOW | BF_ST_INEXACT;
-}
-
-/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is
-   assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be
-   infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result
-   is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of
-   overflow not returning infinity. */
-static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,
-                      int ret)
-{
-    limb_t v, a;
-    int shift, add_one, rnd_mode;
-    slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;
-
-    /* e_min and e_max are computed to match the IEEE 754 conventions */
-    e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
-    e_min = -e_range + 3;
-    e_max = e_range;
-
-    if (flags & BF_FLAG_RADPNT_PREC) {
-        /* 'prec' is the precision after the radix point */
-        if (prec1 != BF_PREC_INF)
-            prec = r->expn + prec1;
-        else
-            prec = prec1;
-    } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
-        /* restrict the precision in case of potentially subnormal
-           result */
-        assert(prec1 != BF_PREC_INF);
-        prec = prec1 - (e_min - r->expn);
-    } else {
-        prec = prec1;
-    }
-
-    /* round to prec bits */
-    rnd_mode = flags & BF_RND_MASK;
-    add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode);
-
-    if (prec <= 0) {
-        if (add_one) {
-            bf_resize(r, 1); /* cannot fail */
-            r->tab[0] = (limb_t)1 << (LIMB_BITS - 1);
-            r->expn += 1 - prec;
-            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
-            return ret;
-        } else {
-            goto underflow;
-        }
-    } else if (add_one) {
-        limb_t carry;
-
-        /* add one starting at digit 'prec - 1' */
-        bit_pos = l * LIMB_BITS - 1 - (prec - 1);
-        pos = bit_pos >> LIMB_LOG2_BITS;
-        carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1));
-
-        for(i = pos; i < l; i++) {
-            v = r->tab[i] + carry;
-            carry = (v < carry);
-            r->tab[i] = v;
-            if (carry == 0)
-                break;
-        }
-        if (carry) {
-            /* shift right by one digit */
-            v = 1;
-            for(i = l - 1; i >= pos; i--) {
-                a = r->tab[i];
-                r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1));
-                v = a;
-            }
-            r->expn++;
-        }
-    }
-
-    /* check underflow */
-    if (unlikely(r->expn < e_min)) {
-        if (flags & BF_FLAG_SUBNORMAL) {
-            /* if inexact, also set the underflow flag */
-            if (ret & BF_ST_INEXACT)
-                ret |= BF_ST_UNDERFLOW;
-        } else {
-        underflow:
-            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
-            bf_set_zero(r, r->sign);
-            return ret;
-        }
-    }
-
-    /* check overflow */
-    if (unlikely(r->expn > e_max))
-        return bf_set_overflow(r, r->sign, prec1, flags);
-
-    /* keep the bits starting at 'prec - 1' */
-    bit_pos = l * LIMB_BITS - 1 - (prec - 1);
-    i = bit_pos >> LIMB_LOG2_BITS;
-    if (i >= 0) {
-        shift = bit_pos & (LIMB_BITS - 1);
-        if (shift != 0)
-            r->tab[i] &= limb_mask(shift, LIMB_BITS - 1);
-    } else {
-        i = 0;
-    }
-    /* remove trailing zeros */
-    while (r->tab[i] == 0)
-        i++;
-    if (i > 0) {
-        l -= i;
-        memmove(r->tab, r->tab + i, l * sizeof(limb_t));
-    }
-    bf_resize(r, l); /* cannot fail */
-    return ret;
-}
-
-/* 'r' must be a finite number. */
-int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags)
-{
-    limb_t l, v, a;
-    int shift, ret;
-    slimb_t i;
-
-    //    bf_print_str("bf_renorm", r);
-    l = r->len;
-    while (l > 0 && r->tab[l - 1] == 0)
-        l--;
-    if (l == 0) {
-        /* zero */
-        r->expn = BF_EXP_ZERO;
-        bf_resize(r, 0); /* cannot fail */
-        ret = 0;
-    } else {
-        r->expn -= (r->len - l) * LIMB_BITS;
-        /* shift to have the MSB set to '1' */
-        v = r->tab[l - 1];
-        shift = clz(v);
-        if (shift != 0) {
-            v = 0;
-            for(i = 0; i < l; i++) {
-                a = r->tab[i];
-                r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift));
-                v = a;
-            }
-            r->expn -= shift;
-        }
-        ret = __bf_round(r, prec1, flags, l, 0);
-    }
-    //    bf_print_str("r_final", r);
-    return ret;
-}
-
-/* return true if rounding can be done at precision 'prec' assuming
-   the exact result r is such that |r-a| <= 2^(EXP(a)-k). */
-/* XXX: check the case where the exponent would be incremented by the
-   rounding */
-int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k)
-{
-    BOOL is_rndn;
-    slimb_t bit_pos, n;
-    limb_t bit;
-
-    if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN)
-        return FALSE;
-    if (rnd_mode == BF_RNDF) {
-        return (k >= (prec + 1));
-    }
-    if (a->expn == BF_EXP_ZERO)
-        return FALSE;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
-    if (k < (prec + 2))
-        return FALSE;
-    bit_pos = a->len * LIMB_BITS - 1 - prec;
-    n = k - prec;
-    /* bit pattern for RNDN or RNDNA: 0111.. or 1000...
-       for other rounding modes: 000... or 111...
-    */
-    bit = get_bit(a->tab, a->len, bit_pos);
-    bit_pos--;
-    n--;
-    bit ^= is_rndn;
-    /* XXX: slow, but a few iterations on average */
-    while (n != 0) {
-        if (get_bit(a->tab, a->len, bit_pos) != bit)
-            return TRUE;
-        bit_pos--;
-        n--;
-    }
-    return FALSE;
-}
-
-/* Cannot fail with BF_ST_MEM_ERROR. */
-int bf_round(bf_t *r, limb_t prec, bf_flags_t flags)
-{
-    if (r->len == 0)
-        return 0;
-    return __bf_round(r, prec, flags, r->len, 0);
-}
-
-/* for debugging */
-static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n)
-{
-    limb_t i;
-    printf("%s: len=%" PRId_LIMB "\n", str, n);
-    for(i = 0; i < n; i++) {
-        printf("%" PRId_LIMB ": " FMT_LIMB "\n",
-               i, tab[i]);
-    }
-}
-
-void mp_print_str(const char *str, const limb_t *tab, limb_t n)
-{
-    slimb_t i;
-    printf("%s= 0x", str);
-    for(i = n - 1; i >= 0; i--) {
-        if (i != (n - 1))
-            printf("_");
-        printf(FMT_LIMB, tab[i]);
-    }
-    printf("\n");
-}
-
-static __maybe_unused void mp_print_str_h(const char *str,
-                                          const limb_t *tab, limb_t n,
-                                          limb_t high)
-{
-    slimb_t i;
-    printf("%s= 0x", str);
-    printf(FMT_LIMB, high);
-    for(i = n - 1; i >= 0; i--) {
-        printf("_");
-        printf(FMT_LIMB, tab[i]);
-    }
-    printf("\n");
-}
-
-/* for debugging */
-void bf_print_str(const char *str, const bf_t *a)
-{
-    slimb_t i;
-    printf("%s=", str);
-
-    if (a->expn == BF_EXP_NAN) {
-        printf("NaN");
-    } else {
-        if (a->sign)
-            putchar('-');
-        if (a->expn == BF_EXP_ZERO) {
-            putchar('0');
-        } else if (a->expn == BF_EXP_INF) {
-            printf("Inf");
-        } else {
-            printf("0x0.");
-            for(i = a->len - 1; i >= 0; i--)
-                printf(FMT_LIMB, a->tab[i]);
-            printf("p%" PRId_LIMB, a->expn);
-        }
-    }
-    printf("\n");
-}
-
-/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0
-   if a = b and > 0 otherwise. */
-int bf_cmpu(const bf_t *a, const bf_t *b)
-{
-    slimb_t i;
-    limb_t len, v1, v2;
-
-    if (a->expn != b->expn) {
-        if (a->expn < b->expn)
-            return -1;
-        else
-            return 1;
-    }
-    len = bf_max(a->len, b->len);
-    for(i = len - 1; i >= 0; i--) {
-        v1 = get_limbz(a, a->len - len + i);
-        v2 = get_limbz(b, b->len - len + i);
-        if (v1 != v2) {
-            if (v1 < v2)
-                return -1;
-            else
-                return 1;
-        }
-    }
-    return 0;
-}
-
-/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */
-int bf_cmp_full(const bf_t *a, const bf_t *b)
-{
-    int res;
-
-    if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-        if (a->expn == b->expn)
-            res = 0;
-        else if (a->expn == BF_EXP_NAN)
-            res = 1;
-        else
-            res = -1;
-    } else if (a->sign != b->sign) {
-        res = 1 - 2 * a->sign;
-    } else {
-        res = bf_cmpu(a, b);
-        if (a->sign)
-            res = -res;
-    }
-    return res;
-}
-
-/* Standard floating point comparison: return 2 if one of the operands
-   is NaN (unordered) or -1, 0, 1 depending on the ordering assuming
-   -0 == +0 */
-int bf_cmp(const bf_t *a, const bf_t *b)
-{
-    int res;
-
-    if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-        res = 2;
-    } else if (a->sign != b->sign) {
-        if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO)
-            res = 0;
-        else
-            res = 1 - 2 * a->sign;
-    } else {
-        res = bf_cmpu(a, b);
-        if (a->sign)
-            res = -res;
-    }
-    return res;
-}
-
-/* Compute the number of bits 'n' matching the pattern:
-   a= X1000..0
-   b= X0111..1
-
-   When computing a-b, the result will have at least n leading zero
-   bits.
-
-   Precondition: a > b and a.expn - b.expn = 0 or 1
-*/
-static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b)
-{
-    slimb_t bit_offset, b_offset, n;
-    int p, p1;
-    limb_t v1, v2, mask;
-
-    bit_offset = a->len * LIMB_BITS - 1;
-    b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) +
-        a->expn - b->expn;
-    n = 0;
-
-    /* first search the equals bits */
-    for(;;) {
-        v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);
-        v2 = get_bits(b->tab, b->len, bit_offset + b_offset);
-        //        printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);
-        if (v1 != v2)
-            break;
-        n += LIMB_BITS;
-        bit_offset -= LIMB_BITS;
-    }
-    /* find the position of the first different bit */
-    p = clz(v1 ^ v2) + 1;
-    n += p;
-    /* then search for '0' in a and '1' in b */
-    p = LIMB_BITS - p;
-    if (p > 0) {
-        /* search in the trailing p bits of v1 and v2 */
-        mask = limb_mask(0, p - 1);
-        p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p);
-        n += p1;
-        if (p1 != p)
-            goto done;
-    }
-    bit_offset -= LIMB_BITS;
-    for(;;) {
-        v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);
-        v2 = get_bits(b->tab, b->len, bit_offset + b_offset);
-        //        printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);
-        if (v1 != 0 || v2 != -1) {
-            /* different: count the matching bits */
-            p1 = bf_min(clz(v1), clz(~v2));
-            n += p1;
-            break;
-        }
-        n += LIMB_BITS;
-        bit_offset -= LIMB_BITS;
-    }
- done:
-    return n;
-}
-
-static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                           bf_flags_t flags, int b_neg)
-{
-    const bf_t *tmp;
-    int is_sub, ret, cmp_res, a_sign, b_sign;
-
-    a_sign = a->sign;
-    b_sign = b->sign ^ b_neg;
-    is_sub = a_sign ^ b_sign;
-    cmp_res = bf_cmpu(a, b);
-    if (cmp_res < 0) {
-        tmp = a;
-        a = b;
-        b = tmp;
-        a_sign = b_sign; /* b_sign is never used later */
-    }
-    /* abs(a) >= abs(b) */
-    if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {
-        /* zero result */
-        bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);
-        ret = 0;
-    } else if (a->len == 0 || b->len == 0) {
-        ret = 0;
-        if (a->expn >= BF_EXP_INF) {
-            if (a->expn == BF_EXP_NAN) {
-                /* at least one operand is NaN */
-                bf_set_nan(r);
-            } else if (b->expn == BF_EXP_INF && is_sub) {
-                /* infinities with different signs */
-                bf_set_nan(r);
-                ret = BF_ST_INVALID_OP;
-            } else {
-                bf_set_inf(r, a_sign);
-            }
-        } else {
-            /* at least one zero and not subtract */
-            bf_set(r, a);
-            r->sign = a_sign;
-            goto renorm;
-        }
-    } else {
-        slimb_t d, a_offset, b_bit_offset, i, cancelled_bits;
-        limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask;
-
-        r->sign = a_sign;
-        r->expn = a->expn;
-        d = a->expn - b->expn;
-        /* must add more precision for the leading cancelled bits in
-           subtraction */
-        if (is_sub) {
-            if (d <= 1)
-                cancelled_bits = count_cancelled_bits(a, b);
-            else
-                cancelled_bits = 1;
-        } else {
-            cancelled_bits = 0;
-        }
-
-        /* add two extra bits for rounding */
-        precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
-        tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS);
-        r_len = bf_min(precl, tot_len);
-        if (bf_resize(r, r_len))
-            goto fail;
-        a_offset = a->len - r_len;
-        b_bit_offset = (b->len - r_len) * LIMB_BITS + d;
-
-        /* compute the bits before for the rounding */
-        carry = is_sub;
-        z = 0;
-        sub_mask = -is_sub;
-        i = r_len - tot_len;
-        while (i < 0) {
-            slimb_t ap, bp;
-            BOOL inflag;
-
-            ap = a_offset + i;
-            bp = b_bit_offset + i * LIMB_BITS;
-            inflag = FALSE;
-            if (ap >= 0 && ap < a->len) {
-                v1 = a->tab[ap];
-                inflag = TRUE;
-            } else {
-                v1 = 0;
-            }
-            if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) {
-                v2 = get_bits(b->tab, b->len, bp);
-                inflag = TRUE;
-            } else {
-                v2 = 0;
-            }
-            if (!inflag) {
-                /* outside 'a' and 'b': go directly to the next value
-                   inside a or b so that the running time does not
-                   depend on the exponent difference */
-                i = 0;
-                if (ap < 0)
-                    i = bf_min(i, -a_offset);
-                /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1
-                   equivalent to
-                   i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS)
-                */
-                if (bp + LIMB_BITS <= 0)
-                    i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS);
-            } else {
-                i++;
-            }
-            v2 ^= sub_mask;
-            u = v1 + v2;
-            carry1 = u < v1;
-            u += carry;
-            carry = (u < carry) | carry1;
-            z |= u;
-        }
-        /* and the result */
-        for(i = 0; i < r_len; i++) {
-            v1 = get_limbz(a, a_offset + i);
-            v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS);
-            v2 ^= sub_mask;
-            u = v1 + v2;
-            carry1 = u < v1;
-            u += carry;
-            carry = (u < carry) | carry1;
-            r->tab[i] = u;
-        }
-        /* set the extra bits for the rounding */
-        r->tab[0] |= (z != 0);
-
-        /* carry is only possible in add case */
-        if (!is_sub && carry) {
-            if (bf_resize(r, r_len + 1))
-                goto fail;
-            r->tab[r_len] = 1;
-            r->expn += LIMB_BITS;
-        }
-    renorm:
-        ret = bf_normalize_and_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bf_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                     bf_flags_t flags)
-{
-    return bf_add_internal(r, a, b, prec, flags, 0);
-}
-
-static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                     bf_flags_t flags)
-{
-    return bf_add_internal(r, a, b, prec, flags, 1);
-}
-
-limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
-              limb_t n, limb_t carry)
-{
-    slimb_t i;
-    limb_t k, a, v, k1;
-
-    k = carry;
-    for(i=0;i<n;i++) {
-        v = op1[i];
-        a = v + op2[i];
-        k1 = a < v;
-        a = a + k;
-        k = (a < k) | k1;
-        res[i] = a;
-    }
-    return k;
-}
-
-limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n)
-{
-    size_t i;
-    limb_t k, a;
-
-    k=b;
-    for(i=0;i<n;i++) {
-        if (k == 0)
-            break;
-        a = tab[i] + k;
-        k = (a < k);
-        tab[i] = a;
-    }
-    return k;
-}
-
-limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2,
-              mp_size_t n, limb_t carry)
-{
-    int i;
-    limb_t k, a, v, k1;
-
-    k = carry;
-    for(i=0;i<n;i++) {
-        v = op1[i];
-        a = v - op2[i];
-        k1 = a > v;
-        v = a - k;
-        k = (v > a) | k1;
-        res[i] = v;
-    }
-    return k;
-}
-
-/* compute 0 - op2 */
-static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry)
-{
-    int i;
-    limb_t k, a, v, k1;
-
-    k = carry;
-    for(i=0;i<n;i++) {
-        v = 0;
-        a = v - op2[i];
-        k1 = a > v;
-        v = a - k;
-        k = (v > a) | k1;
-        res[i] = v;
-    }
-    return k;
-}
-
-limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n)
-{
-    mp_size_t i;
-    limb_t k, a, v;
-
-    k=b;
-    for(i=0;i<n;i++) {
-        v = tab[i];
-        a = v - k;
-        k = a > v;
-        tab[i] = a;
-        if (k == 0)
-            break;
-    }
-    return k;
-}
-
-/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
-   1 <= shift <= LIMB_BITS - 1 */
-static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n,
-                     int shift, limb_t high)
-{
-    mp_size_t i;
-    limb_t l, a;
-
-    assert(shift >= 1 && shift < LIMB_BITS);
-    l = high;
-    for(i = n - 1; i >= 0; i--) {
-        a = tab[i];
-        tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift));
-        l = a;
-    }
-    return l & (((limb_t)1 << shift) - 1);
-}
-
-/* tabr[] = taba[] * b + l. Return the high carry */
-static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
-                      limb_t b, limb_t l)
-{
-    limb_t i;
-    dlimb_t t;
-
-    for(i = 0; i < n; i++) {
-        t = (dlimb_t)taba[i] * (dlimb_t)b + l;
-        tabr[i] = t;
-        l = t >> LIMB_BITS;
-    }
-    return l;
-}
-
-/* tabr[] += taba[] * b, return the high word. */
-static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
-                          limb_t b)
-{
-    limb_t i, l;
-    dlimb_t t;
-
-    l = 0;
-    for(i = 0; i < n; i++) {
-        t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i];
-        tabr[i] = t;
-        l = t >> LIMB_BITS;
-    }
-    return l;
-}
-
-/* size of the result : op1_size + op2_size. */
-static void mp_mul_basecase(limb_t *result,
-                            const limb_t *op1, limb_t op1_size,
-                            const limb_t *op2, limb_t op2_size)
-{
-    limb_t i, r;
-
-    result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
-    for(i=1;i<op2_size;i++) {
-        r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
-        result[i + op1_size] = r;
-    }
-}
-
-/* return 0 if OK, -1 if memory error */
-/* XXX: change API so that result can be allocated */
-int mp_mul(bf_context_t *s, limb_t *result,
-           const limb_t *op1, limb_t op1_size,
-           const limb_t *op2, limb_t op2_size)
-{
-#ifdef USE_FFT_MUL
-    if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) {
-        bf_t r_s, *r = &r_s;
-        r->tab = result;
-        /* XXX: optimize memory usage in API */
-        if (fft_mul(s, r, (limb_t *)op1, op1_size,
-                    (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE))
-            return -1;
-    } else
-#endif
-    {
-        mp_mul_basecase(result, op1, op1_size, op2, op2_size);
-    }
-    return 0;
-}
-
-/* tabr[] -= taba[] * b. Return the value to substract to the high
-   word. */
-static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
-                          limb_t b)
-{
-    limb_t i, l;
-    dlimb_t t;
-
-    l = 0;
-    for(i = 0; i < n; i++) {
-        t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l;
-        tabr[i] = t;
-        l = -(t >> LIMB_BITS);
-    }
-    return l;
-}
-
-/* WARNING: d must be >= 2^(LIMB_BITS-1) */
-static inline limb_t udiv1norm_init(limb_t d)
-{
-    limb_t a0, a1;
-    a1 = -d - 1;
-    a0 = -1;
-    return (((dlimb_t)a1 << LIMB_BITS) | a0) / d;
-}
-
-/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0
-   / d' with 0 <= a1 < d. */
-static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0,
-                                limb_t d, limb_t d_inv)
-{
-    limb_t n1m, n_adj, q, r, ah;
-    dlimb_t a;
-    n1m = ((slimb_t)a0 >> (LIMB_BITS - 1));
-    n_adj = a0 + (n1m & d);
-    a = (dlimb_t)d_inv * (a1 - n1m) + n_adj;
-    q = (a >> LIMB_BITS) + a1;
-    /* compute a - q * r and update q so that the remainder is\
-       between 0 and d - 1 */
-    a = ((dlimb_t)a1 << LIMB_BITS) | a0;
-    a = a - (dlimb_t)q * d - d;
-    ah = a >> LIMB_BITS;
-    q += 1 + ah;
-    r = (limb_t)a + (ah & d);
-    *pr = r;
-    return q;
-}
-
-/* b must be >= 1 << (LIMB_BITS - 1) */
-static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,
-                          limb_t b, limb_t r)
-{
-    slimb_t i;
-
-    if (n >= UDIV1NORM_THRESHOLD) {
-        limb_t b_inv;
-        b_inv = udiv1norm_init(b);
-        for(i = n - 1; i >= 0; i--) {
-            tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
-        }
-    } else {
-        dlimb_t a1;
-        for(i = n - 1; i >= 0; i--) {
-            a1 = ((dlimb_t)r << LIMB_BITS) | taba[i];
-            tabr[i] = a1 / b;
-            r = a1 % b;
-        }
-    }
-    return r;
-}
-
-static int mp_divnorm_large(bf_context_t *s,
-                            limb_t *tabq, limb_t *taba, limb_t na,
-                            const limb_t *tabb, limb_t nb);
-
-/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
-   - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba'
-   is modified and contains the remainder (nb limbs). tabq[0..na-nb]
-   contains the quotient with tabq[na - nb] <= 1. */
-static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na,
-                      const limb_t *tabb, limb_t nb)
-{
-    limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
-    slimb_t i, j;
-
-    b1 = tabb[nb - 1];
-    if (nb == 1) {
-        taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
-        return 0;
-    }
-    n = na - nb;
-    if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) {
-        return mp_divnorm_large(s, tabq, taba, na, tabb, nb);
-    }
-
-    if (n >= UDIV1NORM_THRESHOLD)
-        b1_inv = udiv1norm_init(b1);
-    else
-        b1_inv = 0;
-
-    /* first iteration: the quotient is only 0 or 1 */
-    q = 1;
-    for(j = nb - 1; j >= 0; j--) {
-        if (taba[n + j] != tabb[j]) {
-            if (taba[n + j] < tabb[j])
-                q = 0;
-            break;
-        }
-    }
-    tabq[n] = q;
-    if (q) {
-        mp_sub(taba + n, taba + n, tabb, nb, 0);
-    }
-
-    for(i = n - 1; i >= 0; i--) {
-        if (unlikely(taba[i + nb] >= b1)) {
-            q = -1;
-        } else if (b1_inv) {
-            q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
-        } else {
-            dlimb_t al;
-            al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1];
-            q = al / b1;
-            r = al % b1;
-        }
-        r = mp_sub_mul1(taba + i, tabb, nb, q);
-
-        v = taba[i + nb];
-        a = v - r;
-        c = (a > v);
-        taba[i + nb] = a;
-
-        if (c != 0) {
-            /* negative result */
-            for(;;) {
-                q--;
-                c = mp_add(taba + i, taba + i, tabb, nb, 0);
-                /* propagate carry and test if positive result */
-                if (c != 0) {
-                    if (++taba[i + nb] == 0) {
-                        break;
-                    }
-                }
-            }
-        }
-        tabq[i] = q;
-    }
-    return 0;
-}
-
-/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a'
-   has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1.
-
-   See Modern Computer Arithmetic by Richard P. Brent and Paul
-   Zimmermann, algorithm 3.5 */
-int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n)
-{
-    mp_size_t l, h, k, i;
-    limb_t *tabxh, *tabt, c, *tabu;
-
-    if (n <= 2) {
-        /* return ceil(B^(2*n)/a) - 1 */
-        /* XXX: could avoid allocation */
-        tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1));
-        tabt = bf_malloc(s, sizeof(limb_t) * (n + 2));
-        if (!tabt || !tabu)
-            goto fail;
-        for(i = 0; i < 2 * n; i++)
-            tabu[i] = 0;
-        tabu[2 * n] = 1;
-        if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n))
-            goto fail;
-        for(i = 0; i < n + 1; i++)
-            tabr[i] = tabt[i];
-        if (mp_scan_nz(tabu, n) == 0) {
-            /* only happens for a=B^n/2 */
-            mp_sub_ui(tabr, 1, n + 1);
-        }
-    } else {
-        l = (n - 1) / 2;
-        h = n - l;
-        /* n=2p  -> l=p-1, h = p + 1, k = p + 3
-           n=2p+1-> l=p,  h = p + 1; k = p + 2
-        */
-        tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1));
-        tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2));
-        if (!tabt || !tabu)
-            goto fail;
-        tabxh = tabr + l;
-        if (mp_recip(s, tabxh, taba + l, h))
-            goto fail;
-        if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */
-            goto fail;
-        while (tabt[n + h] != 0) {
-            mp_sub_ui(tabxh, 1, h + 1);
-            c = mp_sub(tabt, tabt, taba, n, 0);
-            mp_sub_ui(tabt + n, c, h + 1);
-        }
-        /* T = B^(n+h) - T */
-        mp_neg(tabt, tabt, n + h + 1, 0);
-        tabt[n + h]++;
-        if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1))
-            goto fail;
-        /* n + 2*h - l + 2 limbs */
-        k = 2 * h - l;
-        for(i = 0; i < l; i++)
-            tabr[i] = tabu[i + k];
-        mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0);
-    }
-    bf_free(s, tabt);
-    bf_free(s, tabu);
-    return 0;
- fail:
-    bf_free(s, tabt);
-    bf_free(s, tabu);
-    return -1;
-}
-
-/* return -1, 0 or 1 */
-static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n)
-{
-    mp_size_t i;
-    for(i = n - 1; i >= 0; i--) {
-        if (taba[i] != tabb[i]) {
-            if (taba[i] < tabb[i])
-                return -1;
-            else
-                return 1;
-        }
-    }
-    return 0;
-}
-
-//#define DEBUG_DIVNORM_LARGE
-//#define DEBUG_DIVNORM_LARGE2
-
-/* subquadratic divnorm */
-static int mp_divnorm_large(bf_context_t *s,
-                            limb_t *tabq, limb_t *taba, limb_t na,
-                            const limb_t *tabb, limb_t nb)
-{
-    limb_t *tabb_inv, nq, *tabt, i, n;
-    nq = na - nb;
-#ifdef DEBUG_DIVNORM_LARGE
-    printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq);
-    mp_print_str("a", taba, na);
-    mp_print_str("b", tabb, nb);
-#endif
-    assert(nq >= 1);
-    n = nq;
-    if (nq < nb)
-        n++;
-    tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1));
-    tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1));
-    if (!tabb_inv || !tabt)
-        goto fail;
-
-    if (n >= nb) {
-        for(i = 0; i < n - nb; i++)
-            tabt[i] = 0;
-        for(i = 0; i < nb; i++)
-            tabt[i + n - nb] = tabb[i];
-    } else {
-        /* truncate B: need to increment it so that the approximate
-           inverse is smaller that the exact inverse */
-        for(i = 0; i < n; i++)
-            tabt[i] = tabb[i + nb - n];
-        if (mp_add_ui(tabt, 1, n)) {
-            /* tabt = B^n : tabb_inv = B^n */
-            memset(tabb_inv, 0, n * sizeof(limb_t));
-            tabb_inv[n] = 1;
-            goto recip_done;
-        }
-    }
-    if (mp_recip(s, tabb_inv, tabt, n))
-        goto fail;
- recip_done:
-    /* Q=A*B^-1 */
-    if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1))
-        goto fail;
-
-    for(i = 0; i < nq + 1; i++)
-        tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)];
-#ifdef DEBUG_DIVNORM_LARGE
-    mp_print_str("q", tabq, nq + 1);
-#endif
-
-    bf_free(s, tabt);
-    bf_free(s, tabb_inv);
-    tabb_inv = NULL;
-
-    /* R=A-B*Q */
-    tabt = bf_malloc(s, sizeof(limb_t) * (na + 1));
-    if (!tabt)
-        goto fail;
-    if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb))
-        goto fail;
-    /* we add one more limb for the result */
-    mp_sub(taba, taba, tabt, nb + 1, 0);
-    bf_free(s, tabt);
-    /* the approximated quotient is smaller than than the exact one,
-       hence we may have to increment it */
-#ifdef DEBUG_DIVNORM_LARGE2
-    int cnt = 0;
-    static int cnt_max;
-#endif
-    for(;;) {
-        if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0)
-            break;
-        taba[nb] -= mp_sub(taba, taba, tabb, nb, 0);
-        mp_add_ui(tabq, 1, nq + 1);
-#ifdef DEBUG_DIVNORM_LARGE2
-        cnt++;
-#endif
-    }
-#ifdef DEBUG_DIVNORM_LARGE2
-    if (cnt > cnt_max) {
-        cnt_max = cnt;
-        printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb);
-    }
-#endif
-    return 0;
- fail:
-    bf_free(s, tabb_inv);
-    bf_free(s, tabt);
-    return -1;
-}
-
-int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-           bf_flags_t flags)
-{
-    int ret, r_sign;
-
-    if (a->len < b->len) {
-        const bf_t *tmp = a;
-        a = b;
-        b = tmp;
-    }
-    r_sign = a->sign ^ b->sign;
-    /* here b->len <= a->len */
-    if (b->len == 0) {
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            ret = 0;
-        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {
-            if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||
-                (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {
-                bf_set_nan(r);
-                ret = BF_ST_INVALID_OP;
-            } else {
-                bf_set_inf(r, r_sign);
-                ret = 0;
-            }
-        } else {
-            bf_set_zero(r, r_sign);
-            ret = 0;
-        }
-    } else {
-        bf_t tmp, *r1 = NULL;
-        limb_t a_len, b_len, precl;
-        limb_t *a_tab, *b_tab;
-
-        a_len = a->len;
-        b_len = b->len;
-
-        if ((flags & BF_RND_MASK) == BF_RNDF) {
-            /* faithful rounding does not require using the full inputs */
-            precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
-            a_len = bf_min(a_len, precl);
-            b_len = bf_min(b_len, precl);
-        }
-        a_tab = a->tab + a->len - a_len;
-        b_tab = b->tab + b->len - b_len;
-
-#ifdef USE_FFT_MUL
-        if (b_len >= FFT_MUL_THRESHOLD) {
-            int mul_flags = 0;
-            if (r == a)
-                mul_flags |= FFT_MUL_R_OVERLAP_A;
-            if (r == b)
-                mul_flags |= FFT_MUL_R_OVERLAP_B;
-            if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags))
-                goto fail;
-        } else
-#endif
-        {
-            if (r == a || r == b) {
-                bf_init(r->ctx, &tmp);
-                r1 = r;
-                r = &tmp;
-            }
-            if (bf_resize(r, a_len + b_len)) {
-#ifdef USE_FFT_MUL
-            fail:
-#endif
-                bf_set_nan(r);
-                ret = BF_ST_MEM_ERROR;
-                goto done;
-            }
-            mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len);
-        }
-        r->sign = r_sign;
-        r->expn = a->expn + b->expn;
-        ret = bf_normalize_and_round(r, prec, flags);
-    done:
-        if (r == &tmp)
-            bf_move(r1, &tmp);
-    }
-    return ret;
-}
-
-/* multiply 'r' by 2^e */
-int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags)
-{
-    slimb_t e_max;
-    if (r->len == 0)
-        return 0;
-    e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1;
-    e = bf_max(e, -e_max);
-    e = bf_min(e, e_max);
-    r->expn += e;
-    return __bf_round(r, prec, flags, r->len, 0);
-}
-
-/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero,
-   Infinite or Nan. */
-slimb_t bf_get_exp_min(const bf_t *a)
-{
-    slimb_t i;
-    limb_t v;
-    int k;
-
-    for(i = 0; i < a->len; i++) {
-        v = a->tab[i];
-        if (v != 0) {
-            k = ctz(v);
-            return a->expn - (a->len - i) * LIMB_BITS + k;
-        }
-    }
-    return 0;
-}
-
-/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the
-   integer defined as floor(a/b) and r = a - q * b. */
-static void bf_tdivremu(bf_t *q, bf_t *r,
-                        const bf_t *a, const bf_t *b)
-{
-    if (bf_cmpu(a, b) < 0) {
-        bf_set_ui(q, 0);
-        bf_set(r, a);
-    } else {
-        bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ);
-        bf_rint(q, BF_RNDZ);
-        bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ);
-        bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ);
-    }
-}
-
-static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                    bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    int ret, r_sign;
-    limb_t n, nb, precl;
-
-    r_sign = a->sign ^ b->sign;
-    if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_inf(r, r_sign);
-            return 0;
-        } else {
-            bf_set_zero(r, r_sign);
-            return 0;
-        }
-    } else if (a->expn == BF_EXP_ZERO) {
-        if (b->expn == BF_EXP_ZERO) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set_zero(r, r_sign);
-            return 0;
-        }
-    } else if (b->expn == BF_EXP_ZERO) {
-        bf_set_inf(r, r_sign);
-        return BF_ST_DIVIDE_ZERO;
-    }
-
-    /* number of limbs of the quotient (2 extra bits for rounding) */
-    precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
-    nb = b->len;
-    n = bf_max(a->len, precl);
-
-    {
-        limb_t *taba, na;
-        slimb_t d;
-
-        na = n + nb;
-        taba = bf_malloc(s, (na + 1) * sizeof(limb_t));
-        if (!taba)
-            goto fail;
-        d = na - a->len;
-        memset(taba, 0, d * sizeof(limb_t));
-        memcpy(taba + d, a->tab, a->len * sizeof(limb_t));
-        if (bf_resize(r, n + 1))
-            goto fail1;
-        if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) {
-        fail1:
-            bf_free(s, taba);
-            goto fail;
-        }
-        /* see if non zero remainder */
-        if (mp_scan_nz(taba, nb))
-            r->tab[0] |= 1;
-        bf_free(r->ctx, taba);
-        r->expn = a->expn - b->expn + LIMB_BITS;
-        r->sign = r_sign;
-        ret = bf_normalize_and_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bf_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-/* division and remainder.
-
-   rnd_mode is the rounding mode for the quotient. The additional
-   rounding mode BF_RND_EUCLIDIAN is supported.
-
-   'q' is an integer. 'r' is rounded with prec and flags (prec can be
-   BF_PREC_INF).
-*/
-int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
-              limb_t prec, bf_flags_t flags, int rnd_mode)
-{
-    bf_t a1_s, *a1 = &a1_s;
-    bf_t b1_s, *b1 = &b1_s;
-    int q_sign, ret;
-    BOOL is_ceil, is_rndn;
-
-    assert(q != a && q != b);
-    assert(r != a && r != b);
-    assert(q != r);
-
-    if (a->len == 0 || b->len == 0) {
-        bf_set_zero(q, 0);
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set(r, a);
-            return bf_round(r, prec, flags);
-        }
-    }
-
-    q_sign = a->sign ^ b->sign;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
-    switch(rnd_mode) {
-    default:
-    case BF_RNDZ:
-    case BF_RNDN:
-    case BF_RNDNA:
-        is_ceil = FALSE;
-        break;
-    case BF_RNDD:
-        is_ceil = q_sign;
-        break;
-    case BF_RNDU:
-        is_ceil = q_sign ^ 1;
-        break;
-    case BF_RNDA:
-        is_ceil = TRUE;
-        break;
-    case BF_DIVREM_EUCLIDIAN:
-        is_ceil = a->sign;
-        break;
-    }
-
-    a1->expn = a->expn;
-    a1->tab = a->tab;
-    a1->len = a->len;
-    a1->sign = 0;
-
-    b1->expn = b->expn;
-    b1->tab = b->tab;
-    b1->len = b->len;
-    b1->sign = 0;
-
-    /* XXX: could improve to avoid having a large 'q' */
-    bf_tdivremu(q, r, a1, b1);
-    if (bf_is_nan(q) || bf_is_nan(r))
-        goto fail;
-
-    if (r->len != 0) {
-        if (is_rndn) {
-            int res;
-            b1->expn--;
-            res = bf_cmpu(r, b1);
-            b1->expn++;
-            if (res > 0 ||
-                (res == 0 &&
-                 (rnd_mode == BF_RNDNA ||
-                  get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) {
-                goto do_sub_r;
-            }
-        } else if (is_ceil) {
-        do_sub_r:
-            ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);
-            ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);
-            if (ret & BF_ST_MEM_ERROR)
-                goto fail;
-        }
-    }
-
-    r->sign ^= a->sign;
-    q->sign = q_sign;
-    return bf_round(r, prec, flags);
- fail:
-    bf_set_nan(q);
-    bf_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-           bf_flags_t flags, int rnd_mode)
-{
-    bf_t q_s, *q = &q_s;
-    int ret;
-
-    bf_init(r->ctx, q);
-    ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);
-    bf_delete(q);
-    return ret;
-}
-
-static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags)
-{
-#if LIMB_BITS == 32
-    return bf_get_int32(pres, a, flags);
-#else
-    return bf_get_int64(pres, a, flags);
-#endif
-}
-
-int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-              bf_flags_t flags, int rnd_mode)
-{
-    bf_t q_s, *q = &q_s;
-    int ret;
-
-    bf_init(r->ctx, q);
-    ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);
-    bf_get_limb(pq, q, BF_GET_INT_MOD);
-    bf_delete(q);
-    return ret;
-}
-
-static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m)
-{
-    dlimb_t t;
-    t = (dlimb_t)a * (dlimb_t)b;
-    return t % m;
-}
-
-#if defined(USE_MUL_CHECK)
-static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r)
-{
-    slimb_t i;
-    dlimb_t t;
-
-    for(i = n - 1; i >= 0; i--) {
-        t = ((dlimb_t)r << LIMB_BITS) | tab[i];
-        r = t % m;
-    }
-    return r;
-}
-#endif
-
-static const uint16_t sqrt_table[192] = {
-128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255,
-};
-
-/* a >= 2^(LIMB_BITS - 2).  Return (s, r) with s=floor(sqrt(a)) and
-   r=a-s^2. 0 <= r <= 2 * s */
-static limb_t mp_sqrtrem1(limb_t *pr, limb_t a)
-{
-    limb_t s1, r1, s, r, q, u, num;
-
-    /* use a table for the 16 -> 8 bit sqrt */
-    s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64];
-    r1 = (a >> (LIMB_BITS - 16)) - s1 * s1;
-    if (r1 > 2 * s1) {
-        r1 -= 2 * s1 + 1;
-        s1++;
-    }
-
-    /* one iteration to get a 32 -> 16 bit sqrt */
-    num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff);
-    q = num / (2 * s1); /* q <= 2^8 */
-    u = num % (2 * s1);
-    s = (s1 << 8) + q;
-    r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff);
-    r -= q * q;
-    if ((slimb_t)r < 0) {
-        s--;
-        r += 2 * s + 1;
-    }
-
-#if LIMB_BITS == 64
-    s1 = s;
-    r1 = r;
-    /* one more iteration for 64 -> 32 bit sqrt */
-    num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff);
-    q = num / (2 * s1); /* q <= 2^16 */
-    u = num % (2 * s1);
-    s = (s1 << 16) + q;
-    r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff);
-    r -= q * q;
-    if ((slimb_t)r < 0) {
-        s--;
-        r += 2 * s + 1;
-    }
-#endif
-    *pr = r;
-    return s;
-}
-
-/* return floor(sqrt(a)) */
-limb_t bf_isqrt(limb_t a)
-{
-    limb_t s, r;
-    int k;
-
-    if (a == 0)
-        return 0;
-    k = clz(a) & ~1;
-    s = mp_sqrtrem1(&r, a << k);
-    s >>= (k >> 1);
-    return s;
-}
-
-static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba)
-{
-    limb_t s1, r1, s, q, u, a0, a1;
-    dlimb_t r, num;
-    int l;
-
-    a0 = taba[0];
-    a1 = taba[1];
-    s1 = mp_sqrtrem1(&r1, a1);
-    l = LIMB_BITS / 2;
-    num = ((dlimb_t)r1 << l) | (a0 >> l);
-    q = num / (2 * s1);
-    u = num % (2 * s1);
-    s = (s1 << l) + q;
-    r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1));
-    if (unlikely((q >> l) != 0))
-        r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */
-    else
-        r -= q * q;
-    if ((slimb_t)(r >> LIMB_BITS) < 0) {
-        s--;
-        r += 2 * (dlimb_t)s + 1;
-    }
-    tabs[0] = s;
-    taba[0] = r;
-    return r >> LIMB_BITS;
-}
-
-//#define DEBUG_SQRTREM
-
-/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest
-   limb of the remainder. */
-static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n,
-                          limb_t *tmp_buf, limb_t *prh)
-{
-    limb_t l, h, rh, ql, qh, c, i;
-
-    if (n == 1) {
-        *prh = mp_sqrtrem2(tabs, taba);
-        return 0;
-    }
-#ifdef DEBUG_SQRTREM
-    mp_print_str("a", taba, 2 * n);
-#endif
-    l = n / 2;
-    h = n - l;
-    if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh))
-        return -1;
-#ifdef DEBUG_SQRTREM
-    mp_print_str("s1", tabs + l, h);
-    mp_print_str_h("r1", taba + 2 * l, h, qh);
-    mp_print_str_h("r2", taba + l, n, qh);
-#endif
-
-    /* the remainder is in taba + 2 * l. Its high bit is in qh */
-    if (qh) {
-        mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);
-    }
-    /* instead of dividing by 2*s, divide by s (which is normalized)
-       and update q and r */
-    if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h))
-        return -1;
-    qh += tmp_buf[l];
-    for(i = 0; i < l; i++)
-        tabs[i] = tmp_buf[i];
-    ql = mp_shr(tabs, tabs, l, 1, qh & 1);
-    qh = qh >> 1; /* 0 or 1 */
-    if (ql)
-        rh = mp_add(taba + l, taba + l, tabs + l, h, 0);
-    else
-        rh = 0;
-#ifdef DEBUG_SQRTREM
-    mp_print_str_h("q", tabs, l, qh);
-    mp_print_str_h("u", taba + l, h, rh);
-#endif
-
-    mp_add_ui(tabs + l, qh, h);
-#ifdef DEBUG_SQRTREM
-    mp_print_str_h("s2", tabs, n, sh);
-#endif
-
-    /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */
-    /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */
-    if (qh) {
-        c = qh;
-    } else {
-        if (mp_mul(s, taba + n, tabs, l, tabs, l))
-            return -1;
-        c = mp_sub(taba, taba, taba + n, 2 * l, 0);
-    }
-    rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l);
-    if ((slimb_t)rh < 0) {
-        mp_sub_ui(tabs, 1, n);
-        rh += mp_add_mul1(taba, tabs, n, 2);
-        rh += mp_add_ui(taba, 1, n);
-    }
-    *prh = rh;
-    return 0;
-}
-
-/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS
-   - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2
-   * s. tabs has n limbs. r is returned in the lower n limbs of
-   taba. Its r[n] is the returned value of the function. */
-/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and
-   inspirated from its GMP implementation */
-int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)
-{
-    limb_t tmp_buf1[8];
-    limb_t *tmp_buf;
-    mp_size_t n2;
-    int ret;
-    n2 = n / 2 + 1;
-    if (n2 <= countof(tmp_buf1)) {
-        tmp_buf = tmp_buf1;
-    } else {
-        tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);
-        if (!tmp_buf)
-            return -1;
-    }
-    ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n);
-    if (tmp_buf != tmp_buf1)
-        bf_free(s, tmp_buf);
-    return ret;
-}
-
-/* Integer square root with remainder. 'a' must be an integer. r =
-   floor(sqrt(a)) and rem = a - r^2.  BF_ST_INEXACT is set if the result
-   is inexact. 'rem' can be NULL if the remainder is not needed. */
-int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a)
-{
-    int ret;
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-        } else if (a->expn == BF_EXP_INF && a->sign) {
-            goto invalid_op;
-        } else {
-            bf_set(r, a);
-        }
-        if (rem1)
-            bf_set_ui(rem1, 0);
-        ret = 0;
-    } else if (a->sign) {
- invalid_op:
-        bf_set_nan(r);
-        if (rem1)
-            bf_set_ui(rem1, 0);
-        ret = BF_ST_INVALID_OP;
-    } else {
-        bf_t rem_s, *rem;
-
-        bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ);
-        bf_rint(r, BF_RNDZ);
-        /* see if the result is exact by computing the remainder */
-        if (rem1) {
-            rem = rem1;
-        } else {
-            rem = &rem_s;
-            bf_init(r->ctx, rem);
-        }
-        /* XXX: could avoid recomputing the remainder */
-        bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ);
-        bf_neg(rem);
-        bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ);
-        if (bf_is_nan(rem)) {
-            ret = BF_ST_MEM_ERROR;
-            goto done;
-        }
-        if (rem->len != 0) {
-            ret = BF_ST_INEXACT;
-        } else {
-            ret = 0;
-        }
-    done:
-        if (!rem1)
-            bf_delete(rem);
-    }
-    return ret;
-}
-
-int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = a->ctx;
-    int ret;
-
-    assert(r != a);
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-        } else if (a->expn == BF_EXP_INF && a->sign) {
-            goto invalid_op;
-        } else {
-            bf_set(r, a);
-        }
-        ret = 0;
-    } else if (a->sign) {
- invalid_op:
-        bf_set_nan(r);
-        ret = BF_ST_INVALID_OP;
-    } else {
-        limb_t *a1;
-        slimb_t n, n1;
-        limb_t res;
-
-        /* convert the mantissa to an integer with at least 2 *
-           prec + 4 bits */
-        n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS);
-        if (bf_resize(r, n))
-            goto fail;
-        a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);
-        if (!a1)
-            goto fail;
-        n1 = bf_min(2 * n, a->len);
-        memset(a1, 0, (2 * n - n1) * sizeof(limb_t));
-        memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));
-        if (a->expn & 1) {
-            res = mp_shr(a1, a1, 2 * n, 1, 0);
-        } else {
-            res = 0;
-        }
-        if (mp_sqrtrem(s, r->tab, a1, n)) {
-            bf_free(s, a1);
-            goto fail;
-        }
-        if (!res) {
-            res = mp_scan_nz(a1, n + 1);
-        }
-        bf_free(s, a1);
-        if (!res) {
-            res = mp_scan_nz(a->tab, a->len - n1);
-        }
-        if (res != 0)
-            r->tab[0] |= 1;
-        r->sign = 0;
-        r->expn = (a->expn + 1) >> 1;
-        ret = bf_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bf_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                            bf_flags_t flags, bf_op2_func_t *func)
-{
-    bf_t tmp;
-    int ret;
-
-    if (r == a || r == b) {
-        bf_init(r->ctx, &tmp);
-        ret = func(&tmp, a, b, prec, flags);
-        bf_move(r, &tmp);
-    } else {
-        ret = func(r, a, b, prec, flags);
-    }
-    return ret;
-}
-
-int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-            bf_flags_t flags)
-{
-    return bf_op2(r, a, b, prec, flags, __bf_add);
-}
-
-int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-            bf_flags_t flags)
-{
-    return bf_op2(r, a, b, prec, flags, __bf_sub);
-}
-
-int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-           bf_flags_t flags)
-{
-    return bf_op2(r, a, b, prec, flags, __bf_div);
-}
-
-int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec,
-               bf_flags_t flags)
-{
-    bf_t b;
-    int ret;
-    bf_init(r->ctx, &b);
-    ret = bf_set_ui(&b, b1);
-    ret |= bf_mul(r, a, &b, prec, flags);
-    bf_delete(&b);
-    return ret;
-}
-
-int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
-               bf_flags_t flags)
-{
-    bf_t b;
-    int ret;
-    bf_init(r->ctx, &b);
-    ret = bf_set_si(&b, b1);
-    ret |= bf_mul(r, a, &b, prec, flags);
-    bf_delete(&b);
-    return ret;
-}
-
-int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
-              bf_flags_t flags)
-{
-    bf_t b;
-    int ret;
-
-    bf_init(r->ctx, &b);
-    ret = bf_set_si(&b, b1);
-    ret |= bf_add(r, a, &b, prec, flags);
-    bf_delete(&b);
-    return ret;
-}
-
-static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec,
-                     bf_flags_t flags)
-{
-    int ret, n_bits, i;
-
-    assert(r != a);
-    if (b == 0)
-        return bf_set_ui(r, 1);
-    ret = bf_set(r, a);
-    n_bits = LIMB_BITS - clz(b);
-    for(i = n_bits - 2; i >= 0; i--) {
-        ret |= bf_mul(r, r, r, prec, flags);
-        if ((b >> i) & 1)
-            ret |= bf_mul(r, r, a, prec, flags);
-    }
-    return ret;
-}
-
-static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b,
-                        limb_t prec, bf_flags_t flags)
-{
-    bf_t a;
-    int ret;
-
-#ifdef USE_BF_DEC
-    if (a1 == 10 && b <= LIMB_DIGITS) {
-        /* use precomputed powers. We do not round at this point
-           because we expect the caller to do it */
-        ret = bf_set_ui(r, mp_pow_dec[b]);
-    } else
-#endif
-    {
-        bf_init(r->ctx, &a);
-        ret = bf_set_ui(&a, a1);
-        ret |= bf_pow_ui(r, &a, b, prec, flags);
-        bf_delete(&a);
-    }
-    return ret;
-}
-
-/* convert to integer (infinite precision) */
-int bf_rint(bf_t *r, int rnd_mode)
-{
-    return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);
-}
-
-/* logical operations */
-#define BF_LOGIC_OR  0
-#define BF_LOGIC_XOR 1
-#define BF_LOGIC_AND 2
-
-static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op)
-{
-    switch(op) {
-    case BF_LOGIC_OR:
-        return a | b;
-    case BF_LOGIC_XOR:
-        return a ^ b;
-    default:
-    case BF_LOGIC_AND:
-        return a & b;
-    }
-}
-
-static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op)
-{
-    bf_t b1_s, a1_s, *a, *b;
-    limb_t a_sign, b_sign, r_sign;
-    slimb_t l, i, a_bit_offset, b_bit_offset;
-    limb_t v1, v2, v1_mask, v2_mask, r_mask;
-    int ret;
-
-    assert(r != a1 && r != b1);
-
-    if (a1->expn <= 0)
-        a_sign = 0; /* minus zero is considered as positive */
-    else
-        a_sign = a1->sign;
-
-    if (b1->expn <= 0)
-        b_sign = 0; /* minus zero is considered as positive */
-    else
-        b_sign = b1->sign;
-
-    if (a_sign) {
-        a = &a1_s;
-        bf_init(r->ctx, a);
-        if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) {
-            b = NULL;
-            goto fail;
-        }
-    } else {
-        a = (bf_t *)a1;
-    }
-
-    if (b_sign) {
-        b = &b1_s;
-        bf_init(r->ctx, b);
-        if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ))
-            goto fail;
-    } else {
-        b = (bf_t *)b1;
-    }
-
-    r_sign = bf_logic_op1(a_sign, b_sign, op);
-    if (op == BF_LOGIC_AND && r_sign == 0) {
-        /* no need to compute extra zeros for and */
-        if (a_sign == 0 && b_sign == 0)
-            l = bf_min(a->expn, b->expn);
-        else if (a_sign == 0)
-            l = a->expn;
-        else
-            l = b->expn;
-    } else {
-        l = bf_max(a->expn, b->expn);
-    }
-    /* Note: a or b can be zero */
-    l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS;
-    if (bf_resize(r, l))
-        goto fail;
-    a_bit_offset = a->len * LIMB_BITS - a->expn;
-    b_bit_offset = b->len * LIMB_BITS - b->expn;
-    v1_mask = -a_sign;
-    v2_mask = -b_sign;
-    r_mask = -r_sign;
-    for(i = 0; i < l; i++) {
-        v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask;
-        v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask;
-        r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask;
-    }
-    r->expn = l * LIMB_BITS;
-    r->sign = r_sign;
-    bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */
-    if (r_sign) {
-        if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ))
-            goto fail;
-    }
-    ret = 0;
- done:
-    if (a == &a1_s)
-        bf_delete(a);
-    if (b == &b1_s)
-        bf_delete(b);
-    return ret;
- fail:
-    bf_set_nan(r);
-    ret = BF_ST_MEM_ERROR;
-    goto done;
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b)
-{
-    return bf_logic_op(r, a, b, BF_LOGIC_OR);
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b)
-{
-    return bf_logic_op(r, a, b, BF_LOGIC_XOR);
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b)
-{
-    return bf_logic_op(r, a, b, BF_LOGIC_AND);
-}
-
-/* conversion between fixed size types */
-
-typedef union {
-    double d;
-    uint64_t u;
-} Float64Union;
-
-int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode)
-{
-    Float64Union u;
-    int e, ret;
-    uint64_t m;
-
-    ret = 0;
-    if (a->expn == BF_EXP_NAN) {
-        u.u = 0x7ff8000000000000; /* quiet nan */
-    } else {
-        bf_t b_s, *b = &b_s;
-
-        bf_init(a->ctx, b);
-        bf_set(b, a);
-        if (bf_is_finite(b)) {
-            ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11));
-        }
-        if (b->expn == BF_EXP_INF) {
-            e = (1 << 11) - 1;
-            m = 0;
-        } else if (b->expn == BF_EXP_ZERO) {
-            e = 0;
-            m = 0;
-        } else {
-            e = b->expn + 1023 - 1;
-#if LIMB_BITS == 32
-            if (b->len == 2) {
-                m = ((uint64_t)b->tab[1] << 32) | b->tab[0];
-            } else {
-                m = ((uint64_t)b->tab[0] << 32);
-            }
-#else
-            m = b->tab[0];
-#endif
-            if (e <= 0) {
-                /* subnormal */
-                m = m >> (12 - e);
-                e = 0;
-            } else {
-                m = (m << 1) >> 12;
-            }
-        }
-        u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63);
-        bf_delete(b);
-    }
-    *pres = u.d;
-    return ret;
-}
-
-int bf_set_float64(bf_t *a, double d)
-{
-    Float64Union u;
-    uint64_t m;
-    int shift, e, sgn;
-
-    u.d = d;
-    sgn = u.u >> 63;
-    e = (u.u >> 52) & ((1 << 11) - 1);
-    m = u.u & (((uint64_t)1 << 52) - 1);
-    if (e == ((1 << 11) - 1)) {
-        if (m != 0) {
-            bf_set_nan(a);
-        } else {
-            bf_set_inf(a, sgn);
-        }
-    } else if (e == 0) {
-        if (m == 0) {
-            bf_set_zero(a, sgn);
-        } else {
-            /* subnormal number */
-            m <<= 12;
-            shift = clz64(m);
-            m <<= shift;
-            e = -shift;
-            goto norm;
-        }
-    } else {
-        m = (m << 11) | ((uint64_t)1 << 63);
-    norm:
-        a->expn = e - 1023 + 1;
-#if LIMB_BITS == 32
-        if (bf_resize(a, 2))
-            goto fail;
-        a->tab[0] = m;
-        a->tab[1] = m >> 32;
-#else
-        if (bf_resize(a, 1))
-            goto fail;
-        a->tab[0] = m;
-#endif
-        a->sign = sgn;
-    }
-    return 0;
-fail:
-    bf_set_nan(a);
-    return BF_ST_MEM_ERROR;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
-   is an overflow and 0 otherwise. */
-int bf_get_int32(int *pres, const bf_t *a, int flags)
-{
-    uint32_t v;
-    int ret;
-    if (a->expn >= BF_EXP_INF) {
-        ret = BF_ST_INVALID_OP;
-        if (flags & BF_GET_INT_MOD) {
-            v = 0;
-        } else if (a->expn == BF_EXP_INF) {
-            v = (uint32_t)INT32_MAX + a->sign;
-        } else {
-            v = INT32_MAX;
-        }
-    } else if (a->expn <= 0) {
-        v = 0;
-        ret = 0;
-    } else if (a->expn <= 31) {
-        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-        if (a->sign)
-            v = -v;
-        ret = 0;
-    } else if (!(flags & BF_GET_INT_MOD)) {
-        ret = BF_ST_INVALID_OP;
-        if (a->sign) {
-            v = (uint32_t)INT32_MAX + 1;
-            if (a->expn == 32 &&
-                (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) {
-                ret = 0;
-            }
-        } else {
-            v = INT32_MAX;
-        }
-    } else {
-        v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);
-        if (a->sign)
-            v = -v;
-        ret = 0;
-    }
-    *pres = v;
-    return ret;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
-   is an overflow and 0 otherwise. */
-int bf_get_int64(int64_t *pres, const bf_t *a, int flags)
-{
-    uint64_t v;
-    int ret;
-    if (a->expn >= BF_EXP_INF) {
-        ret = BF_ST_INVALID_OP;
-        if (flags & BF_GET_INT_MOD) {
-            v = 0;
-        } else if (a->expn == BF_EXP_INF) {
-            v = (uint64_t)INT64_MAX + a->sign;
-        } else {
-            v = INT64_MAX;
-        }
-    } else if (a->expn <= 0) {
-        v = 0;
-        ret = 0;
-    } else if (a->expn <= 63) {
-#if LIMB_BITS == 32
-        if (a->expn <= 32)
-            v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-        else
-            v = (((uint64_t)a->tab[a->len - 1] << 32) |
-                 get_limbz(a, a->len - 2)) >> (64 - a->expn);
-#else
-        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-#endif
-        if (a->sign)
-            v = -v;
-        ret = 0;
-    } else if (!(flags & BF_GET_INT_MOD)) {
-        ret = BF_ST_INVALID_OP;
-        if (a->sign) {
-            uint64_t v1;
-            v = (uint64_t)INT64_MAX + 1;
-            if (a->expn == 64) {
-                v1 = a->tab[a->len - 1];
-#if LIMB_BITS == 32
-                v1 = (v1 << 32) | get_limbz(a, a->len - 2);
-#endif
-                if (v1 == v)
-                    ret = 0;
-            }
-        } else {
-            v = INT64_MAX;
-        }
-    } else {
-        slimb_t bit_pos = a->len * LIMB_BITS - a->expn;
-        v = get_bits(a->tab, a->len, bit_pos);
-#if LIMB_BITS == 32
-        v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32;
-#endif
-        if (a->sign)
-            v = -v;
-        ret = 0;
-    }
-    *pres = v;
-    return ret;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
-   is an overflow and 0 otherwise. */
-int bf_get_uint64(uint64_t *pres, const bf_t *a)
-{
-    uint64_t v;
-    int ret;
-    if (a->expn == BF_EXP_NAN) {
-        goto overflow;
-    } else if (a->expn <= 0) {
-        v = 0;
-        ret = 0;
-    } else if (a->sign) {
-        v = 0;
-        ret = BF_ST_INVALID_OP;
-    } else if (a->expn <= 64) {
-#if LIMB_BITS == 32
-        if (a->expn <= 32)
-            v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-        else
-            v = (((uint64_t)a->tab[a->len - 1] << 32) |
-                 get_limbz(a, a->len - 2)) >> (64 - a->expn);
-#else
-        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-#endif
-        ret = 0;
-    } else {
-    overflow:
-        v = UINT64_MAX;
-        ret = BF_ST_INVALID_OP;
-    }
-    *pres = v;
-    return ret;
-}
-
-/* base conversion from radix */
-
-static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = {
-#if LIMB_BITS == 32
-32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-#else
-64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
-#endif
-};
-
-static limb_t get_limb_radix(int radix)
-{
-    int i, k;
-    limb_t radixl;
-
-    k = digits_per_limb_table[radix - 2];
-    radixl = radix;
-    for(i = 1; i < k; i++)
-        radixl *= radix;
-    return radixl;
-}
-
-/* return != 0 if error */
-static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab,
-                                     limb_t n, int level, limb_t n0,
-                                     limb_t radix, bf_t *pow_tab)
-{
-    int ret;
-    if (n == 1) {
-        ret = bf_set_ui(r, tab[0]);
-    } else {
-        bf_t T_s, *T = &T_s, *B;
-        limb_t n1, n2;
-
-        n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;
-        n1 = n - n2;
-        //        printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2);
-        B = &pow_tab[level];
-        if (B->len == 0) {
-            ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ);
-            if (ret)
-                return ret;
-        }
-        ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0,
-                                        radix, pow_tab);
-        if (ret)
-            return ret;
-        ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ);
-        if (ret)
-            return ret;
-        bf_init(r->ctx, T);
-        ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0,
-                                        radix, pow_tab);
-        if (!ret)
-            ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ);
-        bf_delete(T);
-    }
-    return ret;
-    //    bf_print_str("  r=", r);
-}
-
-/* return 0 if OK != 0 if memory error */
-static int bf_integer_from_radix(bf_t *r, const limb_t *tab,
-                                 limb_t n, limb_t radix)
-{
-    bf_context_t *s = r->ctx;
-    int pow_tab_len, i, ret;
-    limb_t radixl;
-    bf_t *pow_tab;
-
-    radixl = get_limb_radix(radix);
-    pow_tab_len = ceil_log2(n) + 2; /* XXX: check */
-    pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);
-    if (!pow_tab)
-        return -1;
-    for(i = 0; i < pow_tab_len; i++)
-        bf_init(r->ctx, &pow_tab[i]);
-    ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab);
-    for(i = 0; i < pow_tab_len; i++) {
-        bf_delete(&pow_tab[i]);
-    }
-    bf_free(s, pow_tab);
-    return ret;
-}
-
-/* compute and round T * radix^expn. */
-int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
-                     slimb_t expn, limb_t prec, bf_flags_t flags)
-{
-    int ret, expn_sign, overflow;
-    slimb_t e, extra_bits, prec1, ziv_extra_bits;
-    bf_t B_s, *B = &B_s;
-
-    if (T->len == 0) {
-        return bf_set(r, T);
-    } else if (expn == 0) {
-        ret = bf_set(r, T);
-        ret |= bf_round(r, prec, flags);
-        return ret;
-    }
-
-    e = expn;
-    expn_sign = 0;
-    if (e < 0) {
-        e = -e;
-        expn_sign = 1;
-    }
-    bf_init(r->ctx, B);
-    if (prec == BF_PREC_INF) {
-        /* infinite precision: only used if the result is known to be exact */
-        ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN);
-        if (expn_sign) {
-            ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN);
-        } else {
-            ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN);
-        }
-    } else {
-        ziv_extra_bits = 16;
-        for(;;) {
-            prec1 = prec + ziv_extra_bits;
-            /* XXX: correct overflow/underflow handling */
-            /* XXX: rigorous error analysis needed */
-            extra_bits = ceil_log2(e) * 2 + 1;
-            ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
-            overflow = !bf_is_finite(B);
-            /* XXX: if bf_pow_ui_ui returns an exact result, can stop
-               after the next operation */
-            if (expn_sign)
-                ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
-            else
-                ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
-            if (ret & BF_ST_MEM_ERROR)
-                break;
-            if ((ret & BF_ST_INEXACT) &&
-                !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) &&
-                !overflow) {
-                /* and more precision and retry */
-                ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);
-            } else {
-                /* XXX: need to use __bf_round() to pass the inexact
-                   flag for the subnormal case */
-                ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT);
-                break;
-            }
-        }
-    }
-    bf_delete(B);
-    return ret;
-}
-
-static inline int to_digit(int c)
-{
-    if (c >= '0' && c <= '9')
-        return c - '0';
-    else if (c >= 'A' && c <= 'Z')
-        return c - 'A' + 10;
-    else if (c >= 'a' && c <= 'z')
-        return c - 'a' + 10;
-    else
-        return 36;
-}
-
-/* add a limb at 'pos' and decrement pos. new space is created if
-   needed. Return 0 if OK, -1 if memory error */
-static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v)
-{
-    slimb_t pos;
-    pos = *ppos;
-    if (unlikely(pos < 0)) {
-        limb_t new_size, d, *new_tab;
-        new_size = bf_max(a->len + 1, a->len * 3 / 2);
-        new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size);
-        if (!new_tab)
-            return -1;
-        a->tab = new_tab;
-        d = new_size - a->len;
-        memmove(a->tab + d, a->tab, a->len * sizeof(limb_t));
-        a->len = new_size;
-        pos += d;
-    }
-    a->tab[pos--] = v;
-    *ppos = pos;
-    return 0;
-}
-
-static int bf_tolower(int c)
-{
-    if (c >= 'A' && c <= 'Z')
-        c = c - 'A' + 'a';
-    return c;
-}
-
-static int strcasestart(const char *str, const char *val, const char **ptr)
-{
-    const char *p, *q;
-    p = str;
-    q = val;
-    while (*q != '\0') {
-        if (bf_tolower(*p) != *q)
-            return 0;
-        p++;
-        q++;
-    }
-    if (ptr)
-        *ptr = p;
-    return 1;
-}
-
-static int bf_atof_internal(bf_t *r, slimb_t *pexponent,
-                            const char *str, const char **pnext, int radix,
-                            limb_t prec, bf_flags_t flags, BOOL is_dec)
-{
-    const char *p, *p_start;
-    int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift;
-    limb_t cur_limb;
-    slimb_t pos, expn, int_len, digit_count;
-    BOOL has_decpt, is_bin_exp;
-    bf_t a_s, *a;
-
-    *pexponent = 0;
-    p = str;
-    if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
-        strcasestart(p, "nan", &p)) {
-        bf_set_nan(r);
-        ret = 0;
-        goto done;
-    }
-    is_neg = 0;
-
-    if (p[0] == '+') {
-        p++;
-        p_start = p;
-    } else if (p[0] == '-') {
-        is_neg = 1;
-        p++;
-        p_start = p;
-    } else {
-        p_start = p;
-    }
-    if (p[0] == '0') {
-        if ((p[1] == 'x' || p[1] == 'X') &&
-            (radix == 0 || radix == 16) &&
-            !(flags & BF_ATOF_NO_HEX)) {
-            radix = 16;
-            p += 2;
-        } else if ((p[1] == 'o' || p[1] == 'O') &&
-                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
-            p += 2;
-            radix = 8;
-        } else if ((p[1] == 'b' || p[1] == 'B') &&
-                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
-            p += 2;
-            radix = 2;
-        } else {
-            goto no_prefix;
-        }
-        /* there must be a digit after the prefix */
-        if (to_digit((uint8_t)*p) >= radix) {
-            bf_set_nan(r);
-            ret = 0;
-            goto done;
-        }
-    no_prefix: ;
-    } else {
-        if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
-            strcasestart(p, "inf", &p)) {
-            bf_set_inf(r, is_neg);
-            ret = 0;
-            goto done;
-        }
-    }
-
-    if (radix == 0)
-        radix = 10;
-    if (is_dec) {
-        assert(radix == 10);
-        radix_bits = 0;
-        a = r;
-    } else if ((radix & (radix - 1)) != 0) {
-        radix_bits = 0; /* base is not a power of two */
-        a = &a_s;
-        bf_init(r->ctx, a);
-    } else {
-        radix_bits = ceil_log2(radix);
-        a = r;
-    }
-
-    /* skip leading zeros */
-    /* XXX: could also skip zeros after the decimal point */
-    while (*p == '0')
-        p++;
-
-    if (radix_bits) {
-        shift = digits_per_limb = LIMB_BITS;
-    } else {
-        radix_bits = 0;
-        shift = digits_per_limb = digits_per_limb_table[radix - 2];
-    }
-    cur_limb = 0;
-    bf_resize(a, 1);
-    pos = 0;
-    has_decpt = FALSE;
-    int_len = digit_count = 0;
-    for(;;) {
-        limb_t c;
-        if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) {
-            if (has_decpt)
-                break;
-            has_decpt = TRUE;
-            int_len = digit_count;
-            p++;
-        }
-        c = to_digit(*p);
-        if (c >= radix)
-            break;
-        digit_count++;
-        p++;
-        if (radix_bits) {
-            shift -= radix_bits;
-            if (shift <= 0) {
-                cur_limb |= c >> (-shift);
-                if (bf_add_limb(a, &pos, cur_limb))
-                    goto mem_error;
-                if (shift < 0)
-                    cur_limb = c << (LIMB_BITS + shift);
-                else
-                    cur_limb = 0;
-                shift += LIMB_BITS;
-            } else {
-                cur_limb |= c << shift;
-            }
-        } else {
-            cur_limb = cur_limb * radix + c;
-            shift--;
-            if (shift == 0) {
-                if (bf_add_limb(a, &pos, cur_limb))
-                    goto mem_error;
-                shift = digits_per_limb;
-                cur_limb = 0;
-            }
-        }
-    }
-    if (!has_decpt)
-        int_len = digit_count;
-
-    /* add the last limb and pad with zeros */
-    if (shift != digits_per_limb) {
-        if (radix_bits == 0) {
-            while (shift != 0) {
-                cur_limb *= radix;
-                shift--;
-            }
-        }
-        if (bf_add_limb(a, &pos, cur_limb)) {
-        mem_error:
-            ret = BF_ST_MEM_ERROR;
-            if (!radix_bits)
-                bf_delete(a);
-            bf_set_nan(r);
-            goto done;
-        }
-    }
-
-    /* reset the next limbs to zero (we prefer to reallocate in the
-       renormalization) */
-    memset(a->tab, 0, (pos + 1) * sizeof(limb_t));
-
-    if (p == p_start) {
-        ret = 0;
-        if (!radix_bits)
-            bf_delete(a);
-        bf_set_nan(r);
-        goto done;
-    }
-
-    /* parse the exponent, if any */
-    expn = 0;
-    is_bin_exp = FALSE;
-    if (((radix == 10 && (*p == 'e' || *p == 'E')) ||
-         (radix != 10 && (*p == '@' ||
-                          (radix_bits && (*p == 'p' || *p == 'P'))))) &&
-        p > p_start) {
-        is_bin_exp = (*p == 'p' || *p == 'P');
-        p++;
-        exp_is_neg = 0;
-        if (*p == '+') {
-            p++;
-        } else if (*p == '-') {
-            exp_is_neg = 1;
-            p++;
-        }
-        for(;;) {
-            int c;
-            c = to_digit(*p);
-            if (c >= 10)
-                break;
-            if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
-                /* exponent overflow */
-                if (exp_is_neg) {
-                    bf_set_zero(r, is_neg);
-                    ret = BF_ST_UNDERFLOW | BF_ST_INEXACT;
-                } else {
-                    bf_set_inf(r, is_neg);
-                    ret = BF_ST_OVERFLOW | BF_ST_INEXACT;
-                }
-                goto done;
-            }
-            p++;
-            expn = expn * 10 + c;
-        }
-        if (exp_is_neg)
-            expn = -expn;
-    }
-    if (is_dec) {
-        a->expn = expn + int_len;
-        a->sign = is_neg;
-        ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags);
-    } else if (radix_bits) {
-        /* XXX: may overflow */
-        if (!is_bin_exp)
-            expn *= radix_bits;
-        a->expn = expn + (int_len * radix_bits);
-        a->sign = is_neg;
-        ret = bf_normalize_and_round(a, prec, flags);
-    } else {
-        limb_t l;
-        pos++;
-        l = a->len - pos; /* number of limbs */
-        if (l == 0) {
-            bf_set_zero(r, is_neg);
-            ret = 0;
-        } else {
-            bf_t T_s, *T = &T_s;
-
-            expn -= l * digits_per_limb - int_len;
-            bf_init(r->ctx, T);
-            if (bf_integer_from_radix(T, a->tab + pos, l, radix)) {
-                bf_set_nan(r);
-                ret = BF_ST_MEM_ERROR;
-            } else {
-                T->sign = is_neg;
-                if (flags & BF_ATOF_EXPONENT) {
-                    /* return the exponent */
-                    *pexponent = expn;
-                    ret = bf_set(r, T);
-                } else {
-                    ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags);
-                }
-            }
-            bf_delete(T);
-        }
-        bf_delete(a);
-    }
- done:
-    if (pnext)
-        *pnext = p;
-    return ret;
-}
-
-/*
-   Return (status, n, exp). 'status' is the floating point status. 'n'
-   is the parsed number.
-
-   If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of
-   two, the parsed number is equal to r *
-   (*pexponent)^radix. Otherwise *pexponent = 0.
-*/
-int bf_atof2(bf_t *r, slimb_t *pexponent,
-             const char *str, const char **pnext, int radix,
-             limb_t prec, bf_flags_t flags)
-{
-    return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags,
-                            FALSE);
-}
-
-int bf_atof(bf_t *r, const char *str, const char **pnext, int radix,
-            limb_t prec, bf_flags_t flags)
-{
-    slimb_t dummy_exp;
-    return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE);
-}
-
-/* base conversion to radix */
-
-#if LIMB_BITS == 64
-#define RADIXL_10 UINT64_C(10000000000000000000)
-#else
-#define RADIXL_10 UINT64_C(1000000000)
-#endif
-
-static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = {
-#if LIMB_BITS == 32
-{ 0x80000000, 0x00000000,},
-{ 0x50c24e60, 0xd4d4f4a7,},
-{ 0x40000000, 0x00000000,},
-{ 0x372068d2, 0x0a1ee5ca,},
-{ 0x3184648d, 0xb8153e7a,},
-{ 0x2d983275, 0x9d5369c4,},
-{ 0x2aaaaaaa, 0xaaaaaaab,},
-{ 0x28612730, 0x6a6a7a54,},
-{ 0x268826a1, 0x3ef3fde6,},
-{ 0x25001383, 0xbac8a744,},
-{ 0x23b46706, 0x82c0c709,},
-{ 0x229729f1, 0xb2c83ded,},
-{ 0x219e7ffd, 0xa5ad572b,},
-{ 0x20c33b88, 0xda7c29ab,},
-{ 0x20000000, 0x00000000,},
-{ 0x1f50b57e, 0xac5884b3,},
-{ 0x1eb22cc6, 0x8aa6e26f,},
-{ 0x1e21e118, 0x0c5daab2,},
-{ 0x1d9dcd21, 0x439834e4,},
-{ 0x1d244c78, 0x367a0d65,},
-{ 0x1cb40589, 0xac173e0c,},
-{ 0x1c4bd95b, 0xa8d72b0d,},
-{ 0x1bead768, 0x98f8ce4c,},
-{ 0x1b903469, 0x050f72e5,},
-{ 0x1b3b433f, 0x2eb06f15,},
-{ 0x1aeb6f75, 0x9c46fc38,},
-{ 0x1aa038eb, 0x0e3bfd17,},
-{ 0x1a593062, 0xb38d8c56,},
-{ 0x1a15f4c3, 0x2b95a2e6,},
-{ 0x19d630dc, 0xcc7ddef9,},
-{ 0x19999999, 0x9999999a,},
-{ 0x195fec80, 0x8a609431,},
-{ 0x1928ee7b, 0x0b4f22f9,},
-{ 0x18f46acf, 0x8c06e318,},
-{ 0x18c23246, 0xdc0a9f3d,},
-#else
-{ 0x80000000, 0x00000000, 0x00000000,},
-{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,},
-{ 0x40000000, 0x00000000, 0x00000000,},
-{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,},
-{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,},
-{ 0x2d983275, 0x9d5369c4, 0x4dec1661,},
-{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,},
-{ 0x28612730, 0x6a6a7a53, 0x810fabde,},
-{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,},
-{ 0x25001383, 0xbac8a744, 0x385a3349,},
-{ 0x23b46706, 0x82c0c709, 0x3f891718,},
-{ 0x229729f1, 0xb2c83ded, 0x15fba800,},
-{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,},
-{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,},
-{ 0x20000000, 0x00000000, 0x00000000,},
-{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,},
-{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,},
-{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,},
-{ 0x1d9dcd21, 0x439834e3, 0x81667575,},
-{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,},
-{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,},
-{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,},
-{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,},
-{ 0x1b903469, 0x050f72e5, 0x0cf5488e,},
-{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,},
-{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,},
-{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,},
-{ 0x1a593062, 0xb38d8c56, 0x7998ab45,},
-{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,},
-{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,},
-{ 0x19999999, 0x99999999, 0x9999999a,},
-{ 0x195fec80, 0x8a609430, 0xe1106014,},
-{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,},
-{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,},
-{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,},
-#endif
-};
-
-static const limb_t log2_radix[BF_RADIX_MAX - 1] = {
-#if LIMB_BITS == 32
-0x20000000,
-0x32b80347,
-0x40000000,
-0x4a4d3c26,
-0x52b80347,
-0x59d5d9fd,
-0x60000000,
-0x6570068e,
-0x6a4d3c26,
-0x6eb3a9f0,
-0x72b80347,
-0x766a008e,
-0x79d5d9fd,
-0x7d053f6d,
-0x80000000,
-0x82cc7edf,
-0x8570068e,
-0x87ef05ae,
-0x8a4d3c26,
-0x8c8ddd45,
-0x8eb3a9f0,
-0x90c10501,
-0x92b80347,
-0x949a784c,
-0x966a008e,
-0x982809d6,
-0x99d5d9fd,
-0x9b74948f,
-0x9d053f6d,
-0x9e88c6b3,
-0xa0000000,
-0xa16bad37,
-0xa2cc7edf,
-0xa4231623,
-0xa570068e,
-#else
-0x2000000000000000,
-0x32b803473f7ad0f4,
-0x4000000000000000,
-0x4a4d3c25e68dc57f,
-0x52b803473f7ad0f4,
-0x59d5d9fd5010b366,
-0x6000000000000000,
-0x6570068e7ef5a1e8,
-0x6a4d3c25e68dc57f,
-0x6eb3a9f01975077f,
-0x72b803473f7ad0f4,
-0x766a008e4788cbcd,
-0x79d5d9fd5010b366,
-0x7d053f6d26089673,
-0x8000000000000000,
-0x82cc7edf592262d0,
-0x8570068e7ef5a1e8,
-0x87ef05ae409a0289,
-0x8a4d3c25e68dc57f,
-0x8c8ddd448f8b845a,
-0x8eb3a9f01975077f,
-0x90c10500d63aa659,
-0x92b803473f7ad0f4,
-0x949a784bcd1b8afe,
-0x966a008e4788cbcd,
-0x982809d5be7072dc,
-0x99d5d9fd5010b366,
-0x9b74948f5532da4b,
-0x9d053f6d26089673,
-0x9e88c6b3626a72aa,
-0xa000000000000000,
-0xa16bad3758efd873,
-0xa2cc7edf592262d0,
-0xa4231623369e78e6,
-0xa570068e7ef5a1e8,
-#endif
-};
-
-/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or
-   b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed
-   when radix is not a power of two. */
-slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
-                          int is_ceil1)
-{
-    int is_neg;
-    limb_t a;
-    BOOL is_ceil;
-
-    is_ceil = is_ceil1;
-    a = a1;
-    if (a1 < 0) {
-        a = -a;
-        is_neg = 1;
-    } else {
-        is_neg = 0;
-    }
-    is_ceil ^= is_neg;
-    if ((radix & (radix - 1)) == 0) {
-        int radix_bits;
-        /* radix is a power of two */
-        radix_bits = ceil_log2(radix);
-        if (is_inv) {
-            if (is_ceil)
-                a += radix_bits - 1;
-            a = a / radix_bits;
-        } else {
-            a = a * radix_bits;
-        }
-    } else {
-        const uint32_t *tab;
-        limb_t b0, b1;
-        dlimb_t t;
-
-        if (is_inv) {
-            tab = inv_log2_radix[radix - 2];
-#if LIMB_BITS == 32
-            b1 = tab[0];
-            b0 = tab[1];
-#else
-            b1 = ((limb_t)tab[0] << 32) | tab[1];
-            b0 = (limb_t)tab[2] << 32;
-#endif
-            t = (dlimb_t)b0 * (dlimb_t)a;
-            t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS);
-            a = t >> (LIMB_BITS - 1);
-        } else {
-            b0 = log2_radix[radix - 2];
-            t = (dlimb_t)b0 * (dlimb_t)a;
-            a = t >> (LIMB_BITS - 3);
-        }
-        /* a = floor(result) and 'result' cannot be an integer */
-        a += is_ceil;
-    }
-    if (is_neg)
-        a = -a;
-    return a;
-}
-
-/* 'n' is the number of output limbs */
-static int bf_integer_to_radix_rec(bf_t *pow_tab,
-                                   limb_t *out, const bf_t *a, limb_t n,
-                                   int level, limb_t n0, limb_t radixl,
-                                   unsigned int radixl_bits)
-{
-    limb_t n1, n2, q_prec;
-    int ret;
-
-    assert(n >= 1);
-    if (n == 1) {
-        out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);
-    } else if (n == 2) {
-        dlimb_t t;
-        slimb_t pos;
-        pos = a->len * LIMB_BITS - a->expn;
-        t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) |
-            get_bits(a->tab, a->len, pos);
-        if (likely(radixl == RADIXL_10)) {
-            /* use division by a constant when possible */
-            out[0] = t % RADIXL_10;
-            out[1] = t / RADIXL_10;
-        } else {
-            out[0] = t % radixl;
-            out[1] = t / radixl;
-        }
-    } else {
-        bf_t Q, R, *B, *B_inv;
-        int q_add;
-        bf_init(a->ctx, &Q);
-        bf_init(a->ctx, &R);
-        n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;
-        n1 = n - n2;
-        B = &pow_tab[2 * level];
-        B_inv = &pow_tab[2 * level + 1];
-        ret = 0;
-        if (B->len == 0) {
-            /* compute BASE^n2 */
-            ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ);
-            /* we use enough bits for the maximum possible 'n1' value,
-               i.e. n2 + 1 */
-            ret |= bf_set_ui(&R, 1);
-            ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN);
-        }
-        //        printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2);
-        q_prec = n1 * radixl_bits;
-        ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN);
-        ret |= bf_rint(&Q, BF_RNDZ);
-
-        ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ);
-        ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ);
-
-        if (ret & BF_ST_MEM_ERROR)
-            goto fail;
-        /* adjust if necessary */
-        q_add = 0;
-        while (R.sign && R.len != 0) {
-            if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ))
-                goto fail;
-            q_add--;
-        }
-        while (bf_cmpu(&R, B) >= 0) {
-            if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ))
-                goto fail;
-            q_add++;
-        }
-        if (q_add != 0) {
-            if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ))
-                goto fail;
-        }
-        if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0,
-                                    radixl, radixl_bits))
-            goto fail;
-        if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0,
-                                    radixl, radixl_bits)) {
-        fail:
-            bf_delete(&Q);
-            bf_delete(&R);
-            return -1;
-        }
-        bf_delete(&Q);
-        bf_delete(&R);
-    }
-    return 0;
-}
-
-/* return 0 if OK != 0 if memory error */
-static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl)
-{
-    bf_context_t *s = r->ctx;
-    limb_t r_len;
-    bf_t *pow_tab;
-    int i, pow_tab_len, ret;
-
-    r_len = r->len;
-    pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */
-    pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);
-    if (!pow_tab)
-        return -1;
-    for(i = 0; i < pow_tab_len; i++)
-        bf_init(r->ctx, &pow_tab[i]);
-
-    ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl,
-                                  ceil_log2(radixl));
-
-    for(i = 0; i < pow_tab_len; i++) {
-        bf_delete(&pow_tab[i]);
-    }
-    bf_free(s, pow_tab);
-    return ret;
-}
-
-/* a must be >= 0. 'P' is the wanted number of digits in radix
-   'radix'. 'r' is the mantissa represented as an integer. *pE
-   contains the exponent. Return != 0 if memory error. */
-static int bf_convert_to_radix(bf_t *r, slimb_t *pE,
-                               const bf_t *a, int radix,
-                               limb_t P, bf_rnd_t rnd_mode,
-                               BOOL is_fixed_exponent)
-{
-    slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0;
-    bf_t B_s, *B = &B_s;
-    int e_sign, ret, res;
-
-    if (a->len == 0) {
-        /* zero case */
-        *pE = 0;
-        return bf_set(r, a);
-    }
-
-    if (is_fixed_exponent) {
-        E = *pE;
-    } else {
-        /* compute the new exponent */
-        E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE);
-    }
-    //    bf_print_str("a", a);
-    //    printf("E=%ld P=%ld radix=%d\n", E, P, radix);
-
-    for(;;) {
-        e = P - E;
-        e_sign = 0;
-        if (e < 0) {
-            e = -e;
-            e_sign = 1;
-        }
-        /* Note: precision for log2(radix) is not critical here */
-        prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE);
-        ziv_extra_bits = 16;
-        for(;;) {
-            prec = prec0 + ziv_extra_bits;
-            /* XXX: rigorous error analysis needed */
-            extra_bits = ceil_log2(e) * 2 + 1;
-            ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits,
-                               BF_RNDN | BF_FLAG_EXT_EXP);
-            if (!e_sign)
-                ret |= bf_mul(r, r, a, prec + extra_bits,
-                              BF_RNDN | BF_FLAG_EXT_EXP);
-            else
-                ret |= bf_div(r, a, r, prec + extra_bits,
-                              BF_RNDN | BF_FLAG_EXT_EXP);
-            if (ret & BF_ST_MEM_ERROR)
-                return BF_ST_MEM_ERROR;
-            /* if the result is not exact, check that it can be safely
-               rounded to an integer */
-            if ((ret & BF_ST_INEXACT) &&
-                !bf_can_round(r, r->expn, rnd_mode, prec)) {
-                /* and more precision and retry */
-                ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);
-                continue;
-            } else {
-                ret = bf_rint(r, rnd_mode);
-                if (ret & BF_ST_MEM_ERROR)
-                    return BF_ST_MEM_ERROR;
-                break;
-            }
-        }
-        if (is_fixed_exponent)
-            break;
-        /* check that the result is < B^P */
-        /* XXX: do a fast approximate test first ? */
-        bf_init(r->ctx, B);
-        ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ);
-        if (ret) {
-            bf_delete(B);
-            return ret;
-        }
-        res = bf_cmpu(r, B);
-        bf_delete(B);
-        if (res < 0)
-            break;
-        /* try a larger exponent */
-        E++;
-    }
-    *pE = E;
-    return 0;
-}
-
-static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len)
-{
-    int digit, i;
-
-    if (radix == 10) {
-        /* specific case with constant divisor */
-        for(i = len - 1; i >= 0; i--) {
-            digit = (limb_t)n % 10;
-            n = (limb_t)n / 10;
-            buf[i] = digit + '0';
-        }
-    } else {
-        for(i = len - 1; i >= 0; i--) {
-            digit = (limb_t)n % radix;
-            n = (limb_t)n / radix;
-            if (digit < 10)
-                digit += '0';
-            else
-                digit += 'a' - 10;
-            buf[i] = digit;
-        }
-    }
-}
-
-/* for power of 2 radixes */
-static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len)
-{
-    int digit, i;
-    unsigned int mask;
-
-    mask = (1 << radix_bits) - 1;
-    for(i = len - 1; i >= 0; i--) {
-        digit = n & mask;
-        n >>= radix_bits;
-        if (digit < 10)
-            digit += '0';
-        else
-            digit += 'a' - 10;
-        buf[i] = digit;
-    }
-}
-
-/* 'a' must be an integer if the is_dec = FALSE or if the radix is not
-   a power of two. A dot is added before the 'dot_pos' digit. dot_pos
-   = n_digits does not display the dot. 0 <= dot_pos <=
-   n_digits. n_digits >= 1. */
-static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
-                          limb_t dot_pos, BOOL is_dec)
-{
-    limb_t i, v, l;
-    slimb_t pos, pos_incr;
-    int digits_per_limb, buf_pos, radix_bits, first_buf_pos;
-    char buf[65];
-    bf_t a_s, *a;
-
-    if (is_dec) {
-        digits_per_limb = LIMB_DIGITS;
-        a = (bf_t *)a1;
-        radix_bits = 0;
-        pos = a->len;
-        pos_incr = 1;
-        first_buf_pos = 0;
-    } else if ((radix & (radix - 1)) == 0) {
-        a = (bf_t *)a1;
-        radix_bits = ceil_log2(radix);
-        digits_per_limb = LIMB_BITS / radix_bits;
-        pos_incr = digits_per_limb * radix_bits;
-        /* digits are aligned relative to the radix point */
-        pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits);
-        first_buf_pos = 0;
-    } else {
-        limb_t n, radixl;
-
-        digits_per_limb = digits_per_limb_table[radix - 2];
-        radixl = get_limb_radix(radix);
-        a = &a_s;
-        bf_init(a1->ctx, a);
-        n = (n_digits + digits_per_limb - 1) / digits_per_limb;
-        if (bf_resize(a, n)) {
-            dbuf_set_error(s);
-            goto done;
-        }
-        if (bf_integer_to_radix(a, a1, radixl)) {
-            dbuf_set_error(s);
-            goto done;
-        }
-        radix_bits = 0;
-        pos = n;
-        pos_incr = 1;
-        first_buf_pos = pos * digits_per_limb - n_digits;
-    }
-    buf_pos = digits_per_limb;
-    i = 0;
-    while (i < n_digits) {
-        if (buf_pos == digits_per_limb) {
-            pos -= pos_incr;
-            if (radix_bits == 0) {
-                v = get_limbz(a, pos);
-                limb_to_a(buf, v, radix, digits_per_limb);
-            } else {
-                v = get_bits(a->tab, a->len, pos);
-                limb_to_a2(buf, v, radix_bits, digits_per_limb);
-            }
-            buf_pos = first_buf_pos;
-            first_buf_pos = 0;
-        }
-        if (i < dot_pos) {
-            l = dot_pos;
-        } else {
-            if (i == dot_pos)
-                dbuf_putc(s, '.');
-            l = n_digits;
-        }
-        l = bf_min(digits_per_limb - buf_pos, l - i);
-        dbuf_put(s, (uint8_t *)(buf + buf_pos), l);
-        buf_pos += l;
-        i += l;
-    }
- done:
-    if (a != a1)
-        bf_delete(a);
-}
-
-static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size)
-{
-    bf_context_t *s = opaque;
-    return bf_realloc(s, ptr, size);
-}
-
-/* return the length in bytes. A trailing '\0' is added */
-static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,
-                              limb_t prec, bf_flags_t flags, BOOL is_dec)
-{
-    bf_context_t *ctx = a2->ctx;
-    DynBuf s_s, *s = &s_s;
-    int radix_bits;
-
-    //    bf_print_str("ftoa", a2);
-    //    printf("radix=%d\n", radix);
-    dbuf_init2(s, ctx, bf_dbuf_realloc);
-    if (a2->expn == BF_EXP_NAN) {
-        dbuf_putstr(s, "NaN");
-    } else {
-        if (a2->sign)
-            dbuf_putc(s, '-');
-        if (a2->expn == BF_EXP_INF) {
-            if (flags & BF_FTOA_JS_QUIRKS)
-                dbuf_putstr(s, "Infinity");
-            else
-                dbuf_putstr(s, "Inf");
-        } else {
-            int fmt, ret;
-            slimb_t n_digits, n, i, n_max, n1;
-            bf_t a1_s, *a1 = &a1_s;
-
-            if ((radix & (radix - 1)) != 0)
-                radix_bits = 0;
-            else
-                radix_bits = ceil_log2(radix);
-
-            fmt = flags & BF_FTOA_FORMAT_MASK;
-            bf_init(ctx, a1);
-            if (fmt == BF_FTOA_FORMAT_FRAC) {
-                if (is_dec || radix_bits != 0) {
-                    if (bf_set(a1, a2))
-                        goto fail1;
-#ifdef USE_BF_DEC
-                    if (is_dec) {
-                        if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)
-                            goto fail1;
-                        n = a1->expn;
-                    } else
-#endif
-                    {
-                        if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)
-                            goto fail1;
-                        n = ceil_div(a1->expn, radix_bits);
-                    }
-                    if (flags & BF_FTOA_ADD_PREFIX) {
-                        if (radix == 16)
-                            dbuf_putstr(s, "0x");
-                        else if (radix == 8)
-                            dbuf_putstr(s, "0o");
-                        else if (radix == 2)
-                            dbuf_putstr(s, "0b");
-                    }
-                    if (a1->expn == BF_EXP_ZERO) {
-                        dbuf_putstr(s, "0");
-                        if (prec > 0) {
-                            dbuf_putstr(s, ".");
-                            for(i = 0; i < prec; i++) {
-                                dbuf_putc(s, '0');
-                            }
-                        }
-                    } else {
-                        n_digits = prec + n;
-                        if (n <= 0) {
-                            /* 0.x */
-                            dbuf_putstr(s, "0.");
-                            for(i = 0; i < -n; i++) {
-                                dbuf_putc(s, '0');
-                            }
-                            if (n_digits > 0) {
-                                output_digits(s, a1, radix, n_digits, n_digits, is_dec);
-                            }
-                        } else {
-                            output_digits(s, a1, radix, n_digits, n, is_dec);
-                        }
-                    }
-                } else {
-                    size_t pos, start;
-                    bf_t a_s, *a = &a_s;
-
-                    /* make a positive number */
-                    a->tab = a2->tab;
-                    a->len = a2->len;
-                    a->expn = a2->expn;
-                    a->sign = 0;
-
-                    /* one more digit for the rounding */
-                    n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE);
-                    n_digits = n + prec;
-                    n1 = n;
-                    if (bf_convert_to_radix(a1, &n1, a, radix, n_digits,
-                                            flags & BF_RND_MASK, TRUE))
-                        goto fail1;
-                    start = s->size;
-                    output_digits(s, a1, radix, n_digits, n, is_dec);
-                    /* remove leading zeros because we allocated one more digit */
-                    pos = start;
-                    while ((pos + 1) < s->size && s->buf[pos] == '0' &&
-                           s->buf[pos + 1] != '.')
-                        pos++;
-                    if (pos > start) {
-                        memmove(s->buf + start, s->buf + pos, s->size - pos);
-                        s->size -= (pos - start);
-                    }
-                }
-            } else {
-#ifdef USE_BF_DEC
-                if (is_dec) {
-                    if (bf_set(a1, a2))
-                        goto fail1;
-                    if (fmt == BF_FTOA_FORMAT_FIXED) {
-                        n_digits = prec;
-                        n_max = n_digits;
-                        if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)
-                            goto fail1;
-                    } else {
-                        /* prec is ignored */
-                        prec = n_digits = a1->len * LIMB_DIGITS;
-                        /* remove the trailing zero digits */
-                        while (n_digits > 1 &&
-                               get_digit(a1->tab, a1->len, prec - n_digits) == 0) {
-                            n_digits--;
-                        }
-                        n_max = n_digits + 4;
-                    }
-                    n = a1->expn;
-                } else
-#endif
-                if (radix_bits != 0) {
-                    if (bf_set(a1, a2))
-                        goto fail1;
-                    if (fmt == BF_FTOA_FORMAT_FIXED) {
-                        slimb_t prec_bits;
-                        n_digits = prec;
-                        n_max = n_digits;
-                        /* align to the radix point */
-                        prec_bits = prec * radix_bits -
-                            smod(-a1->expn, radix_bits);
-                        if (bf_round(a1, prec_bits,
-                                     (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)
-                            goto fail1;
-                    } else {
-                        limb_t digit_mask;
-                        slimb_t pos;
-                        /* position of the digit before the most
-                           significant digit in bits */
-                        pos = a1->len * LIMB_BITS +
-                            smod(-a1->expn, radix_bits);
-                        n_digits = ceil_div(pos, radix_bits);
-                        /* remove the trailing zero digits */
-                        digit_mask = ((limb_t)1 << radix_bits) - 1;
-                        while (n_digits > 1 &&
-                               (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) {
-                            n_digits--;
-                        }
-                        n_max = n_digits + 4;
-                    }
-                    n = ceil_div(a1->expn, radix_bits);
-                } else {
-                    bf_t a_s, *a = &a_s;
-
-                    /* make a positive number */
-                    a->tab = a2->tab;
-                    a->len = a2->len;
-                    a->expn = a2->expn;
-                    a->sign = 0;
-
-                    if (fmt == BF_FTOA_FORMAT_FIXED) {
-                        n_digits = prec;
-                        n_max = n_digits;
-                    } else {
-                        slimb_t n_digits_max, n_digits_min;
-
-                        assert(prec != BF_PREC_INF);
-                        n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE);
-                        /* max number of digits for non exponential
-                           notation. The rational is to have the same rule
-                           as JS i.e. n_max = 21 for 64 bit float in base 10. */
-                        n_max = n_digits + 4;
-                        if (fmt == BF_FTOA_FORMAT_FREE_MIN) {
-                            bf_t b_s, *b = &b_s;
-
-                            /* find the minimum number of digits by
-                               dichotomy. */
-                            /* XXX: inefficient */
-                            n_digits_max = n_digits;
-                            n_digits_min = 1;
-                            bf_init(ctx, b);
-                            while (n_digits_min < n_digits_max) {
-                                n_digits = (n_digits_min + n_digits_max) / 2;
-                                if (bf_convert_to_radix(a1, &n, a, radix, n_digits,
-                                                        flags & BF_RND_MASK, FALSE)) {
-                                    bf_delete(b);
-                                    goto fail1;
-                                }
-                                /* convert back to a number and compare */
-                                ret = bf_mul_pow_radix(b, a1, radix, n - n_digits,
-                                                       prec,
-                                                       (flags & ~BF_RND_MASK) |
-                                                       BF_RNDN);
-                                if (ret & BF_ST_MEM_ERROR) {
-                                    bf_delete(b);
-                                    goto fail1;
-                                }
-                                if (bf_cmpu(b, a) == 0) {
-                                    n_digits_max = n_digits;
-                                } else {
-                                    n_digits_min = n_digits + 1;
-                                }
-                            }
-                            bf_delete(b);
-                            n_digits = n_digits_max;
-                        }
-                    }
-                    if (bf_convert_to_radix(a1, &n, a, radix, n_digits,
-                                            flags & BF_RND_MASK, FALSE)) {
-                    fail1:
-                        bf_delete(a1);
-                        goto fail;
-                    }
-                }
-                if (a1->expn == BF_EXP_ZERO &&
-                    fmt != BF_FTOA_FORMAT_FIXED &&
-                    !(flags & BF_FTOA_FORCE_EXP)) {
-                    /* just output zero */
-                    dbuf_putstr(s, "0");
-                } else {
-                    if (flags & BF_FTOA_ADD_PREFIX) {
-                        if (radix == 16)
-                            dbuf_putstr(s, "0x");
-                        else if (radix == 8)
-                            dbuf_putstr(s, "0o");
-                        else if (radix == 2)
-                            dbuf_putstr(s, "0b");
-                    }
-                    if (a1->expn == BF_EXP_ZERO)
-                        n = 1;
-                    if ((flags & BF_FTOA_FORCE_EXP) ||
-                        n <= -6 || n > n_max) {
-                        const char *fmt;
-                        /* exponential notation */
-                        output_digits(s, a1, radix, n_digits, 1, is_dec);
-                        if (radix_bits != 0 && radix <= 16) {
-                            if (flags & BF_FTOA_JS_QUIRKS)
-                                fmt = "p%+" PRId_LIMB;
-                            else
-                                fmt = "p%" PRId_LIMB;
-                            dbuf_printf(s, fmt, (n - 1) * radix_bits);
-                        } else {
-                            if (flags & BF_FTOA_JS_QUIRKS)
-                                fmt = "%c%+" PRId_LIMB;
-                            else
-                                fmt = "%c%" PRId_LIMB;
-                            dbuf_printf(s, fmt,
-                                        radix <= 10 ? 'e' : '@', n - 1);
-                        }
-                    } else if (n <= 0) {
-                        /* 0.x */
-                        dbuf_putstr(s, "0.");
-                        for(i = 0; i < -n; i++) {
-                            dbuf_putc(s, '0');
-                        }
-                        output_digits(s, a1, radix, n_digits, n_digits, is_dec);
-                    } else {
-                        if (n_digits <= n) {
-                            /* no dot */
-                            output_digits(s, a1, radix, n_digits, n_digits, is_dec);
-                            for(i = 0; i < (n - n_digits); i++)
-                                dbuf_putc(s, '0');
-                        } else {
-                            output_digits(s, a1, radix, n_digits, n, is_dec);
-                        }
-                    }
-                }
-            }
-            bf_delete(a1);
-        }
-    }
-    dbuf_putc(s, '\0');
-    if (dbuf_error(s))
-        goto fail;
-    if (plen)
-        *plen = s->size - 1;
-    return (char *)s->buf;
- fail:
-    bf_free(ctx, s->buf);
-    if (plen)
-        *plen = 0;
-    return NULL;
-}
-
-char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
-              bf_flags_t flags)
-{
-    return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE);
-}
-
-/***************************************************************/
-/* transcendental functions */
-
-/* Note: the algorithm is from MPFR */
-static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1,
-                              limb_t n2, BOOL need_P)
-{
-    bf_context_t *s = T->ctx;
-    if ((n2 - n1) == 1) {
-        if (n1 == 0) {
-            bf_set_ui(P, 3);
-        } else {
-            bf_set_ui(P, n1);
-            P->sign = 1;
-        }
-        bf_set_ui(Q, 2 * n1 + 1);
-        Q->expn += 2;
-        bf_set(T, P);
-    } else {
-        limb_t m;
-        bf_t T1_s, *T1 = &T1_s;
-        bf_t P1_s, *P1 = &P1_s;
-        bf_t Q1_s, *Q1 = &Q1_s;
-
-        m = n1 + ((n2 - n1) >> 1);
-        bf_const_log2_rec(T, P, Q, n1, m, TRUE);
-        bf_init(s, T1);
-        bf_init(s, P1);
-        bf_init(s, Q1);
-        bf_const_log2_rec(T1, P1, Q1, m, n2, need_P);
-        bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ);
-        bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ);
-        bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ);
-        if (need_P)
-            bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ);
-        bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ);
-        bf_delete(T1);
-        bf_delete(P1);
-        bf_delete(Q1);
-    }
-}
-
-/* compute log(2) with faithful rounding at precision 'prec' */
-static void bf_const_log2_internal(bf_t *T, limb_t prec)
-{
-    limb_t w, N;
-    bf_t P_s, *P = &P_s;
-    bf_t Q_s, *Q = &Q_s;
-
-    w = prec + 15;
-    N = w / 3 + 1;
-    bf_init(T->ctx, P);
-    bf_init(T->ctx, Q);
-    bf_const_log2_rec(T, P, Q, 0, N, FALSE);
-    bf_div(T, T, Q, prec, BF_RNDN);
-    bf_delete(P);
-    bf_delete(Q);
-}
-
-/* PI constant */
-
-#define CHUD_A 13591409
-#define CHUD_B 545140134
-#define CHUD_C 640320
-#define CHUD_BITS_PER_TERM 47
-
-static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g,
-                    limb_t prec)
-{
-    bf_context_t *s = P->ctx;
-    int64_t c;
-
-    if (a == (b - 1)) {
-        bf_t T0, T1;
-
-        bf_init(s, &T0);
-        bf_init(s, &T1);
-        bf_set_ui(G, 2 * b - 1);
-        bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN);
-        bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN);
-        bf_set_ui(&T0, CHUD_B);
-        bf_mul_ui(&T0, &T0, b, prec, BF_RNDN);
-        bf_set_ui(&T1, CHUD_A);
-        bf_add(&T0, &T0, &T1, prec, BF_RNDN);
-        bf_mul(P, G, &T0, prec, BF_RNDN);
-        P->sign = b & 1;
-
-        bf_set_ui(Q, b);
-        bf_mul_ui(Q, Q, b, prec, BF_RNDN);
-        bf_mul_ui(Q, Q, b, prec, BF_RNDN);
-        bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN);
-        bf_delete(&T0);
-        bf_delete(&T1);
-    } else {
-        bf_t P2, Q2, G2;
-
-        bf_init(s, &P2);
-        bf_init(s, &Q2);
-        bf_init(s, &G2);
-
-        c = (a + b) / 2;
-        chud_bs(P, Q, G, a, c, 1, prec);
-        chud_bs(&P2, &Q2, &G2, c, b, need_g, prec);
-
-        /* Q = Q1 * Q2 */
-        /* G = G1 * G2 */
-        /* P = P1 * Q2 + P2 * G1 */
-        bf_mul(&P2, &P2, G, prec, BF_RNDN);
-        if (!need_g)
-            bf_set_ui(G, 0);
-        bf_mul(P, P, &Q2, prec, BF_RNDN);
-        bf_add(P, P, &P2, prec, BF_RNDN);
-        bf_delete(&P2);
-
-        bf_mul(Q, Q, &Q2, prec, BF_RNDN);
-        bf_delete(&Q2);
-        if (need_g)
-            bf_mul(G, G, &G2, prec, BF_RNDN);
-        bf_delete(&G2);
-    }
-}
-
-/* compute Pi with faithful rounding at precision 'prec' using the
-   Chudnovsky formula */
-static void bf_const_pi_internal(bf_t *Q, limb_t prec)
-{
-    bf_context_t *s = Q->ctx;
-    int64_t n, prec1;
-    bf_t P, G;
-
-    /* number of serie terms */
-    n = prec / CHUD_BITS_PER_TERM + 1;
-    /* XXX: precision analysis */
-    prec1 = prec + 32;
-
-    bf_init(s, &P);
-    bf_init(s, &G);
-
-    chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF);
-
-    bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN);
-    bf_add(&P, &G, &P, prec1, BF_RNDN);
-    bf_div(Q, Q, &P, prec1, BF_RNDF);
-
-    bf_set_ui(&P, CHUD_C);
-    bf_sqrt(&G, &P, prec1, BF_RNDF);
-    bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF);
-    bf_mul(Q, Q, &G, prec, BF_RNDN);
-    bf_delete(&P);
-    bf_delete(&G);
-}
-
-static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags,
-                        BFConstCache *c,
-                        void (*func)(bf_t *res, limb_t prec), int sign)
-{
-    limb_t ziv_extra_bits, prec1;
-
-    ziv_extra_bits = 32;
-    for(;;) {
-        prec1 = prec + ziv_extra_bits;
-        if (c->prec < prec1) {
-            if (c->val.len == 0)
-                bf_init(T->ctx, &c->val);
-            func(&c->val, prec1);
-            c->prec = prec1;
-        } else {
-            prec1 = c->prec;
-        }
-        bf_set(T, &c->val);
-        T->sign = sign;
-        if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) {
-            /* and more precision and retry */
-            ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);
-        } else {
-            break;
-        }
-    }
-    return bf_round(T, prec, flags);
-}
-
-static void bf_const_free(BFConstCache *c)
-{
-    bf_delete(&c->val);
-    memset(c, 0, sizeof(*c));
-}
-
-int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = T->ctx;
-    return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0);
-}
-
-/* return rounded pi * (1 - 2 * sign) */
-static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = T->ctx;
-    return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal,
-                        sign);
-}
-
-int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags)
-{
-    return bf_const_pi_signed(T, 0, prec, flags);
-}
-
-void bf_clear_cache(bf_context_t *s)
-{
-#ifdef USE_FFT_MUL
-    fft_clear_cache(s);
-#endif
-    bf_const_free(&s->log2_cache);
-    bf_const_free(&s->pi_cache);
-}
-
-/* ZivFunc should compute the result 'r' with faithful rounding at
-   precision 'prec'. For efficiency purposes, the final bf_round()
-   does not need to be done in the function. */
-typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque);
-
-static int bf_ziv_rounding(bf_t *r, const bf_t *a,
-                           limb_t prec, bf_flags_t flags,
-                           ZivFunc *f, void *opaque)
-{
-    int rnd_mode, ret;
-    slimb_t prec1, ziv_extra_bits;
-
-    rnd_mode = flags & BF_RND_MASK;
-    if (rnd_mode == BF_RNDF) {
-        /* no need to iterate */
-        f(r, a, prec, opaque);
-        ret = 0;
-    } else {
-        ziv_extra_bits = 32;
-        for(;;) {
-            prec1 = prec + ziv_extra_bits;
-            ret = f(r, a, prec1, opaque);
-            if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) {
-                /* overflow or underflow should never happen because
-                   it indicates the rounding cannot be done correctly,
-                   but we do not catch all the cases */
-                return ret;
-            }
-            /* if the result is exact, we can stop */
-            if (!(ret & BF_ST_INEXACT)) {
-                ret = 0;
-                break;
-            }
-            if (bf_can_round(r, prec, rnd_mode, prec1)) {
-                ret = BF_ST_INEXACT;
-                break;
-            }
-            ziv_extra_bits = ziv_extra_bits * 2;
-            //            printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits);
-        }
-    }
-    if (r->len == 0)
-        return ret;
-    else
-        return __bf_round(r, prec, flags, r->len, ret);
-}
-
-/* add (1 - 2*e_sign) * 2^e */
-static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign,
-                          limb_t prec, int flags)
-{
-    bf_t T_s, *T = &T_s;
-    int ret;
-    /* small argument case: result = 1 + epsilon * sign(x) */
-    bf_init(a->ctx, T);
-    bf_set_ui(T, 1);
-    T->sign = e_sign;
-    T->expn += e;
-    ret = bf_add(r, r, T, prec, flags);
-    bf_delete(T);
-    return ret;
-}
-
-/* Compute the exponential using faithful rounding at precision 'prec'.
-   Note: the algorithm is from MPFR */
-static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    slimb_t n, K, l, i, prec1;
-
-    assert(r != a);
-
-    /* argument reduction:
-       T = a - n*log(2) with 0 <= T < log(2) and n integer.
-    */
-    bf_init(s, T);
-    if (a->expn <= -1) {
-        /* 0 <= abs(a) <= 0.5 */
-        if (a->sign)
-            n = -1;
-        else
-            n = 0;
-    } else {
-        bf_const_log2(T, LIMB_BITS, BF_RNDZ);
-        bf_div(T, a, T, LIMB_BITS, BF_RNDD);
-        bf_get_limb(&n, T, 0);
-    }
-
-    K = bf_isqrt((prec + 1) / 2);
-    l = (prec - 1) / K + 1;
-    /* XXX: precision analysis ? */
-    prec1 = prec + (K + 2 * l + 18) + K + 8;
-    if (a->expn > 0)
-        prec1 += a->expn;
-    //    printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1);
-
-    bf_const_log2(T, prec1, BF_RNDF);
-    bf_mul_si(T, T, n, prec1, BF_RNDN);
-    bf_sub(T, a, T, prec1, BF_RNDN);
-
-    /* reduce the range of T */
-    bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ);
-
-    /* Taylor expansion around zero :
-     1 + x + x^2/2 + ... + x^n/n!
-     = (1 + x * (1 + x/2 * (1 + ... (x/n))))
-    */
-    {
-        bf_t U_s, *U = &U_s;
-
-        bf_init(s, U);
-        bf_set_ui(r, 1);
-        for(i = l ; i >= 1; i--) {
-            bf_set_ui(U, i);
-            bf_div(U, T, U, prec1, BF_RNDN);
-            bf_mul(r, r, U, prec1, BF_RNDN);
-            bf_add_si(r, r, 1, prec1, BF_RNDN);
-        }
-        bf_delete(U);
-    }
-    bf_delete(T);
-
-    /* undo the range reduction */
-    for(i = 0; i < K; i++) {
-        bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
-    }
-
-    /* undo the argument reduction */
-    bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP);
-
-    return BF_ST_INEXACT;
-}
-
-/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */
-static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r,
-                                        const bf_t *a_low, const bf_t *a_high,
-                                        limb_t prec, bf_flags_t flags)
-{
-    bf_t T_s, *T = &T_s;
-    bf_t log2_s, *log2 = &log2_s;
-    slimb_t e_min, e_max;
-
-    if (a_high->expn <= 0)
-        return 0;
-
-    e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
-    e_min = -e_max + 3;
-    if (flags & BF_FLAG_SUBNORMAL)
-        e_min -= (prec - 1);
-
-    bf_init(s, T);
-    bf_init(s, log2);
-    bf_const_log2(log2, LIMB_BITS, BF_RNDU);
-    bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU);
-    /* a_low > e_max * log(2) implies exp(a) > e_max */
-    if (bf_cmp_lt(T, a_low) > 0) {
-        /* overflow */
-        bf_delete(T);
-        bf_delete(log2);
-        return bf_set_overflow(r, 0, prec, flags);
-    }
-    /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */
-    bf_const_log2(log2, LIMB_BITS, BF_RNDD);
-    bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD);
-    if (bf_cmp_lt(a_high, T)) {
-        int rnd_mode = flags & BF_RND_MASK;
-
-        /* underflow */
-        bf_delete(T);
-        bf_delete(log2);
-        if (rnd_mode == BF_RNDU) {
-            /* set the smallest value */
-            bf_set_ui(r, 1);
-            r->expn = e_min;
-        } else {
-            bf_set_zero(r, 0);
-        }
-        return BF_ST_UNDERFLOW | BF_ST_INEXACT;
-    }
-    bf_delete(log2);
-    bf_delete(T);
-    return 0;
-}
-
-int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    int ret;
-    assert(r != a);
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-        } else if (a->expn == BF_EXP_INF) {
-            if (a->sign)
-                bf_set_zero(r, 0);
-            else
-                bf_set_inf(r, 0);
-        } else {
-            bf_set_ui(r, 1);
-        }
-        return 0;
-    }
-
-    ret = check_exp_underflow_overflow(s, r, a, a, prec, flags);
-    if (ret)
-        return ret;
-    if (a->expn < 0 && (-a->expn) >= (prec + 2)) {
-        /* small argument case: result = 1 + epsilon * sign(x) */
-        bf_set_ui(r, 1);
-        return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags);
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL);
-}
-
-static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    bf_t U_s, *U = &U_s;
-    bf_t V_s, *V = &V_s;
-    slimb_t n, prec1, l, i, K;
-
-    assert(r != a);
-
-    bf_init(s, T);
-    /* argument reduction 1 */
-    /* T=a*2^n with 2/3 <= T <= 4/3 */
-    {
-        bf_t U_s, *U = &U_s;
-        bf_set(T, a);
-        n = T->expn;
-        T->expn = 0;
-        /* U= ~ 2/3 */
-        bf_init(s, U);
-        bf_set_ui(U, 0xaaaaaaaa);
-        U->expn = 0;
-        if (bf_cmp_lt(T, U)) {
-            T->expn++;
-            n--;
-        }
-        bf_delete(U);
-    }
-    //    printf("n=%ld\n", n);
-    //    bf_print_str("T", T);
-
-    /* XXX: precision analysis */
-    /* number of iterations for argument reduction 2 */
-    K = bf_isqrt((prec + 1) / 2);
-    /* order of Taylor expansion */
-    l = prec / (2 * K) + 1;
-    /* precision of the intermediate computations */
-    prec1 = prec + K + 2 * l + 32;
-
-    bf_init(s, U);
-    bf_init(s, V);
-
-    /* Note: cancellation occurs here, so we use more precision (XXX:
-       reduce the precision by computing the exact cancellation) */
-    bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN);
-
-    /* argument reduction 2 */
-    for(i = 0; i < K; i++) {
-        /* T = T / (1 + sqrt(1 + T)) */
-        bf_add_si(U, T, 1, prec1, BF_RNDN);
-        bf_sqrt(V, U, prec1, BF_RNDF);
-        bf_add_si(U, V, 1, prec1, BF_RNDN);
-        bf_div(T, T, U, prec1, BF_RNDN);
-    }
-
-    {
-        bf_t Y_s, *Y = &Y_s;
-        bf_t Y2_s, *Y2 = &Y2_s;
-        bf_init(s, Y);
-        bf_init(s, Y2);
-
-        /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x)
-           = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1)
-           with Y=Y^2
-           = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...)))
-        */
-        bf_add_si(Y, T, 2, prec1, BF_RNDN);
-        bf_div(Y, T, Y, prec1, BF_RNDN);
-
-        bf_mul(Y2, Y, Y, prec1, BF_RNDN);
-        bf_set_ui(r, 0);
-        for(i = l; i >= 1; i--) {
-            bf_set_ui(U, 1);
-            bf_set_ui(V, 2 * i + 1);
-            bf_div(U, U, V, prec1, BF_RNDN);
-            bf_add(r, r, U, prec1, BF_RNDN);
-            bf_mul(r, r, Y2, prec1, BF_RNDN);
-        }
-        bf_add_si(r, r, 1, prec1, BF_RNDN);
-        bf_mul(r, r, Y, prec1, BF_RNDN);
-        bf_delete(Y);
-        bf_delete(Y2);
-    }
-    bf_delete(V);
-    bf_delete(U);
-
-    /* multiplication by 2 for the Taylor expansion and undo the
-       argument reduction 2*/
-    bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ);
-
-    /* undo the argument reduction 1 */
-    bf_const_log2(T, prec1, BF_RNDF);
-    bf_mul_si(T, T, n, prec1, BF_RNDN);
-    bf_add(r, r, T, prec1, BF_RNDN);
-
-    bf_delete(T);
-    return BF_ST_INEXACT;
-}
-
-int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-
-    assert(r != a);
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            if (a->sign) {
-                bf_set_nan(r);
-                return BF_ST_INVALID_OP;
-            } else {
-                bf_set_inf(r, 0);
-                return 0;
-            }
-        } else {
-            bf_set_inf(r, 1);
-            return 0;
-        }
-    }
-    if (a->sign) {
-        bf_set_nan(r);
-        return BF_ST_INVALID_OP;
-    }
-    bf_init(s, T);
-    bf_set_ui(T, 1);
-    if (bf_cmp_eq(a, T)) {
-        bf_set_zero(r, 0);
-        bf_delete(T);
-        return 0;
-    }
-    bf_delete(T);
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL);
-}
-
-/* x and y finite and x > 0 */
-static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    const bf_t *y = opaque;
-    bf_t T_s, *T = &T_s;
-    limb_t prec1;
-
-    bf_init(s, T);
-    /* XXX: proof for the added precision */
-    prec1 = prec + 32;
-    bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP);
-    bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP);
-    if (bf_is_nan(T))
-        bf_set_nan(r);
-    else
-        bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */
-    bf_delete(T);
-    return BF_ST_INEXACT;
-}
-
-/* x and y finite, x > 0, y integer and y fits on one limb */
-static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    const bf_t *y = opaque;
-    bf_t T_s, *T = &T_s;
-    limb_t prec1;
-    int ret;
-    slimb_t y1;
-
-    bf_get_limb(&y1, y, 0);
-    if (y1 < 0)
-        y1 = -y1;
-    /* XXX: proof for the added precision */
-    prec1 = prec + ceil_log2(y1) * 2 + 8;
-    ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
-    if (y->sign) {
-        bf_init(s, T);
-        bf_set_ui(T, 1);
-        ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
-        bf_delete(T);
-    }
-    return ret;
-}
-
-/* x must be a finite non zero float. Return TRUE if there is a
-   floating point number r such as x=r^(2^n) and return this floating
-   point number 'r'. Otherwise return FALSE and r is undefined. */
-static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    slimb_t e, i, er;
-    limb_t v;
-
-    /* x = m*2^e with m odd integer */
-    e = bf_get_exp_min(x);
-    /* fast check on the exponent */
-    if (n > (LIMB_BITS - 1)) {
-        if (e != 0)
-            return FALSE;
-        er = 0;
-    } else {
-        if ((e & (((limb_t)1 << n) - 1)) != 0)
-            return FALSE;
-        er = e >> n;
-    }
-    /* every perfect odd square = 1 modulo 8 */
-    v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e);
-    if ((v & 7) != 1)
-        return FALSE;
-
-    bf_init(s, T);
-    bf_set(T, x);
-    T->expn -= e;
-    for(i = 0; i < n; i++) {
-        if (i != 0)
-            bf_set(T, r);
-        if (bf_sqrtrem(r, NULL, T) != 0)
-            return FALSE;
-    }
-    r->expn += er;
-    return TRUE;
-}
-
-/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */
-int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    bf_t ytmp_s;
-    BOOL y_is_int, y_is_odd;
-    int r_sign, ret, rnd_mode;
-    slimb_t y_emin;
-
-    if (x->len == 0 || y->len == 0) {
-        if (y->expn == BF_EXP_ZERO) {
-            /* pow(x, 0) = 1 */
-            bf_set_ui(r, 1);
-        } else if (x->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-        } else {
-            int cmp_x_abs_1;
-            bf_set_ui(r, 1);
-            cmp_x_abs_1 = bf_cmpu(x, r);
-            if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) &&
-                (y->expn >= BF_EXP_INF)) {
-                bf_set_nan(r);
-            } else if (cmp_x_abs_1 == 0 &&
-                       (!x->sign || y->expn != BF_EXP_NAN)) {
-                /* pow(1, y) = 1 even if y = NaN */
-                /* pow(-1, +/-inf) = 1 */
-            } else if (y->expn == BF_EXP_NAN) {
-                bf_set_nan(r);
-            } else if (y->expn == BF_EXP_INF) {
-                if (y->sign == (cmp_x_abs_1 > 0)) {
-                    bf_set_zero(r, 0);
-                } else {
-                    bf_set_inf(r, 0);
-                }
-            } else {
-                y_emin = bf_get_exp_min(y);
-                y_is_odd = (y_emin == 0);
-                if (y->sign == (x->expn == BF_EXP_ZERO)) {
-                    bf_set_inf(r, y_is_odd & x->sign);
-                    if (y->sign) {
-                        /* pow(0, y) with y < 0 */
-                        return BF_ST_DIVIDE_ZERO;
-                    }
-                } else {
-                    bf_set_zero(r, y_is_odd & x->sign);
-                }
-            }
-        }
-        return 0;
-    }
-    bf_init(s, T);
-    bf_set(T, x);
-    y_emin = bf_get_exp_min(y);
-    y_is_int = (y_emin >= 0);
-    rnd_mode = flags & BF_RND_MASK;
-    if (x->sign) {
-        if (!y_is_int) {
-            bf_set_nan(r);
-            bf_delete(T);
-            return BF_ST_INVALID_OP;
-        }
-        y_is_odd = (y_emin == 0);
-        r_sign = y_is_odd;
-        /* change the directed rounding mode if the sign of the result
-           is changed */
-        if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU))
-            flags ^= 1;
-        bf_neg(T);
-    } else {
-        r_sign = 0;
-    }
-
-    bf_set_ui(r, 1);
-    if (bf_cmp_eq(T, r)) {
-        /* abs(x) = 1: nothing more to do */
-        ret = 0;
-    } else {
-        /* check the overflow/underflow cases */
-        {
-            bf_t al_s, *al = &al_s;
-            bf_t ah_s, *ah = &ah_s;
-            limb_t precl = LIMB_BITS;
-
-            bf_init(s, al);
-            bf_init(s, ah);
-            /* compute bounds of log(abs(x)) * y with a low precision */
-            /* XXX: compute bf_log() once */
-            /* XXX: add a fast test before this slow test */
-            bf_log(al, T, precl, BF_RNDD);
-            bf_log(ah, T, precl, BF_RNDU);
-            bf_mul(al, al, y, precl, BF_RNDD ^ y->sign);
-            bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign);
-            ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags);
-            bf_delete(al);
-            bf_delete(ah);
-            if (ret)
-                goto done;
-        }
-
-        if (y_is_int) {
-            slimb_t T_bits, e;
-        int_pow:
-            T_bits = T->expn - bf_get_exp_min(T);
-            if (T_bits == 1) {
-                /* pow(2^b, y) = 2^(b*y) */
-                bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ);
-                bf_get_limb(&e, T, 0);
-                bf_set_ui(r, 1);
-                ret = bf_mul_2exp(r, e, prec, flags);
-            } else if (prec == BF_PREC_INF) {
-                slimb_t y1;
-                /* specific case for infinite precision (integer case) */
-                bf_get_limb(&y1, y, 0);
-                assert(!y->sign);
-                /* x must be an integer, so abs(x) >= 2 */
-                if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) {
-                    bf_delete(T);
-                    return bf_set_overflow(r, 0, BF_PREC_INF, flags);
-                }
-                ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ);
-            } else {
-                if (y->expn <= 31) {
-                    /* small enough power: use exponentiation in all cases */
-                } else if (y->sign) {
-                    /* cannot be exact */
-                    goto general_case;
-                } else {
-                    if (rnd_mode == BF_RNDF)
-                        goto general_case; /* no need to track exact results */
-                    /* see if the result has a chance to be exact:
-                       if x=a*2^b (a odd), x^y=a^y*2^(b*y)
-                       x^y needs a precision of at least floor_log2(a)*y bits
-                    */
-                    bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ);
-                    bf_get_limb(&e, r, 0);
-                    if (prec < e)
-                        goto general_case;
-                }
-                ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y);
-            }
-        } else {
-            if (rnd_mode != BF_RNDF) {
-                bf_t *y1;
-                if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) {
-                    /* the problem is reduced to a power to an integer */
-#if 0
-                    printf("\nn=%" PRId64 "\n", -(int64_t)y_emin);
-                    bf_print_str("T", T);
-                    bf_print_str("r", r);
-#endif
-                    bf_set(T, r);
-                    y1 = &ytmp_s;
-                    y1->tab = y->tab;
-                    y1->len = y->len;
-                    y1->sign = y->sign;
-                    y1->expn = y->expn - y_emin;
-                    y = y1;
-                    goto int_pow;
-                }
-            }
-        general_case:
-            ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y);
-        }
-    }
- done:
-    bf_delete(T);
-    r->sign = r_sign;
-    return ret;
-}
-
-/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */
-static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    bf_init(s, T);
-    bf_set(T, x);
-    bf_mul(r, T, T, prec1, BF_RNDN);
-    bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ);
-    bf_add(T, T, r, prec1, BF_RNDN);
-    bf_neg(T);
-    bf_sqrt(r, T, prec1, BF_RNDF);
-    bf_delete(T);
-}
-
-static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)
-{
-    bf_context_t *s1 = a->ctx;
-    bf_t T_s, *T = &T_s;
-    bf_t U_s, *U = &U_s;
-    bf_t r_s, *r = &r_s;
-    slimb_t K, prec1, i, l, mod, prec2;
-    int is_neg;
-
-    assert(c != a && s != a);
-
-    bf_init(s1, T);
-    bf_init(s1, U);
-    bf_init(s1, r);
-
-    /* XXX: precision analysis */
-    K = bf_isqrt(prec / 2);
-    l = prec / (2 * K) + 1;
-    prec1 = prec + 2 * K + l + 8;
-
-    /* after the modulo reduction, -pi/4 <= T <= pi/4 */
-    if (a->expn <= -1) {
-        /* abs(a) <= 0.25: no modulo reduction needed */
-        bf_set(T, a);
-        mod = 0;
-    } else {
-        slimb_t cancel;
-        cancel = 0;
-        for(;;) {
-            prec2 = prec1 + a->expn + cancel;
-            bf_const_pi(U, prec2, BF_RNDF);
-            bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ);
-            bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN);
-            //            printf("T.expn=%ld prec2=%ld\n", T->expn, prec2);
-            if (mod == 0 || (T->expn != BF_EXP_ZERO &&
-                             (T->expn + prec2) >= (prec1 - 1)))
-                break;
-            /* increase the number of bits until the precision is good enough */
-            cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2);
-        }
-        mod &= 3;
-    }
-
-    is_neg = T->sign;
-
-    /* compute cosm1(x) = cos(x) - 1 */
-    bf_mul(T, T, T, prec1, BF_RNDN);
-    bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ);
-
-    /* Taylor expansion:
-       -x^2/2 + x^4/4! - x^6/6! + ...
-    */
-    bf_set_ui(r, 1);
-    for(i = l ; i >= 1; i--) {
-        bf_set_ui(U, 2 * i - 1);
-        bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ);
-        bf_div(U, T, U, prec1, BF_RNDN);
-        bf_mul(r, r, U, prec1, BF_RNDN);
-        bf_neg(r);
-        if (i != 1)
-            bf_add_si(r, r, 1, prec1, BF_RNDN);
-    }
-    bf_delete(U);
-
-    /* undo argument reduction:
-       cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2)
-    */
-    for(i = 0; i < K; i++) {
-        bf_mul(T, r, r, prec1, BF_RNDN);
-        bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);
-        bf_add(r, r, T, prec1, BF_RNDN);
-        bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);
-    }
-    bf_delete(T);
-
-    if (c) {
-        if ((mod & 1) == 0) {
-            bf_add_si(c, r, 1, prec1, BF_RNDN);
-        } else {
-            bf_sqrt_sin(c, r, prec1);
-            c->sign = is_neg ^ 1;
-        }
-        c->sign ^= mod >> 1;
-    }
-    if (s) {
-        if ((mod & 1) == 0) {
-            bf_sqrt_sin(s, r, prec1);
-            s->sign = is_neg;
-        } else {
-            bf_add_si(s, r, 1, prec1, BF_RNDN);
-        }
-        s->sign ^= mod >> 1;
-    }
-    bf_delete(r);
-    return BF_ST_INEXACT;
-}
-
-static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    return bf_sincos(NULL, r, a, prec);
-}
-
-int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set_ui(r, 1);
-            return 0;
-        }
-    }
-
-    /* small argument case: result = 1+r(x) with r(x) = -x^2/2 +
-       O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */
-    if (a->expn < 0) {
-        slimb_t e;
-        e = 2 * a->expn - 1;
-        if (e < -(prec + 2)) {
-            bf_set_ui(r, 1);
-            return bf_add_epsilon(r, r, e, 1, prec, flags);
-        }
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL);
-}
-
-static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    return bf_sincos(r, NULL, a, prec);
-}
-
-int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set_zero(r, a->sign);
-            return 0;
-        }
-    }
-
-    /* small argument case: result = x+r(x) with r(x) = -x^3/6 +
-       O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */
-    if (a->expn < 0) {
-        slimb_t e;
-        e = sat_add(2 * a->expn, a->expn - 2);
-        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
-            bf_set(r, a);
-            return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);
-        }
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL);
-}
-
-static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    limb_t prec1;
-
-    /* XXX: precision analysis */
-    prec1 = prec + 8;
-    bf_init(s, T);
-    bf_sincos(r, T, a, prec1);
-    bf_div(r, r, T, prec1, BF_RNDF);
-    bf_delete(T);
-    return BF_ST_INEXACT;
-}
-
-int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    assert(r != a);
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set_zero(r, a->sign);
-            return 0;
-        }
-    }
-
-    /* small argument case: result = x+r(x) with r(x) = x^3/3 +
-       O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */
-    if (a->expn < 0) {
-        slimb_t e;
-        e = sat_add(2 * a->expn, a->expn - 1);
-        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
-            bf_set(r, a);
-            return bf_add_epsilon(r, r, e, a->sign, prec, flags);
-        }
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL);
-}
-
-/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to
-   avoid cancellation) */
-static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec,
-                            void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    BOOL add_pi2 = (BOOL)(intptr_t)opaque;
-    bf_t T_s, *T = &T_s;
-    bf_t U_s, *U = &U_s;
-    bf_t V_s, *V = &V_s;
-    bf_t X2_s, *X2 = &X2_s;
-    int cmp_1;
-    slimb_t prec1, i, K, l;
-
-    /* XXX: precision analysis */
-    K = bf_isqrt((prec + 1) / 2);
-    l = prec / (2 * K) + 1;
-    prec1 = prec + K + 2 * l + 32;
-    //    printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1);
-
-    bf_init(s, T);
-    cmp_1 = (a->expn >= 1); /* a >= 1 */
-    if (cmp_1) {
-        bf_set_ui(T, 1);
-        bf_div(T, T, a, prec1, BF_RNDN);
-    } else {
-        bf_set(T, a);
-    }
-
-    /* abs(T) <= 1 */
-
-    /* argument reduction */
-
-    bf_init(s, U);
-    bf_init(s, V);
-    bf_init(s, X2);
-    for(i = 0; i < K; i++) {
-        /* T = T / (1 + sqrt(1 + T^2)) */
-        bf_mul(U, T, T, prec1, BF_RNDN);
-        bf_add_si(U, U, 1, prec1, BF_RNDN);
-        bf_sqrt(V, U, prec1, BF_RNDN);
-        bf_add_si(V, V, 1, prec1, BF_RNDN);
-        bf_div(T, T, V, prec1, BF_RNDN);
-    }
-
-    /* Taylor series:
-       x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1)
-    */
-    bf_mul(X2, T, T, prec1, BF_RNDN);
-    bf_set_ui(r, 0);
-    for(i = l; i >= 1; i--) {
-        bf_set_si(U, 1);
-        bf_set_ui(V, 2 * i + 1);
-        bf_div(U, U, V, prec1, BF_RNDN);
-        bf_neg(r);
-        bf_add(r, r, U, prec1, BF_RNDN);
-        bf_mul(r, r, X2, prec1, BF_RNDN);
-    }
-    bf_neg(r);
-    bf_add_si(r, r, 1, prec1, BF_RNDN);
-    bf_mul(r, r, T, prec1, BF_RNDN);
-
-    /* undo the argument reduction */
-    bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ);
-
-    bf_delete(U);
-    bf_delete(V);
-    bf_delete(X2);
-
-    i = add_pi2;
-    if (cmp_1 > 0) {
-        /* undo the inversion : r = sign(a)*PI/2 - r */
-        bf_neg(r);
-        i += 1 - 2 * a->sign;
-    }
-    /* add i*(pi/2) with -1 <= i <= 2 */
-    if (i != 0) {
-        bf_const_pi(T, prec1, BF_RNDF);
-        if (i != 2)
-            bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ);
-        T->sign = (i < 0);
-        bf_add(r, T, r, prec1, BF_RNDN);
-    }
-
-    bf_delete(T);
-    return BF_ST_INEXACT;
-}
-
-int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    int res;
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF)  {
-            /* -PI/2 or PI/2 */
-            bf_const_pi_signed(r, a->sign, prec, flags);
-            bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);
-            return BF_ST_INEXACT;
-        } else {
-            bf_set_zero(r, a->sign);
-            return 0;
-        }
-    }
-
-    bf_init(s, T);
-    bf_set_ui(T, 1);
-    res = bf_cmpu(a, T);
-    bf_delete(T);
-    if (res == 0) {
-        /* short cut: abs(a) == 1 -> +/-pi/4 */
-        bf_const_pi_signed(r, a->sign, prec, flags);
-        bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ);
-        return BF_ST_INEXACT;
-    }
-
-    /* small argument case: result = x+r(x) with r(x) = -x^3/3 +
-       O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */
-    if (a->expn < 0) {
-        slimb_t e;
-        e = sat_add(2 * a->expn, a->expn - 1);
-        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
-            bf_set(r, a);
-            return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);
-        }
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE);
-}
-
-static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    const bf_t *x = opaque;
-    bf_t T_s, *T = &T_s;
-    limb_t prec1;
-    int ret;
-
-    if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) {
-        bf_set_nan(r);
-        return 0;
-    }
-
-    /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */
-    bf_init(s, T);
-    prec1 = prec + 32;
-    if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) {
-        bf_set_ui(T, 1);
-        T->sign = y->sign ^ x->sign;
-    } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) {
-        bf_set_zero(T, y->sign ^ x->sign);
-    } else {
-        bf_div(T, y, x, prec1, BF_RNDF);
-    }
-    ret = bf_atan(r, T, prec1, BF_RNDF);
-
-    if (x->sign) {
-        /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */
-        bf_const_pi(T, prec1, BF_RNDF);
-        T->sign = y->sign;
-        bf_add(r, r, T, prec1, BF_RNDN);
-        ret |= BF_ST_INEXACT;
-    }
-
-    bf_delete(T);
-    return ret;
-}
-
-int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
-             limb_t prec, bf_flags_t flags)
-{
-    return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x);
-}
-
-static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
-    bf_context_t *s = r->ctx;
-    BOOL is_acos = (BOOL)(intptr_t)opaque;
-    bf_t T_s, *T = &T_s;
-    limb_t prec1, prec2;
-
-    /* asin(x) = atan(x/sqrt(1-x^2))
-       acos(x) = pi/2 - asin(x) */
-    prec1 = prec + 8;
-    /* increase the precision in x^2 to compensate the cancellation in
-       (1-x^2) if x is close to 1 */
-    /* XXX: use less precision when possible */
-    if (a->expn >= 0)
-        prec2 = BF_PREC_INF;
-    else
-        prec2 = prec1;
-    bf_init(s, T);
-    bf_mul(T, a, a, prec2, BF_RNDN);
-    bf_neg(T);
-    bf_add_si(T, T, 1, prec2, BF_RNDN);
-
-    bf_sqrt(r, T, prec1, BF_RNDN);
-    bf_div(T, a, r, prec1, BF_RNDN);
-    if (is_acos)
-        bf_neg(T);
-    bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos);
-    bf_delete(T);
-    return BF_ST_INEXACT;
-}
-
-int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    int res;
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_set_zero(r, a->sign);
-            return 0;
-        }
-    }
-    bf_init(s, T);
-    bf_set_ui(T, 1);
-    res = bf_cmpu(a, T);
-    bf_delete(T);
-    if (res > 0) {
-        bf_set_nan(r);
-        return BF_ST_INVALID_OP;
-    }
-
-    /* small argument case: result = x+r(x) with r(x) = x^3/6 +
-       O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */
-    if (a->expn < 0) {
-        slimb_t e;
-        e = sat_add(2 * a->expn, a->expn - 2);
-        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
-            bf_set(r, a);
-            return bf_add_epsilon(r, r, e, a->sign, prec, flags);
-        }
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE);
-}
-
-int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = r->ctx;
-    bf_t T_s, *T = &T_s;
-    int res;
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            bf_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bf_const_pi(r, prec, flags);
-            bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);
-            return BF_ST_INEXACT;
-        }
-    }
-    bf_init(s, T);
-    bf_set_ui(T, 1);
-    res = bf_cmpu(a, T);
-    bf_delete(T);
-    if (res > 0) {
-        bf_set_nan(r);
-        return BF_ST_INVALID_OP;
-    } else if (res == 0 && a->sign == 0) {
-        bf_set_zero(r, 0);
-        return 0;
-    }
-
-    return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE);
-}
-
-/***************************************************************/
-/* decimal floating point numbers */
-
-#ifdef USE_BF_DEC
-
-#define adddq(r1, r0, a1, a0)                   \
-    do {                                        \
-        limb_t __t = r0;                        \
-        r0 += (a0);                             \
-        r1 += (a1) + (r0 < __t);                \
-    } while (0)
-
-#define subdq(r1, r0, a1, a0)                   \
-    do {                                        \
-        limb_t __t = r0;                        \
-        r0 -= (a0);                             \
-        r1 -= (a1) + (r0 > __t);                \
-    } while (0)
-
-#if LIMB_BITS == 64
-
-/* Note: we assume __int128 is available */
-#define muldq(r1, r0, a, b)                     \
-    do {                                        \
-        unsigned __int128 __t;                          \
-        __t = (unsigned __int128)(a) * (unsigned __int128)(b);  \
-        r0 = __t;                               \
-        r1 = __t >> 64;                         \
-    } while (0)
-
-#define divdq(q, r, a1, a0, b)                  \
-    do {                                        \
-        unsigned __int128 __t;                  \
-        limb_t __b = (b);                       \
-        __t = ((unsigned __int128)(a1) << 64) | (a0);   \
-        q = __t / __b;                                  \
-        r = __t % __b;                                  \
-    } while (0)
-
-#else
-
-#define muldq(r1, r0, a, b)                     \
-    do {                                        \
-        uint64_t __t;                          \
-        __t = (uint64_t)(a) * (uint64_t)(b);  \
-        r0 = __t;                               \
-        r1 = __t >> 32;                         \
-    } while (0)
-
-#define divdq(q, r, a1, a0, b)                  \
-    do {                                        \
-        uint64_t __t;                  \
-        limb_t __b = (b);                       \
-        __t = ((uint64_t)(a1) << 32) | (a0);   \
-        q = __t / __b;                                  \
-        r = __t % __b;                                  \
-    } while (0)
-
-#endif /* LIMB_BITS != 64 */
-
-#if LIMB_DIGITS == 19
-
-/* WARNING: hardcoded for b = 1e19. It is assumed that:
-   0 <= a1 < 2^63 */
-#define divdq_base(q, r, a1, a0)\
-do {\
-    uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \
-    __a0 = a0;\
-    __a1 = a1;\
-    __t0 = __a1;\
-    __t0 = shld(__t0, __a0, 1);\
-    muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \
-    muldq(__t1, __t0, q, __b);\
-    subdq(__a1, __a0, __t1, __t0);\
-    subdq(__a1, __a0, 1, __b * 2);    \
-    __t0 = (slimb_t)__a1 >> 1; \
-    q += 2 + __t0;\
-    adddq(__a1, __a0, 0, __b & __t0);\
-    q += __a1;                  \
-    __a0 += __b & __a1;           \
-    r = __a0;\
-} while(0)
-
-#elif LIMB_DIGITS == 9
-
-/* WARNING: hardcoded for b = 1e9. It is assumed that:
-   0 <= a1 < 2^29 */
-#define divdq_base(q, r, a1, a0)\
-do {\
-    uint32_t __t0, __t1, __b = BF_DEC_BASE; \
-    __t0 = a1;\
-    __t1 = a0;\
-    __t0 = (__t0 << 3) | (__t1 >> (32 - 3));    \
-    muldq(q, __t1, __t0, 2305843009U);\
-    r = a0 - q * __b;\
-    __t1 = (r >= __b);\
-    q += __t1;\
-    if (__t1)\
-        r -= __b;\
-} while(0)
-
-#endif
-
-/* fast integer division by a fixed constant */
-
-typedef struct FastDivData {
-    limb_t m1; /* multiplier */
-    int8_t shift1;
-    int8_t shift2;
-} FastDivData;
-
-/* From "Division by Invariant Integers using Multiplication" by
-   Torborn Granlund and Peter L. Montgomery */
-/* d must be != 0 */
-static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d)
-{
-    int l;
-    limb_t q, r, m1;
-    if (d == 1)
-        l = 0;
-    else
-        l = 64 - clz64(d - 1);
-    divdq(q, r, ((limb_t)1 << l) - d, 0, d);
-    (void)r;
-    m1 = q + 1;
-    //    printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1);
-    s->m1 = m1;
-    s->shift1 = l;
-    if (s->shift1 > 1)
-        s->shift1 = 1;
-    s->shift2 = l - 1;
-    if (s->shift2 < 0)
-        s->shift2 = 0;
-}
-
-static inline limb_t fast_udiv(limb_t a, const FastDivData *s)
-{
-    limb_t t0, t1;
-    muldq(t1, t0, s->m1, a);
-    t0 = (a - t1) >> s->shift1;
-    return (t1 + t0) >> s->shift2;
-}
-
-/* contains 10^i */
-const limb_t mp_pow_dec[LIMB_DIGITS + 1] = {
-    1U,
-    10U,
-    100U,
-    1000U,
-    10000U,
-    100000U,
-    1000000U,
-    10000000U,
-    100000000U,
-    1000000000U,
-#if LIMB_BITS == 64
-    10000000000U,
-    100000000000U,
-    1000000000000U,
-    10000000000000U,
-    100000000000000U,
-    1000000000000000U,
-    10000000000000000U,
-    100000000000000000U,
-    1000000000000000000U,
-    10000000000000000000U,
-#endif
-};
-
-/* precomputed from fast_udiv_init(10^i) */
-static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = {
-#if LIMB_BITS == 32
-    { 0x00000001, 0, 0 },
-    { 0x9999999a, 1, 3 },
-    { 0x47ae147b, 1, 6 },
-    { 0x0624dd30, 1, 9 },
-    { 0xa36e2eb2, 1, 13 },
-    { 0x4f8b588f, 1, 16 },
-    { 0x0c6f7a0c, 1, 19 },
-    { 0xad7f29ac, 1, 23 },
-    { 0x5798ee24, 1, 26 },
-    { 0x12e0be83, 1, 29 },
-#else
-    { 0x0000000000000001, 0, 0 },
-    { 0x999999999999999a, 1, 3 },
-    { 0x47ae147ae147ae15, 1, 6 },
-    { 0x0624dd2f1a9fbe77, 1, 9 },
-    { 0xa36e2eb1c432ca58, 1, 13 },
-    { 0x4f8b588e368f0847, 1, 16 },
-    { 0x0c6f7a0b5ed8d36c, 1, 19 },
-    { 0xad7f29abcaf48579, 1, 23 },
-    { 0x5798ee2308c39dfa, 1, 26 },
-    { 0x12e0be826d694b2f, 1, 29 },
-    { 0xb7cdfd9d7bdbab7e, 1, 33 },
-    { 0x5fd7fe17964955fe, 1, 36 },
-    { 0x19799812dea11198, 1, 39 },
-    { 0xc25c268497681c27, 1, 43 },
-    { 0x6849b86a12b9b01f, 1, 46 },
-    { 0x203af9ee756159b3, 1, 49 },
-    { 0xcd2b297d889bc2b7, 1, 53 },
-    { 0x70ef54646d496893, 1, 56 },
-    { 0x2725dd1d243aba0f, 1, 59 },
-    { 0xd83c94fb6d2ac34d, 1, 63 },
-#endif
-};
-
-/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */
-static inline limb_t fast_shr_dec(limb_t a, int shift)
-{
-    return fast_udiv(a, &mp_pow_div[shift]);
-}
-
-/* division and remainder by 10^shift */
-#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift]
-
-limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2,
-                  mp_size_t n, limb_t carry)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t k, a, v;
-
-    k=carry;
-    for(i=0;i<n;i++) {
-        /* XXX: reuse the trick in add_mod */
-        v = op1[i];
-        a = v + op2[i] + k - base;
-        k = a <= v;
-        if (!k)
-            a += base;
-        res[i]=a;
-    }
-    return k;
-}
-
-limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t k, a, v;
-
-    k=b;
-    for(i=0;i<n;i++) {
-        v = tab[i];
-        a = v + k - base;
-        k = a <= v;
-        if (!k)
-            a += base;
-        tab[i] = a;
-        if (k == 0)
-            break;
-    }
-    return k;
-}
-
-limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2,
-                  mp_size_t n, limb_t carry)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t k, v, a;
-
-    k=carry;
-    for(i=0;i<n;i++) {
-        v = op1[i];
-        a = v - op2[i] - k;
-        k = a > v;
-        if (k)
-            a += base;
-        res[i] = a;
-    }
-    return k;
-}
-
-limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t k, v, a;
-
-    k=b;
-    for(i=0;i<n;i++) {
-        v = tab[i];
-        a = v - k;
-        k = a > v;
-        if (k)
-            a += base;
-        tab[i]=a;
-        if (k == 0)
-            break;
-    }
-    return k;
-}
-
-/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */
-limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
-                   limb_t b, limb_t l)
-{
-    mp_size_t i;
-    limb_t t0, t1, r;
-
-    for(i = 0; i < n; i++) {
-        muldq(t1, t0, taba[i], b);
-        adddq(t1, t0, 0, l);
-        divdq_base(l, r, t1, t0);
-        tabr[i] = r;
-    }
-    return l;
-}
-
-/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add
-   to the high word */
-limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
-                       limb_t b)
-{
-    mp_size_t i;
-    limb_t l, t0, t1, r;
-
-    l = 0;
-    for(i = 0; i < n; i++) {
-        muldq(t1, t0, taba[i], b);
-        adddq(t1, t0, 0, l);
-        adddq(t1, t0, 0, tabr[i]);
-        divdq_base(l, r, t1, t0);
-        tabr[i] = r;
-    }
-    return l;
-}
-
-/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to
-   substract to the high word. */
-limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
-                       limb_t b)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t l, t0, t1, r, a, v, c;
-
-    /* XXX: optimize */
-    l = 0;
-    for(i = 0; i < n; i++) {
-        muldq(t1, t0, taba[i], b);
-        adddq(t1, t0, 0, l);
-        divdq_base(l, r, t1, t0);
-        v = tabr[i];
-        a = v - r;
-        c = a > v;
-        if (c)
-            a += base;
-        /* never bigger than base because r = 0 when l = base - 1 */
-        l += c;
-        tabr[i] = a;
-    }
-    return l;
-}
-
-/* size of the result : op1_size + op2_size. */
-void mp_mul_basecase_dec(limb_t *result,
-                         const limb_t *op1, mp_size_t op1_size,
-                         const limb_t *op2, mp_size_t op2_size)
-{
-    mp_size_t i;
-    limb_t r;
-
-    result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0);
-
-    for(i=1;i<op2_size;i++) {
-        r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]);
-        result[i + op1_size] = r;
-    }
-}
-
-/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r <
-   b. Return the remainder. */
-limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na,
-                   limb_t b, limb_t r)
-{
-    limb_t base = BF_DEC_BASE;
-    mp_size_t i;
-    limb_t t0, t1, q;
-    int shift;
-
-#if (BF_DEC_BASE % 2) == 0
-    if (b == 2) {
-        limb_t base_div2;
-        /* Note: only works if base is even */
-        base_div2 = base >> 1;
-        if (r)
-            r = base_div2;
-        for(i = na - 1; i >= 0; i--) {
-            t0 = taba[i];
-            tabr[i] = (t0 >> 1) + r;
-            r = 0;
-            if (t0 & 1)
-                r = base_div2;
-        }
-        if (r)
-            r = 1;
-    } else
-#endif
-    if (na >= UDIV1NORM_THRESHOLD) {
-        shift = clz(b);
-        if (shift == 0) {
-            /* normalized case: b >= 2^(LIMB_BITS-1) */
-            limb_t b_inv;
-            b_inv = udiv1norm_init(b);
-            for(i = na - 1; i >= 0; i--) {
-                muldq(t1, t0, r, base);
-                adddq(t1, t0, 0, taba[i]);
-                q = udiv1norm(&r, t1, t0, b, b_inv);
-                tabr[i] = q;
-            }
-        } else {
-            limb_t b_inv;
-            b <<= shift;
-            b_inv = udiv1norm_init(b);
-            for(i = na - 1; i >= 0; i--) {
-                muldq(t1, t0, r, base);
-                adddq(t1, t0, 0, taba[i]);
-                t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift));
-                t0 <<= shift;
-                q = udiv1norm(&r, t1, t0, b, b_inv);
-                r >>= shift;
-                tabr[i] = q;
-            }
-        }
-    } else {
-        for(i = na - 1; i >= 0; i--) {
-            muldq(t1, t0, r, base);
-            adddq(t1, t0, 0, taba[i]);
-            divdq(q, r, t1, t0, b);
-            tabr[i] = q;
-        }
-    }
-    return r;
-}
-
-static __maybe_unused void mp_print_str_dec(const char *str,
-                                       const limb_t *tab, slimb_t n)
-{
-    slimb_t i;
-    printf("%s=", str);
-    for(i = n - 1; i >= 0; i--) {
-        if (i != n - 1)
-            printf("_");
-        printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);
-    }
-    printf("\n");
-}
-
-static __maybe_unused void mp_print_str_h_dec(const char *str,
-                                              const limb_t *tab, slimb_t n,
-                                              limb_t high)
-{
-    slimb_t i;
-    printf("%s=", str);
-    printf("%0*" PRIu_LIMB, LIMB_DIGITS, high);
-    for(i = n - 1; i >= 0; i--) {
-        printf("_");
-        printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);
-    }
-    printf("\n");
-}
-
-//#define DEBUG_DIV_SLOW
-
-#define DIV_STATIC_ALLOC_LEN 16
-
-/* return q = a / b and r = a % b.
-
-   taba[na] must be allocated if tabb1[nb - 1] < B / 2.  tabb1[nb - 1]
-   must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1]
-   >= B / 2.
-
-   The remainder is is returned in taba and contains nb libms. tabq
-   contains na - nb + 1 limbs. No overlap is permitted.
-
-   Running time of the standard method: (na - nb + 1) * nb
-   Return 0 if OK, -1 if memory alloc error
-*/
-/* XXX: optimize */
-static int mp_div_dec(bf_context_t *s, limb_t *tabq,
-                      limb_t *taba, mp_size_t na,
-                      const limb_t *tabb1, mp_size_t nb)
-{
-    limb_t base = BF_DEC_BASE;
-    limb_t r, mult, t0, t1, a, c, q, v, *tabb;
-    mp_size_t i, j;
-    limb_t static_tabb[DIV_STATIC_ALLOC_LEN];
-
-#ifdef DEBUG_DIV_SLOW
-    mp_print_str_dec("a", taba, na);
-    mp_print_str_dec("b", tabb1, nb);
-#endif
-
-    /* normalize tabb */
-    r = tabb1[nb - 1];
-    assert(r != 0);
-    i = na - nb;
-    if (r >= BF_DEC_BASE / 2) {
-        mult = 1;
-        tabb = (limb_t *)tabb1;
-        q = 1;
-        for(j = nb - 1; j >= 0; j--) {
-            if (taba[i + j] != tabb[j]) {
-                if (taba[i + j] < tabb[j])
-                    q = 0;
-                break;
-            }
-        }
-        tabq[i] = q;
-        if (q) {
-            mp_sub_dec(taba + i, taba + i, tabb, nb, 0);
-        }
-        i--;
-    } else {
-        mult = base / (r + 1);
-        if (likely(nb <= DIV_STATIC_ALLOC_LEN)) {
-            tabb = static_tabb;
-        } else {
-            tabb = bf_malloc(s, sizeof(limb_t) * nb);
-            if (!tabb)
-                return -1;
-        }
-        mp_mul1_dec(tabb, tabb1, nb, mult, 0);
-        taba[na] = mp_mul1_dec(taba, taba, na, mult, 0);
-    }
-
-#ifdef DEBUG_DIV_SLOW
-    printf("mult=" FMT_LIMB "\n", mult);
-    mp_print_str_dec("a_norm", taba, na + 1);
-    mp_print_str_dec("b_norm", tabb, nb);
-#endif
-
-    for(; i >= 0; i--) {
-        if (unlikely(taba[i + nb] >= tabb[nb - 1])) {
-            /* XXX: check if it is really possible */
-            q = base - 1;
-        } else {
-            muldq(t1, t0, taba[i + nb], base);
-            adddq(t1, t0, 0, taba[i + nb - 1]);
-            divdq(q, r, t1, t0, tabb[nb - 1]);
-        }
-        //        printf("i=%d q1=%ld\n", i, q);
-
-        r = mp_sub_mul1_dec(taba + i, tabb, nb, q);
-        //        mp_dump("r1", taba + i, nb, bd);
-        //        printf("r2=%ld\n", r);
-
-        v = taba[i + nb];
-        a = v - r;
-        c = a > v;
-        if (c)
-            a += base;
-        taba[i + nb] = a;
-
-        if (c != 0) {
-            /* negative result */
-            for(;;) {
-                q--;
-                c = mp_add_dec(taba + i, taba + i, tabb, nb, 0);
-                /* propagate carry and test if positive result */
-                if (c != 0) {
-                    if (++taba[i + nb] == base) {
-                        break;
-                    }
-                }
-            }
-        }
-        tabq[i] = q;
-    }
-
-#ifdef DEBUG_DIV_SLOW
-    mp_print_str_dec("q", tabq, na - nb + 1);
-    mp_print_str_dec("r", taba, nb);
-#endif
-
-    /* remove the normalization */
-    if (mult != 1) {
-        mp_div1_dec(taba, taba, nb, mult, 0);
-        if (unlikely(tabb != static_tabb))
-            bf_free(s, tabb);
-    }
-    return 0;
-}
-
-/* divide by 10^shift */
-static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
-                         limb_t shift, limb_t high)
-{
-    mp_size_t i;
-    limb_t l, a, q, r;
-
-    assert(shift >= 1 && shift < LIMB_DIGITS);
-    l = high;
-    for(i = n - 1; i >= 0; i--) {
-        a = tab[i];
-        fast_shr_rem_dec(q, r, a, shift);
-        tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift];
-        l = r;
-    }
-    return l;
-}
-
-/* multiply by 10^shift */
-static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
-                         limb_t shift, limb_t low)
-{
-    mp_size_t i;
-    limb_t l, a, q, r;
-
-    assert(shift >= 1 && shift < LIMB_DIGITS);
-    l = low;
-    for(i = 0; i < n; i++) {
-        a = tab[i];
-        fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift);
-        tab_r[i] = r * mp_pow_dec[shift] + l;
-        l = q;
-    }
-    return l;
-}
-
-static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba)
-{
-    int k;
-    dlimb_t a, b, r;
-    limb_t taba1[2], s, r0, r1;
-
-    /* convert to binary and normalize */
-    a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0];
-    k = clz(a >> LIMB_BITS) & ~1;
-    b = a << k;
-    taba1[0] = b;
-    taba1[1] = b >> LIMB_BITS;
-    mp_sqrtrem2(&s, taba1);
-    s >>= (k >> 1);
-    /* convert the remainder back to decimal */
-    r = a - (dlimb_t)s * (dlimb_t)s;
-    divdq_base(r1, r0, r >> LIMB_BITS, r);
-    taba[0] = r0;
-    tabs[0] = s;
-    return r1;
-}
-
-//#define DEBUG_SQRTREM_DEC
-
-/* tmp_buf must contain (n / 2 + 1 limbs) */
-static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n,
-                                 limb_t *tmp_buf)
-{
-    limb_t l, h, rh, ql, qh, c, i;
-
-    if (n == 1)
-        return mp_sqrtrem2_dec(tabs, taba);
-#ifdef DEBUG_SQRTREM_DEC
-    mp_print_str_dec("a", taba, 2 * n);
-#endif
-    l = n / 2;
-    h = n - l;
-    qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf);
-#ifdef DEBUG_SQRTREM_DEC
-    mp_print_str_dec("s1", tabs + l, h);
-    mp_print_str_h_dec("r1", taba + 2 * l, h, qh);
-    mp_print_str_h_dec("r2", taba + l, n, qh);
-#endif
-
-    /* the remainder is in taba + 2 * l. Its high bit is in qh */
-    if (qh) {
-        mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);
-    }
-    /* instead of dividing by 2*s, divide by s (which is normalized)
-       and update q and r */
-    mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h);
-    qh += tmp_buf[l];
-    for(i = 0; i < l; i++)
-        tabs[i] = tmp_buf[i];
-    ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1);
-    qh = qh >> 1; /* 0 or 1 */
-    if (ql)
-        rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0);
-    else
-        rh = 0;
-#ifdef DEBUG_SQRTREM_DEC
-    mp_print_str_h_dec("q", tabs, l, qh);
-    mp_print_str_h_dec("u", taba + l, h, rh);
-#endif
-
-    mp_add_ui_dec(tabs + l, qh, h);
-#ifdef DEBUG_SQRTREM_DEC
-    mp_print_str_dec("s2", tabs, n);
-#endif
-
-    /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */
-    /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */
-    if (qh) {
-        c = qh;
-    } else {
-        mp_mul_basecase_dec(taba + n, tabs, l, tabs, l);
-        c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0);
-    }
-    rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l);
-    if ((slimb_t)rh < 0) {
-        mp_sub_ui_dec(tabs, 1, n);
-        rh += mp_add_mul1_dec(taba, tabs, n, 2);
-        rh += mp_add_ui_dec(taba, 1, n);
-    }
-    return rh;
-}
-
-/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s,
-   r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n
-   limbs. r is returned in the lower n limbs of taba. Its r[n] is the
-   returned value of the function. */
-int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)
-{
-    limb_t tmp_buf1[8];
-    limb_t *tmp_buf;
-    mp_size_t n2;
-    n2 = n / 2 + 1;
-    if (n2 <= countof(tmp_buf1)) {
-        tmp_buf = tmp_buf1;
-    } else {
-        tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);
-        if (!tmp_buf)
-            return -1;
-    }
-    taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf);
-    if (tmp_buf != tmp_buf1)
-        bf_free(s, tmp_buf);
-    return 0;
-}
-
-/* return the number of leading zero digits, from 0 to LIMB_DIGITS */
-static int clz_dec(limb_t a)
-{
-    if (a == 0)
-        return LIMB_DIGITS;
-    switch(LIMB_BITS - 1 - clz(a)) {
-    case 0: /* 1-1 */
-        return LIMB_DIGITS - 1;
-    case 1: /* 2-3 */
-        return LIMB_DIGITS - 1;
-    case 2: /* 4-7 */
-        return LIMB_DIGITS - 1;
-    case 3: /* 8-15 */
-        if (a < 10)
-            return LIMB_DIGITS - 1;
-        else
-            return LIMB_DIGITS - 2;
-    case 4: /* 16-31 */
-        return LIMB_DIGITS - 2;
-    case 5: /* 32-63 */
-        return LIMB_DIGITS - 2;
-    case 6: /* 64-127 */
-        if (a < 100)
-            return LIMB_DIGITS - 2;
-        else
-            return LIMB_DIGITS - 3;
-    case 7: /* 128-255 */
-        return LIMB_DIGITS - 3;
-    case 8: /* 256-511 */
-        return LIMB_DIGITS - 3;
-    case 9: /* 512-1023 */
-        if (a < 1000)
-            return LIMB_DIGITS - 3;
-        else
-            return LIMB_DIGITS - 4;
-    case 10: /* 1024-2047 */
-        return LIMB_DIGITS - 4;
-    case 11: /* 2048-4095 */
-        return LIMB_DIGITS - 4;
-    case 12: /* 4096-8191 */
-        return LIMB_DIGITS - 4;
-    case 13: /* 8192-16383 */
-        if (a < 10000)
-            return LIMB_DIGITS - 4;
-        else
-            return LIMB_DIGITS - 5;
-    case 14: /* 16384-32767 */
-        return LIMB_DIGITS - 5;
-    case 15: /* 32768-65535 */
-        return LIMB_DIGITS - 5;
-    case 16: /* 65536-131071 */
-        if (a < 100000)
-            return LIMB_DIGITS - 5;
-        else
-            return LIMB_DIGITS - 6;
-    case 17: /* 131072-262143 */
-        return LIMB_DIGITS - 6;
-    case 18: /* 262144-524287 */
-        return LIMB_DIGITS - 6;
-    case 19: /* 524288-1048575 */
-        if (a < 1000000)
-            return LIMB_DIGITS - 6;
-        else
-            return LIMB_DIGITS - 7;
-    case 20: /* 1048576-2097151 */
-        return LIMB_DIGITS - 7;
-    case 21: /* 2097152-4194303 */
-        return LIMB_DIGITS - 7;
-    case 22: /* 4194304-8388607 */
-        return LIMB_DIGITS - 7;
-    case 23: /* 8388608-16777215 */
-        if (a < 10000000)
-            return LIMB_DIGITS - 7;
-        else
-            return LIMB_DIGITS - 8;
-    case 24: /* 16777216-33554431 */
-        return LIMB_DIGITS - 8;
-    case 25: /* 33554432-67108863 */
-        return LIMB_DIGITS - 8;
-    case 26: /* 67108864-134217727 */
-        if (a < 100000000)
-            return LIMB_DIGITS - 8;
-        else
-            return LIMB_DIGITS - 9;
-#if LIMB_BITS == 64
-    case 27: /* 134217728-268435455 */
-        return LIMB_DIGITS - 9;
-    case 28: /* 268435456-536870911 */
-        return LIMB_DIGITS - 9;
-    case 29: /* 536870912-1073741823 */
-        if (a < 1000000000)
-            return LIMB_DIGITS - 9;
-        else
-            return LIMB_DIGITS - 10;
-    case 30: /* 1073741824-2147483647 */
-        return LIMB_DIGITS - 10;
-    case 31: /* 2147483648-4294967295 */
-        return LIMB_DIGITS - 10;
-    case 32: /* 4294967296-8589934591 */
-        return LIMB_DIGITS - 10;
-    case 33: /* 8589934592-17179869183 */
-        if (a < 10000000000)
-            return LIMB_DIGITS - 10;
-        else
-            return LIMB_DIGITS - 11;
-    case 34: /* 17179869184-34359738367 */
-        return LIMB_DIGITS - 11;
-    case 35: /* 34359738368-68719476735 */
-        return LIMB_DIGITS - 11;
-    case 36: /* 68719476736-137438953471 */
-        if (a < 100000000000)
-            return LIMB_DIGITS - 11;
-        else
-            return LIMB_DIGITS - 12;
-    case 37: /* 137438953472-274877906943 */
-        return LIMB_DIGITS - 12;
-    case 38: /* 274877906944-549755813887 */
-        return LIMB_DIGITS - 12;
-    case 39: /* 549755813888-1099511627775 */
-        if (a < 1000000000000)
-            return LIMB_DIGITS - 12;
-        else
-            return LIMB_DIGITS - 13;
-    case 40: /* 1099511627776-2199023255551 */
-        return LIMB_DIGITS - 13;
-    case 41: /* 2199023255552-4398046511103 */
-        return LIMB_DIGITS - 13;
-    case 42: /* 4398046511104-8796093022207 */
-        return LIMB_DIGITS - 13;
-    case 43: /* 8796093022208-17592186044415 */
-        if (a < 10000000000000)
-            return LIMB_DIGITS - 13;
-        else
-            return LIMB_DIGITS - 14;
-    case 44: /* 17592186044416-35184372088831 */
-        return LIMB_DIGITS - 14;
-    case 45: /* 35184372088832-70368744177663 */
-        return LIMB_DIGITS - 14;
-    case 46: /* 70368744177664-140737488355327 */
-        if (a < 100000000000000)
-            return LIMB_DIGITS - 14;
-        else
-            return LIMB_DIGITS - 15;
-    case 47: /* 140737488355328-281474976710655 */
-        return LIMB_DIGITS - 15;
-    case 48: /* 281474976710656-562949953421311 */
-        return LIMB_DIGITS - 15;
-    case 49: /* 562949953421312-1125899906842623 */
-        if (a < 1000000000000000)
-            return LIMB_DIGITS - 15;
-        else
-            return LIMB_DIGITS - 16;
-    case 50: /* 1125899906842624-2251799813685247 */
-        return LIMB_DIGITS - 16;
-    case 51: /* 2251799813685248-4503599627370495 */
-        return LIMB_DIGITS - 16;
-    case 52: /* 4503599627370496-9007199254740991 */
-        return LIMB_DIGITS - 16;
-    case 53: /* 9007199254740992-18014398509481983 */
-        if (a < 10000000000000000)
-            return LIMB_DIGITS - 16;
-        else
-            return LIMB_DIGITS - 17;
-    case 54: /* 18014398509481984-36028797018963967 */
-        return LIMB_DIGITS - 17;
-    case 55: /* 36028797018963968-72057594037927935 */
-        return LIMB_DIGITS - 17;
-    case 56: /* 72057594037927936-144115188075855871 */
-        if (a < 100000000000000000)
-            return LIMB_DIGITS - 17;
-        else
-            return LIMB_DIGITS - 18;
-    case 57: /* 144115188075855872-288230376151711743 */
-        return LIMB_DIGITS - 18;
-    case 58: /* 288230376151711744-576460752303423487 */
-        return LIMB_DIGITS - 18;
-    case 59: /* 576460752303423488-1152921504606846975 */
-        if (a < 1000000000000000000)
-            return LIMB_DIGITS - 18;
-        else
-            return LIMB_DIGITS - 19;
-#endif
-    default:
-        return 0;
-    }
-}
-
-/* for debugging */
-void bfdec_print_str(const char *str, const bfdec_t *a)
-{
-    slimb_t i;
-    printf("%s=", str);
-
-    if (a->expn == BF_EXP_NAN) {
-        printf("NaN");
-    } else {
-        if (a->sign)
-            putchar('-');
-        if (a->expn == BF_EXP_ZERO) {
-            putchar('0');
-        } else if (a->expn == BF_EXP_INF) {
-            printf("Inf");
-        } else {
-            printf("0.");
-            for(i = a->len - 1; i >= 0; i--)
-                printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]);
-            printf("e%" PRId_LIMB, a->expn);
-        }
-    }
-    printf("\n");
-}
-
-/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */
-static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos)
-{
-    slimb_t pos;
-    limb_t v, q;
-    int shift;
-
-    if (bit_pos < 0)
-        return 0;
-    pos = (limb_t)bit_pos / LIMB_DIGITS;
-    shift = (limb_t)bit_pos % LIMB_DIGITS;
-    fast_shr_rem_dec(q, v, r->tab[pos], shift + 1);
-    (void)q;
-    if (v != 0)
-        return 1;
-    pos--;
-    while (pos >= 0) {
-        if (r->tab[pos] != 0)
-            return 1;
-        pos--;
-    }
-    return 0;
-}
-
-static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos)
-{
-    slimb_t i;
-    int shift;
-    i = floor_div(pos, LIMB_DIGITS);
-    if (i < 0 || i >= len)
-        return 0;
-    shift = pos - i * LIMB_DIGITS;
-    return fast_shr_dec(tab[i], shift) % 10;
-}
-
-#if 0
-static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos)
-{
-    limb_t a0, a1;
-    int shift;
-    slimb_t i;
-
-    i = floor_div(pos, LIMB_DIGITS);
-    shift = pos - i * LIMB_DIGITS;
-    if (i >= 0 && i < len)
-        a0 = tab[i];
-    else
-        a0 = 0;
-    if (shift == 0) {
-        return a0;
-    } else {
-        i++;
-        if (i >= 0 && i < len)
-            a1 = tab[i];
-        else
-            a1 = 0;
-        return fast_shr_dec(a0, shift) +
-            fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) *
-            mp_pow_dec[shift];
-    }
-}
-#endif
-
-/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */
-static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l,
-                             slimb_t prec, int rnd_mode)
-{
-    int add_one, inexact;
-    limb_t digit1, digit0;
-
-    //    bfdec_print_str("get_rnd_add", r);
-    if (rnd_mode == BF_RNDF) {
-        digit0 = 1; /* faithful rounding does not honor the INEXACT flag */
-    } else {
-        /* starting limb for bit 'prec + 1' */
-        digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1));
-    }
-
-    /* get the digit at 'prec' */
-    digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec);
-    inexact = (digit1 | digit0) != 0;
-
-    add_one = 0;
-    switch(rnd_mode) {
-    case BF_RNDZ:
-        break;
-    case BF_RNDN:
-        if (digit1 == 5) {
-            if (digit0) {
-                add_one = 1;
-            } else {
-                /* round to even */
-                add_one =
-                    get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1;
-            }
-        } else if (digit1 > 5) {
-            add_one = 1;
-        }
-        break;
-    case BF_RNDD:
-    case BF_RNDU:
-        if (r->sign == (rnd_mode == BF_RNDD))
-            add_one = inexact;
-        break;
-    case BF_RNDNA:
-    case BF_RNDF:
-        add_one = (digit1 >= 5);
-        break;
-    case BF_RNDA:
-        add_one = inexact;
-        break;
-    default:
-        abort();
-    }
-
-    if (inexact)
-        *pret |= BF_ST_INEXACT;
-    return add_one;
-}
-
-/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is
-   assumed to have length 'l' (1 <= l <= r->len). prec1 can be
-   BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with
-   BF_ST_MEM_ERROR.
- */
-static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
-{
-    int shift, add_one, rnd_mode, ret;
-    slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;
-
-    /* XXX: align to IEEE 754 2008 for decimal numbers ? */
-    e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
-    e_min = -e_range + 3;
-    e_max = e_range;
-
-    if (flags & BF_FLAG_RADPNT_PREC) {
-        /* 'prec' is the precision after the decimal point */
-        if (prec1 != BF_PREC_INF)
-            prec = r->expn + prec1;
-        else
-            prec = prec1;
-    } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
-        /* restrict the precision in case of potentially subnormal
-           result */
-        assert(prec1 != BF_PREC_INF);
-        prec = prec1 - (e_min - r->expn);
-    } else {
-        prec = prec1;
-    }
-
-    /* round to prec bits */
-    rnd_mode = flags & BF_RND_MASK;
-    ret = 0;
-    add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode);
-
-    if (prec <= 0) {
-        if (add_one) {
-            bfdec_resize(r, 1); /* cannot fail because r is non zero */
-            r->tab[0] = BF_DEC_BASE / 10;
-            r->expn += 1 - prec;
-            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
-            return ret;
-        } else {
-            goto underflow;
-        }
-    } else if (add_one) {
-        limb_t carry;
-
-        /* add one starting at digit 'prec - 1' */
-        bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);
-        pos = bit_pos / LIMB_DIGITS;
-        carry = mp_pow_dec[bit_pos % LIMB_DIGITS];
-        carry = mp_add_ui_dec(r->tab + pos, carry, l - pos);
-        if (carry) {
-            /* shift right by one digit */
-            mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1);
-            r->expn++;
-        }
-    }
-
-    /* check underflow */
-    if (unlikely(r->expn < e_min)) {
-        if (flags & BF_FLAG_SUBNORMAL) {
-            /* if inexact, also set the underflow flag */
-            if (ret & BF_ST_INEXACT)
-                ret |= BF_ST_UNDERFLOW;
-        } else {
-        underflow:
-            bfdec_set_zero(r, r->sign);
-            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
-            return ret;
-        }
-    }
-
-    /* check overflow */
-    if (unlikely(r->expn > e_max)) {
-        bfdec_set_inf(r, r->sign);
-        ret |= BF_ST_OVERFLOW | BF_ST_INEXACT;
-        return ret;
-    }
-
-    /* keep the bits starting at 'prec - 1' */
-    bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);
-    i = floor_div(bit_pos, LIMB_DIGITS);
-    if (i >= 0) {
-        shift = smod(bit_pos, LIMB_DIGITS);
-        if (shift != 0) {
-            r->tab[i] = fast_shr_dec(r->tab[i], shift) *
-                mp_pow_dec[shift];
-        }
-    } else {
-        i = 0;
-    }
-    /* remove trailing zeros */
-    while (r->tab[i] == 0)
-        i++;
-    if (i > 0) {
-        l -= i;
-        memmove(r->tab, r->tab + i, l * sizeof(limb_t));
-    }
-    bfdec_resize(r, l); /* cannot fail */
-    return ret;
-}
-
-/* Cannot fail with BF_ST_MEM_ERROR. */
-int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags)
-{
-    if (r->len == 0)
-        return 0;
-    return __bfdec_round(r, prec, flags, r->len);
-}
-
-/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR.  */
-int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags)
-{
-    limb_t l, v;
-    int shift, ret;
-
-    //    bfdec_print_str("bf_renorm", r);
-    l = r->len;
-    while (l > 0 && r->tab[l - 1] == 0)
-        l--;
-    if (l == 0) {
-        /* zero */
-        r->expn = BF_EXP_ZERO;
-        bfdec_resize(r, 0); /* cannot fail */
-        ret = 0;
-    } else {
-        r->expn -= (r->len - l) * LIMB_DIGITS;
-        /* shift to have the MSB set to '1' */
-        v = r->tab[l - 1];
-        shift = clz_dec(v);
-        if (shift != 0) {
-            mp_shl_dec(r->tab, r->tab, l, shift, 0);
-            r->expn -= shift;
-        }
-        ret = __bfdec_round(r, prec1, flags, l);
-    }
-    //    bf_print_str("r_final", r);
-    return ret;
-}
-
-int bfdec_set_ui(bfdec_t *r, uint64_t v)
-{
-#if LIMB_BITS == 32
-    if (v >= BF_DEC_BASE * BF_DEC_BASE) {
-        if (bfdec_resize(r, 3))
-            goto fail;
-        r->tab[0] = v % BF_DEC_BASE;
-        v /= BF_DEC_BASE;
-        r->tab[1] = v % BF_DEC_BASE;
-        r->tab[2] = v / BF_DEC_BASE;
-        r->expn = 3 * LIMB_DIGITS;
-    } else
-#endif
-    if (v >= BF_DEC_BASE) {
-        if (bfdec_resize(r, 2))
-            goto fail;
-        r->tab[0] = v % BF_DEC_BASE;
-        r->tab[1] = v / BF_DEC_BASE;
-        r->expn = 2 * LIMB_DIGITS;
-    } else {
-        if (bfdec_resize(r, 1))
-            goto fail;
-        r->tab[0] = v;
-        r->expn = LIMB_DIGITS;
-    }
-    r->sign = 0;
-    return bfdec_normalize_and_round(r, BF_PREC_INF, 0);
- fail:
-    bfdec_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-int bfdec_set_si(bfdec_t *r, int64_t v)
-{
-    int ret;
-    if (v < 0) {
-        ret = bfdec_set_ui(r, -v);
-        r->sign = 1;
-    } else {
-        ret = bfdec_set_ui(r, v);
-    }
-    return ret;
-}
-
-static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg)
-{
-    bf_context_t *s = r->ctx;
-    int is_sub, cmp_res, a_sign, b_sign, ret;
-
-    a_sign = a->sign;
-    b_sign = b->sign ^ b_neg;
-    is_sub = a_sign ^ b_sign;
-    cmp_res = bfdec_cmpu(a, b);
-    if (cmp_res < 0) {
-        const bfdec_t *tmp;
-        tmp = a;
-        a = b;
-        b = tmp;
-        a_sign = b_sign; /* b_sign is never used later */
-    }
-    /* abs(a) >= abs(b) */
-    if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {
-        /* zero result */
-        bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);
-        ret = 0;
-    } else if (a->len == 0 || b->len == 0) {
-        ret = 0;
-        if (a->expn >= BF_EXP_INF) {
-            if (a->expn == BF_EXP_NAN) {
-                /* at least one operand is NaN */
-                bfdec_set_nan(r);
-                ret = 0;
-            } else if (b->expn == BF_EXP_INF && is_sub) {
-                /* infinities with different signs */
-                bfdec_set_nan(r);
-                ret = BF_ST_INVALID_OP;
-            } else {
-                bfdec_set_inf(r, a_sign);
-            }
-        } else {
-            /* at least one zero and not subtract */
-            if (bfdec_set(r, a))
-                return BF_ST_MEM_ERROR;
-            r->sign = a_sign;
-            goto renorm;
-        }
-    } else {
-        slimb_t d, a_offset, b_offset, i, r_len;
-        limb_t carry;
-        limb_t *b1_tab;
-        int b_shift;
-        mp_size_t b1_len;
-
-        d = a->expn - b->expn;
-
-        /* XXX: not efficient in time and memory if the precision is
-           not infinite */
-        r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);
-        if (bfdec_resize(r, r_len))
-            goto fail;
-        r->sign = a_sign;
-        r->expn = a->expn;
-
-        a_offset = r_len - a->len;
-        for(i = 0; i < a_offset; i++)
-            r->tab[i] = 0;
-        for(i = 0; i < a->len; i++)
-            r->tab[a_offset + i] = a->tab[i];
-
-        b_shift = d % LIMB_DIGITS;
-        if (b_shift == 0) {
-            b1_len = b->len;
-            b1_tab = (limb_t *)b->tab;
-        } else {
-            b1_len = b->len + 1;
-            b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len);
-            if (!b1_tab)
-                goto fail;
-            b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) *
-                mp_pow_dec[LIMB_DIGITS - b_shift];
-        }
-        b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);
-
-        if (is_sub) {
-            carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset,
-                               b1_tab, b1_len, 0);
-            if (carry != 0) {
-                carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry,
-                                      r_len - (b_offset + b1_len));
-                assert(carry == 0);
-            }
-        } else {
-            carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset,
-                               b1_tab, b1_len, 0);
-            if (carry != 0) {
-                carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry,
-                                      r_len - (b_offset + b1_len));
-            }
-            if (carry != 0) {
-                if (bfdec_resize(r, r_len + 1)) {
-                    if (b_shift != 0)
-                        bf_free(s, b1_tab);
-                    goto fail;
-                }
-                r->tab[r_len] = 1;
-                r->expn += LIMB_DIGITS;
-            }
-        }
-        if (b_shift != 0)
-            bf_free(s, b1_tab);
-    renorm:
-        ret = bfdec_normalize_and_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bfdec_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-                     bf_flags_t flags)
-{
-    return bfdec_add_internal(r, a, b, prec, flags, 0);
-}
-
-static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-                     bf_flags_t flags)
-{
-    return bfdec_add_internal(r, a, b, prec, flags, 1);
-}
-
-int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags)
-{
-    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
-                  (bf_op2_func_t *)__bfdec_add);
-}
-
-int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags)
-{
-    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
-                  (bf_op2_func_t *)__bfdec_sub);
-}
-
-int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags)
-{
-    int ret, r_sign;
-
-    if (a->len < b->len) {
-        const bfdec_t *tmp = a;
-        a = b;
-        b = tmp;
-    }
-    r_sign = a->sign ^ b->sign;
-    /* here b->len <= a->len */
-    if (b->len == 0) {
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bfdec_set_nan(r);
-            ret = 0;
-        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {
-            if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||
-                (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {
-                bfdec_set_nan(r);
-                ret = BF_ST_INVALID_OP;
-            } else {
-                bfdec_set_inf(r, r_sign);
-                ret = 0;
-            }
-        } else {
-            bfdec_set_zero(r, r_sign);
-            ret = 0;
-        }
-    } else {
-        bfdec_t tmp, *r1 = NULL;
-        limb_t a_len, b_len;
-        limb_t *a_tab, *b_tab;
-
-        a_len = a->len;
-        b_len = b->len;
-        a_tab = a->tab;
-        b_tab = b->tab;
-
-        if (r == a || r == b) {
-            bfdec_init(r->ctx, &tmp);
-            r1 = r;
-            r = &tmp;
-        }
-        if (bfdec_resize(r, a_len + b_len)) {
-            bfdec_set_nan(r);
-            ret = BF_ST_MEM_ERROR;
-            goto done;
-        }
-        mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len);
-        r->sign = r_sign;
-        r->expn = a->expn + b->expn;
-        ret = bfdec_normalize_and_round(r, prec, flags);
-    done:
-        if (r == &tmp)
-            bfdec_move(r1, &tmp);
-    }
-    return ret;
-}
-
-int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
-                 bf_flags_t flags)
-{
-    bfdec_t b;
-    int ret;
-    bfdec_init(r->ctx, &b);
-    ret = bfdec_set_si(&b, b1);
-    ret |= bfdec_mul(r, a, &b, prec, flags);
-    bfdec_delete(&b);
-    return ret;
-}
-
-int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
-                 bf_flags_t flags)
-{
-    bfdec_t b;
-    int ret;
-
-    bfdec_init(r->ctx, &b);
-    ret = bfdec_set_si(&b, b1);
-    ret |= bfdec_add(r, a, &b, prec, flags);
-    bfdec_delete(&b);
-    return ret;
-}
-
-static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
-                       limb_t prec, bf_flags_t flags)
-{
-    int ret, r_sign;
-    limb_t n, nb, precl;
-
-    r_sign = a->sign ^ b->sign;
-    if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bfdec_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {
-            bfdec_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else if (a->expn == BF_EXP_INF) {
-            bfdec_set_inf(r, r_sign);
-            return 0;
-        } else {
-            bfdec_set_zero(r, r_sign);
-            return 0;
-        }
-    } else if (a->expn == BF_EXP_ZERO) {
-        if (b->expn == BF_EXP_ZERO) {
-            bfdec_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bfdec_set_zero(r, r_sign);
-            return 0;
-        }
-    } else if (b->expn == BF_EXP_ZERO) {
-        bfdec_set_inf(r, r_sign);
-        return BF_ST_DIVIDE_ZERO;
-    }
-
-    nb = b->len;
-    if (prec == BF_PREC_INF) {
-        /* infinite precision: return BF_ST_INVALID_OP if not an exact
-           result */
-        /* XXX: check */
-        precl = nb + 1;
-    } else if (flags & BF_FLAG_RADPNT_PREC) {
-        /* number of digits after the decimal point */
-        /* XXX: check (2 extra digits for rounding + 2 digits) */
-        precl = (bf_max(a->expn - b->expn, 0) + 2 +
-                 prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
-    } else {
-        /* number of limbs of the quotient (2 extra digits for rounding) */
-        precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
-    }
-    n = bf_max(a->len, precl);
-
-    {
-        limb_t *taba, na, i;
-        slimb_t d;
-
-        na = n + nb;
-        taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t));
-        if (!taba)
-            goto fail;
-        d = na - a->len;
-        memset(taba, 0, d * sizeof(limb_t));
-        memcpy(taba + d, a->tab, a->len * sizeof(limb_t));
-        if (bfdec_resize(r, n + 1))
-            goto fail1;
-        if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) {
-        fail1:
-            bf_free(r->ctx, taba);
-            goto fail;
-        }
-        /* see if non zero remainder */
-        for(i = 0; i < nb; i++) {
-            if (taba[i] != 0)
-                break;
-        }
-        bf_free(r->ctx, taba);
-        if (i != nb) {
-            if (prec == BF_PREC_INF) {
-                bfdec_set_nan(r);
-                return BF_ST_INVALID_OP;
-            } else {
-                r->tab[0] |= 1;
-            }
-        }
-        r->expn = a->expn - b->expn + LIMB_DIGITS;
-        r->sign = r_sign;
-        ret = bfdec_normalize_and_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bfdec_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags)
-{
-    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
-                  (bf_op2_func_t *)__bfdec_div);
-}
-
-/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the
-   integer defined as floor(a/b) and r = a - q * b. */
-static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r,
-                           const bfdec_t *a, const bfdec_t *b)
-{
-    if (bfdec_cmpu(a, b) < 0) {
-        bfdec_set_ui(q, 0);
-        bfdec_set(r, a);
-    } else {
-        bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC);
-        bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ);
-        bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ);
-    }
-}
-
-/* division and remainder.
-
-   rnd_mode is the rounding mode for the quotient. The additional
-   rounding mode BF_RND_EUCLIDIAN is supported.
-
-   'q' is an integer. 'r' is rounded with prec and flags (prec can be
-   BF_PREC_INF).
-*/
-int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
-                 limb_t prec, bf_flags_t flags, int rnd_mode)
-{
-    bf_context_t *s = q->ctx;
-    bfdec_t a1_s, *a1 = &a1_s;
-    bfdec_t b1_s, *b1 = &b1_s;
-    bfdec_t r1_s, *r1 = &r1_s;
-    int q_sign, res;
-    BOOL is_ceil, is_rndn;
-
-    assert(q != a && q != b);
-    assert(r != a && r != b);
-    assert(q != r);
-
-    if (a->len == 0 || b->len == 0) {
-        bfdec_set_zero(q, 0);
-        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
-            bfdec_set_nan(r);
-            return 0;
-        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {
-            bfdec_set_nan(r);
-            return BF_ST_INVALID_OP;
-        } else {
-            bfdec_set(r, a);
-            return bfdec_round(r, prec, flags);
-        }
-    }
-
-    q_sign = a->sign ^ b->sign;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
-    switch(rnd_mode) {
-    default:
-    case BF_RNDZ:
-    case BF_RNDN:
-    case BF_RNDNA:
-        is_ceil = FALSE;
-        break;
-    case BF_RNDD:
-        is_ceil = q_sign;
-        break;
-    case BF_RNDU:
-        is_ceil = q_sign ^ 1;
-        break;
-    case BF_RNDA:
-        is_ceil = TRUE;
-        break;
-    case BF_DIVREM_EUCLIDIAN:
-        is_ceil = a->sign;
-        break;
-    }
-
-    a1->expn = a->expn;
-    a1->tab = a->tab;
-    a1->len = a->len;
-    a1->sign = 0;
-
-    b1->expn = b->expn;
-    b1->tab = b->tab;
-    b1->len = b->len;
-    b1->sign = 0;
-
-    //    bfdec_print_str("a1", a1);
-    //    bfdec_print_str("b1", b1);
-    /* XXX: could improve to avoid having a large 'q' */
-    bfdec_tdivremu(s, q, r, a1, b1);
-    if (bfdec_is_nan(q) || bfdec_is_nan(r))
-        goto fail;
-    //    bfdec_print_str("q", q);
-    //    bfdec_print_str("r", r);
-
-    if (r->len != 0) {
-        if (is_rndn) {
-            bfdec_init(s, r1);
-            if (bfdec_set(r1, r))
-                goto fail;
-            if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) {
-                bfdec_delete(r1);
-                goto fail;
-            }
-            res = bfdec_cmpu(r1, b);
-            bfdec_delete(r1);
-            if (res > 0 ||
-                (res == 0 &&
-                 (rnd_mode == BF_RNDNA ||
-                  (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) {
-                goto do_sub_r;
-            }
-        } else if (is_ceil) {
-        do_sub_r:
-            res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);
-            res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);
-            if (res & BF_ST_MEM_ERROR)
-                goto fail;
-        }
-    }
-
-    r->sign ^= a->sign;
-    q->sign = q_sign;
-    return bfdec_round(r, prec, flags);
- fail:
-    bfdec_set_nan(q);
-    bfdec_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags, int rnd_mode)
-{
-    bfdec_t q_s, *q = &q_s;
-    int ret;
-
-    bfdec_init(r->ctx, q);
-    ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode);
-    bfdec_delete(q);
-    return ret;
-}
-
-/* convert to integer (infinite precision) */
-int bfdec_rint(bfdec_t *r, int rnd_mode)
-{
-    return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);
-}
-
-int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags)
-{
-    bf_context_t *s = a->ctx;
-    int ret, k;
-    limb_t *a1, v;
-    slimb_t n, n1, prec1;
-    limb_t res;
-
-    assert(r != a);
-
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bfdec_set_nan(r);
-        } else if (a->expn == BF_EXP_INF && a->sign) {
-            goto invalid_op;
-        } else {
-            bfdec_set(r, a);
-        }
-        ret = 0;
-    } else if (a->sign || prec == BF_PREC_INF) {
- invalid_op:
-        bfdec_set_nan(r);
-        ret = BF_ST_INVALID_OP;
-    } else {
-        if (flags & BF_FLAG_RADPNT_PREC) {
-            prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1);
-        } else {
-            prec1 = prec;
-        }
-        /* convert the mantissa to an integer with at least 2 *
-           prec + 4 digits */
-        n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS);
-        if (bfdec_resize(r, n))
-            goto fail;
-        a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);
-        if (!a1)
-            goto fail;
-        n1 = bf_min(2 * n, a->len);
-        memset(a1, 0, (2 * n - n1) * sizeof(limb_t));
-        memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));
-        if (a->expn & 1) {
-            res = mp_shr_dec(a1, a1, 2 * n, 1, 0);
-        } else {
-            res = 0;
-        }
-        /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1
-           because mp_sqrtrem2_dec already does it */
-        k = 0;
-        if (n > 1) {
-            v = a1[2 * n - 1];
-            while (v < BF_DEC_BASE / 4) {
-                k++;
-                v *= 4;
-            }
-            if (k != 0)
-                mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0);
-        }
-        if (mp_sqrtrem_dec(s, r->tab, a1, n)) {
-            bf_free(s, a1);
-            goto fail;
-        }
-        if (k != 0)
-            mp_div1_dec(r->tab, r->tab, n, 1 << k, 0);
-        if (!res) {
-            res = mp_scan_nz(a1, n + 1);
-        }
-        bf_free(s, a1);
-        if (!res) {
-            res = mp_scan_nz(a->tab, a->len - n1);
-        }
-        if (res != 0)
-            r->tab[0] |= 1;
-        r->sign = 0;
-        r->expn = (a->expn + 1) >> 1;
-        ret = bfdec_round(r, prec, flags);
-    }
-    return ret;
- fail:
-    bfdec_set_nan(r);
-    return BF_ST_MEM_ERROR;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there
-   is an overflow and 0 otherwise. No memory error is possible. */
-int bfdec_get_int32(int *pres, const bfdec_t *a)
-{
-    uint32_t v;
-    int ret;
-    if (a->expn >= BF_EXP_INF) {
-        ret = 0;
-        if (a->expn == BF_EXP_INF) {
-            v = (uint32_t)INT32_MAX + a->sign;
-             /* XXX: return overflow ? */
-        } else {
-            v = INT32_MAX;
-        }
-    } else if (a->expn <= 0) {
-        v = 0;
-        ret = 0;
-    } else if (a->expn <= 9) {
-        v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);
-        if (a->sign)
-            v = -v;
-        ret = 0;
-    } else if (a->expn == 10) {
-        uint64_t v1;
-        uint32_t v_max;
-#if LIMB_BITS == 64
-        v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);
-#else
-        v1 = (uint64_t)a->tab[a->len - 1] * 10 +
-            get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1);
-#endif
-        v_max = (uint32_t)INT32_MAX + a->sign;
-        if (v1 > v_max) {
-            v = v_max;
-            ret = BF_ST_OVERFLOW;
-        } else {
-            v = v1;
-            if (a->sign)
-                v = -v;
-            ret = 0;
-        }
-    } else {
-        v = (uint32_t)INT32_MAX + a->sign;
-        ret = BF_ST_OVERFLOW;
-    }
-    *pres = v;
-    return ret;
-}
-
-/* power to an integer with infinite precision */
-int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b)
-{
-    int ret, n_bits, i;
-
-    assert(r != a);
-    if (b == 0)
-        return bfdec_set_ui(r, 1);
-    ret = bfdec_set(r, a);
-    n_bits = LIMB_BITS - clz(b);
-    for(i = n_bits - 2; i >= 0; i--) {
-        ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ);
-        if ((b >> i) & 1)
-            ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ);
-    }
-    return ret;
-}
-
-char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags)
-{
-    return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE);
-}
-
-int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
-               limb_t prec, bf_flags_t flags)
-{
-    slimb_t dummy_exp;
-    return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec,
-                            flags, TRUE);
-}
-
-#endif /* USE_BF_DEC */
-
-#ifdef USE_FFT_MUL
-/***************************************************************/
-/* Integer multiplication with FFT */
-
-/* or LIMB_BITS at bit position 'pos' in tab */
-static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val)
-{
-    limb_t i;
-    int p;
-
-    i = pos >> LIMB_LOG2_BITS;
-    p = pos & (LIMB_BITS - 1);
-    if (i < len)
-        tab[i] |= val << p;
-    if (p != 0) {
-        i++;
-        if (i < len) {
-            tab[i] |= val >> (LIMB_BITS - p);
-        }
-    }
-}
-
-#if defined(__AVX2__)
-
-typedef double NTTLimb;
-
-/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */
-#define NTT_MOD_LOG2_MIN 50
-#define NTT_MOD_LOG2_MAX 51
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 39
-static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
-    { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, },
-    { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd,
- 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a,
- 0x00066015555557e3, 0x000725960b60b623,
- 0x0002fc1fa1d6ce12,
-};
-
-#else
-
-typedef limb_t NTTLimb;
-
-#if LIMB_BITS == 64
-
-#define NTT_MOD_LOG2_MIN 61
-#define NTT_MOD_LOG2_MAX 62
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 51
-static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
-    { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, },
-    { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4,
- 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638,
- 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f,
- 0x3a5493e93e93e94a,
-};
-
-#elif LIMB_BITS == 32
-
-/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */
-#define NTT_MOD_LOG2_MIN 29
-#define NTT_MOD_LOG2_MAX 30
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 20
-static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
-    { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, },
-    { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b,
- 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938,
- 0x000000000932ab3e, 0x000000002f40eef8,
- 0x000000002e760905,
-};
-
-#endif /* LIMB_BITS */
-
-#endif /* !AVX2 */
-
-#if defined(__AVX2__)
-#define NTT_TRIG_K_MAX 18
-#else
-#define NTT_TRIG_K_MAX 19
-#endif
-
-typedef struct BFNTTState {
-    bf_context_t *ctx;
-
-    /* used for mul_mod_fast() */
-    limb_t ntt_mods_div[NB_MODS];
-
-    limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1];
-    limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1];
-    NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1];
-    /* 1/2^n mod m */
-    limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2];
-#if defined(__AVX2__)
-    __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2];
-    __m256d ntt_mods_vec[NB_MODS];
-    __m256d ntt_mods_inv_vec[NB_MODS];
-#else
-    limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2];
-#endif
-} BFNTTState;
-
-static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx);
-
-/* add modulo with up to (LIMB_BITS-1) bit modulo */
-static inline limb_t add_mod(limb_t a, limb_t b, limb_t m)
-{
-    limb_t r;
-    r = a + b;
-    if (r >= m)
-        r -= m;
-    return r;
-}
-
-/* sub modulo with up to LIMB_BITS bit modulo */
-static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m)
-{
-    limb_t r;
-    r = a - b;
-    if (r > a)
-        r += m;
-    return r;
-}
-
-/* return (r0+r1*B) mod m
-   precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN)
-*/
-static inline limb_t mod_fast(dlimb_t r,
-                                limb_t m, limb_t m_inv)
-{
-    limb_t a1, q, t0, r1, r0;
-
-    a1 = r >> NTT_MOD_LOG2_MIN;
-
-    q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS;
-    r = r - (dlimb_t)q * m - m * 2;
-    r1 = r >> LIMB_BITS;
-    t0 = (slimb_t)r1 >> 1;
-    r += m & t0;
-    r0 = r;
-    r1 = r >> LIMB_BITS;
-    r0 += m & r1;
-    return r0;
-}
-
-/* faster version using precomputed modulo inverse.
-   precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */
-static inline limb_t mul_mod_fast(limb_t a, limb_t b,
-                                    limb_t m, limb_t m_inv)
-{
-    dlimb_t r;
-    r = (dlimb_t)a * (dlimb_t)b;
-    return mod_fast(r, m, m_inv);
-}
-
-static inline limb_t init_mul_mod_fast(limb_t m)
-{
-    dlimb_t t;
-    assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX);
-    assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN);
-    t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN);
-    return t / m;
-}
-
-/* Faster version used when the multiplier is constant. 0 <= a < 2^64,
-   0 <= b < m. */
-static inline limb_t mul_mod_fast2(limb_t a, limb_t b,
-                                     limb_t m, limb_t b_inv)
-{
-    limb_t r, q;
-
-    q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;
-    r = a * b - q * m;
-    if (r >= m)
-        r -= m;
-    return r;
-}
-
-/* Faster version used when the multiplier is constant. 0 <= a < 2^64,
-   0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r +
-   m'. */
-static inline limb_t mul_mod_fast3(limb_t a, limb_t b,
-                                     limb_t m, limb_t b_inv)
-{
-    limb_t r, q;
-
-    q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;
-    r = a * b - q * m;
-    return r;
-}
-
-static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m)
-{
-    return ((dlimb_t)b << LIMB_BITS) / m;
-}
-
-#ifdef __AVX2__
-
-static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)
-{
-    slimb_t v;
-    v = a;
-    if (v < 0)
-        v += m;
-    if (v >= m)
-        v -= m;
-    return v;
-}
-
-static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m)
-{
-    return (slimb_t)a;
-}
-
-static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m)
-{
-    if (a >= (m / 2))
-        a -= m;
-    return (slimb_t)a;
-}
-
-/* return r + m if r < 0 otherwise r. */
-static inline __m256d ntt_mod1(__m256d r, __m256d m)
-{
-    return _mm256_blendv_pd(r, r + m, r);
-}
-
-/* input: abs(r) < 2 * m. Output: abs(r) < m */
-static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f)
-{
-    return _mm256_blendv_pd(r, r + m2f, r) - mf;
-}
-
-/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */
-static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf,
-                                  __m256d m_inv)
-{
-    __m256d r, q, ab1, ab0, qm0, qm1;
-    ab1 = a * b;
-    q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */
-    qm1 = q * mf;
-    qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */
-    ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */
-    r = (ab1 - qm1) + (ab0 - qm0);
-    return r;
-}
-
-static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align)
-{
-    void *ptr;
-    void **ptr1;
-    ptr = bf_malloc(s, size + sizeof(void *) + align - 1);
-    if (!ptr)
-        return NULL;
-    ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) &
-                     ~(align - 1));
-    ptr1[-1] = ptr;
-    return ptr1;
-}
-
-static void bf_aligned_free(bf_context_t *s, void *ptr)
-{
-    if (!ptr)
-        return;
-    bf_free(s, ((void **)ptr)[-1]);
-}
-
-static void *ntt_malloc(BFNTTState *s, size_t size)
-{
-    return bf_aligned_malloc(s->ctx, size, 64);
-}
-
-static void ntt_free(BFNTTState *s, void *ptr)
-{
-    bf_aligned_free(s->ctx, ptr);
-}
-
-static no_inline int ntt_fft(BFNTTState *s,
-                             NTTLimb *out_buf, NTTLimb *in_buf,
-                             NTTLimb *tmp_buf, int fft_len_log2,
-                             int inverse, int m_idx)
-{
-    limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j;
-    NTTLimb *tab_in, *tab_out, *tmp, *trig;
-    __m256d m_inv, mf, m2f, c, a0, a1, b0, b1;
-    limb_t m;
-    int l;
-
-    m = ntt_mods[m_idx];
-
-    m_inv = _mm256_set1_pd(1.0 / (double)m);
-    mf = _mm256_set1_pd(m);
-    m2f = _mm256_set1_pd(m * 2);
-
-    n = (limb_t)1 << fft_len_log2;
-    assert(n >= 8);
-    stride_in = n / 2;
-
-    tab_in = in_buf;
-    tab_out = tmp_buf;
-    trig = get_trig(s, fft_len_log2, inverse, m_idx);
-    if (!trig)
-        return -1;
-    p = 0;
-    for(k = 0; k < stride_in; k += 4) {
-        a0 = _mm256_load_pd(&tab_in[k]);
-        a1 = _mm256_load_pd(&tab_in[k + stride_in]);
-        c = _mm256_load_pd(trig);
-        trig += 4;
-        b0 = ntt_mod(a0 + a1, mf, m2f);
-        b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
-        a0 = _mm256_permute2f128_pd(b0, b1, 0x20);
-        a1 = _mm256_permute2f128_pd(b0, b1, 0x31);
-        a0 = _mm256_permute4x64_pd(a0, 0xd8);
-        a1 = _mm256_permute4x64_pd(a1, 0xd8);
-        _mm256_store_pd(&tab_out[p], a0);
-        _mm256_store_pd(&tab_out[p + 4], a1);
-        p += 2 * 4;
-    }
-    tmp = tab_in;
-    tab_in = tab_out;
-    tab_out = tmp;
-
-    trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx);
-    if (!trig)
-        return -1;
-    p = 0;
-    for(k = 0; k < stride_in; k += 4) {
-        a0 = _mm256_load_pd(&tab_in[k]);
-        a1 = _mm256_load_pd(&tab_in[k + stride_in]);
-        c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]);
-        trig += 2;
-        b0 = ntt_mod(a0 + a1, mf, m2f);
-        b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
-        a0 = _mm256_permute2f128_pd(b0, b1, 0x20);
-        a1 = _mm256_permute2f128_pd(b0, b1, 0x31);
-        _mm256_store_pd(&tab_out[p], a0);
-        _mm256_store_pd(&tab_out[p + 4], a1);
-        p += 2 * 4;
-    }
-    tmp = tab_in;
-    tab_in = tab_out;
-    tab_out = tmp;
-
-    nb_blocks = n / 4;
-    fft_per_block = 4;
-
-    l = fft_len_log2 - 2;
-    while (nb_blocks != 2) {
-        nb_blocks >>= 1;
-        p = 0;
-        k = 0;
-        trig = get_trig(s, l, inverse, m_idx);
-        if (!trig)
-            return -1;
-        for(i = 0; i < nb_blocks; i++) {
-            c = _mm256_set1_pd(trig[0]);
-            trig++;
-            for(j = 0; j < fft_per_block; j += 4) {
-                a0 = _mm256_load_pd(&tab_in[k + j]);
-                a1 = _mm256_load_pd(&tab_in[k + j + stride_in]);
-                b0 = ntt_mod(a0 + a1, mf, m2f);
-                b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
-                _mm256_store_pd(&tab_out[p + j], b0);
-                _mm256_store_pd(&tab_out[p + j + fft_per_block], b1);
-            }
-            k += fft_per_block;
-            p += 2 * fft_per_block;
-        }
-        fft_per_block <<= 1;
-        l--;
-        tmp = tab_in;
-        tab_in = tab_out;
-        tab_out = tmp;
-    }
-
-    tab_out = out_buf;
-    for(k = 0; k < stride_in; k += 4) {
-        a0 = _mm256_load_pd(&tab_in[k]);
-        a1 = _mm256_load_pd(&tab_in[k + stride_in]);
-        b0 = ntt_mod(a0 + a1, mf, m2f);
-        b1 = ntt_mod(a0 - a1, mf, m2f);
-        _mm256_store_pd(&tab_out[k], b0);
-        _mm256_store_pd(&tab_out[k + stride_in], b1);
-    }
-    return 0;
-}
-
-static void ntt_vec_mul(BFNTTState *s,
-                        NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2,
-                        int k_tot, int m_idx)
-{
-    limb_t i, c_inv, n, m;
-    __m256d m_inv, mf, a, b, c;
-
-    m = ntt_mods[m_idx];
-    c_inv = s->ntt_len_inv[m_idx][k_tot][0];
-    m_inv = _mm256_set1_pd(1.0 / (double)m);
-    mf = _mm256_set1_pd(m);
-    c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m));
-    n = (limb_t)1 << fft_len_log2;
-    for(i = 0; i < n; i += 4) {
-        a = _mm256_load_pd(&tab1[i]);
-        b = _mm256_load_pd(&tab2[i]);
-        a = ntt_mul_mod(a, b, mf, m_inv);
-        a = ntt_mul_mod(a, c, mf, m_inv);
-        _mm256_store_pd(&tab1[i], a);
-    }
-}
-
-static no_inline void mul_trig(NTTLimb *buf,
-                               limb_t n, limb_t c1, limb_t m, limb_t m_inv1)
-{
-    limb_t i, c2, c3, c4;
-    __m256d c, c_mul, a0, mf, m_inv;
-    assert(n >= 2);
-
-    mf = _mm256_set1_pd(m);
-    m_inv = _mm256_set1_pd(1.0 / (double)m);
-
-    c2 = mul_mod_fast(c1, c1, m, m_inv1);
-    c3 = mul_mod_fast(c2, c1, m, m_inv1);
-    c4 = mul_mod_fast(c2, c2, m, m_inv1);
-    c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m),
-                       int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m));
-    c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m));
-    for(i = 0; i < n; i += 4) {
-        a0 = _mm256_load_pd(&buf[i]);
-        a0 = ntt_mul_mod(a0, c, mf, m_inv);
-        _mm256_store_pd(&buf[i], a0);
-        c = ntt_mul_mod(c, c_mul, mf, m_inv);
-    }
-}
-
-#else
-
-static void *ntt_malloc(BFNTTState *s, size_t size)
-{
-    return bf_malloc(s->ctx, size);
-}
-
-static void ntt_free(BFNTTState *s, void *ptr)
-{
-    bf_free(s->ctx, ptr);
-}
-
-static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)
-{
-    if (a >= m)
-        a -= m;
-    return a;
-}
-
-static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m)
-{
-    return a;
-}
-
-static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf,
-                             NTTLimb *tmp_buf, int fft_len_log2,
-                             int inverse, int m_idx)
-{
-    limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2;
-    NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv;
-    int l;
-
-    m = ntt_mods[m_idx];
-    m2 = 2 * m;
-    n = (limb_t)1 << fft_len_log2;
-    nb_blocks = n;
-    fft_per_block = 1;
-    stride_in = n / 2;
-    tab_in = in_buf;
-    tab_out = tmp_buf;
-    l = fft_len_log2;
-    while (nb_blocks != 2) {
-        nb_blocks >>= 1;
-        p = 0;
-        k = 0;
-        trig = get_trig(s, l, inverse, m_idx);
-        if (!trig)
-            return -1;
-        for(i = 0; i < nb_blocks; i++) {
-            c = trig[0];
-            c_inv = trig[1];
-            trig += 2;
-            for(j = 0; j < fft_per_block; j++) {
-                a0 = tab_in[k + j];
-                a1 = tab_in[k + j + stride_in];
-                b0 = add_mod(a0, a1, m2);
-                b1 = a0 - a1 + m2;
-                b1 = mul_mod_fast3(b1, c, m, c_inv);
-                tab_out[p + j] = b0;
-                tab_out[p + j + fft_per_block] = b1;
-            }
-            k += fft_per_block;
-            p += 2 * fft_per_block;
-        }
-        fft_per_block <<= 1;
-        l--;
-        tmp = tab_in;
-        tab_in = tab_out;
-        tab_out = tmp;
-    }
-    /* no twiddle in last step */
-    tab_out = out_buf;
-    for(k = 0; k < stride_in; k++) {
-        a0 = tab_in[k];
-        a1 = tab_in[k + stride_in];
-        b0 = add_mod(a0, a1, m2);
-        b1 = sub_mod(a0, a1, m2);
-        tab_out[k] = b0;
-        tab_out[k + stride_in] = b1;
-    }
-    return 0;
-}
-
-static void ntt_vec_mul(BFNTTState *s,
-                        NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2,
-                        int k_tot, int m_idx)
-{
-    limb_t i, norm, norm_inv, a, n, m, m_inv;
-
-    m = ntt_mods[m_idx];
-    m_inv = s->ntt_mods_div[m_idx];
-    norm = s->ntt_len_inv[m_idx][k_tot][0];
-    norm_inv = s->ntt_len_inv[m_idx][k_tot][1];
-    n = (limb_t)1 << fft_len_log2;
-    for(i = 0; i < n; i++) {
-        a = tab1[i];
-        /* need to reduce the range so that the product is <
-           2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */
-        if (a >= m)
-            a -= m;
-        a = mul_mod_fast(a, tab2[i], m, m_inv);
-        a = mul_mod_fast3(a, norm, m, norm_inv);
-        tab1[i] = a;
-    }
-}
-
-static no_inline void mul_trig(NTTLimb *buf,
-                               limb_t n, limb_t c_mul, limb_t m, limb_t m_inv)
-{
-    limb_t i, c0, c_mul_inv;
-
-    c0 = 1;
-    c_mul_inv = init_mul_mod_fast2(c_mul, m);
-    for(i = 0; i < n; i++) {
-        buf[i] = mul_mod_fast(buf[i], c0, m, m_inv);
-        c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv);
-    }
-}
-
-#endif /* !AVX2 */
-
-static no_inline NTTLimb *get_trig(BFNTTState *s,
-                                   int k, int inverse, int m_idx)
-{
-    NTTLimb *tab;
-    limb_t i, n2, c, c_mul, m, c_mul_inv;
-
-    if (k > NTT_TRIG_K_MAX)
-        return NULL;
-
-    tab = s->ntt_trig[m_idx][inverse][k];
-    if (tab)
-        return tab;
-    n2 = (limb_t)1 << (k - 1);
-    m = ntt_mods[m_idx];
-#ifdef __AVX2__
-    tab = ntt_malloc(s, sizeof(NTTLimb) * n2);
-#else
-    tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2);
-#endif
-    if (!tab)
-        return NULL;
-    c = 1;
-    c_mul = s->ntt_proot_pow[m_idx][inverse][k];
-    c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k];
-    for(i = 0; i < n2; i++) {
-#ifdef __AVX2__
-        tab[i] = int_to_ntt_limb2(c, m);
-#else
-        tab[2 * i] = int_to_ntt_limb(c, m);
-        tab[2 * i + 1] = init_mul_mod_fast2(c, m);
-#endif
-        c = mul_mod_fast2(c, c_mul, m, c_mul_inv);
-    }
-    s->ntt_trig[m_idx][inverse][k] = tab;
-    return tab;
-}
-
-void fft_clear_cache(bf_context_t *s1)
-{
-    int m_idx, inverse, k;
-    BFNTTState *s = s1->ntt_state;
-    if (s) {
-        for(m_idx = 0; m_idx < NB_MODS; m_idx++) {
-            for(inverse = 0; inverse < 2; inverse++) {
-                for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) {
-                    if (s->ntt_trig[m_idx][inverse][k]) {
-                        ntt_free(s, s->ntt_trig[m_idx][inverse][k]);
-                        s->ntt_trig[m_idx][inverse][k] = NULL;
-                    }
-                }
-            }
-        }
-#if defined(__AVX2__)
-        bf_aligned_free(s1, s);
-#else
-        bf_free(s1, s);
-#endif
-        s1->ntt_state = NULL;
-    }
-}
-
-#define STRIP_LEN 16
-
-/* dst = buf1, src = buf2 */
-static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1,
-                           int k1, int k2, limb_t n1, limb_t n2, int inverse,
-                           limb_t m_idx)
-{
-    limb_t i, j, c_mul, c0, m, m_inv, strip_len, l;
-    NTTLimb *buf2, *buf3;
-
-    buf2 = NULL;
-    buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1);
-    if (!buf3)
-        goto fail;
-    if (k2 == 0) {
-        if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx))
-            goto fail;
-    } else {
-        strip_len = STRIP_LEN;
-        buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len);
-        if (!buf2)
-            goto fail;
-        m = ntt_mods[m_idx];
-        m_inv = s->ntt_mods_div[m_idx];
-        c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2];
-        c_mul = 1;
-        assert((n2 % strip_len) == 0);
-        for(j = 0; j < n2; j += strip_len) {
-            for(i = 0; i < n1; i++) {
-                for(l = 0; l < strip_len; l++) {
-                    buf2[i + l * n1] = buf1[i * n2 + (j + l)];
-                }
-            }
-            for(l = 0; l < strip_len; l++) {
-                if (inverse)
-                    mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);
-                if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx))
-                    goto fail;
-                if (!inverse)
-                    mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);
-                c_mul = mul_mod_fast(c_mul, c0, m, m_inv);
-            }
-
-            for(i = 0; i < n1; i++) {
-                for(l = 0; l < strip_len; l++) {
-                    buf1[i * n2 + (j + l)] = buf2[i + l *n1];
-                }
-            }
-        }
-        ntt_free(s, buf2);
-    }
-    ntt_free(s, buf3);
-    return 0;
- fail:
-    ntt_free(s, buf2);
-    ntt_free(s, buf3);
-    return -1;
-}
-
-
-/* dst = buf1, src = buf2, tmp = buf3 */
-static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2,
-                    int k, int k_tot, limb_t m_idx)
-{
-    limb_t n1, n2, i;
-    int k1, k2;
-
-    if (k <= NTT_TRIG_K_MAX) {
-        k1 = k;
-    } else {
-        /* recursive split of the FFT */
-        k1 = bf_min(k / 2, NTT_TRIG_K_MAX);
-    }
-    k2 = k - k1;
-    n1 = (limb_t)1 << k1;
-    n2 = (limb_t)1 << k2;
-
-    if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx))
-        return -1;
-    if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx))
-        return -1;
-    if (k2 == 0) {
-        ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx);
-    } else {
-        for(i = 0; i < n1; i++) {
-            ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx);
-        }
-    }
-    if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx))
-        return -1;
-    return 0;
-}
-
-
-static no_inline void limb_to_ntt(BFNTTState *s,
-                                  NTTLimb *tabr, limb_t fft_len,
-                                  const limb_t *taba, limb_t a_len, int dpl,
-                                  int first_m_idx, int nb_mods)
-{
-    slimb_t i, n;
-    dlimb_t a, b;
-    int j, shift;
-    limb_t base_mask1, a0, a1, a2, r, m, m_inv;
-
-#if 0
-    for(i = 0; i < a_len; i++) {
-        printf("%" PRId64 ": " FMT_LIMB "\n",
-               (int64_t)i, taba[i]);
-    }
-#endif
-    memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods);
-    shift = dpl & (LIMB_BITS - 1);
-    if (shift == 0)
-        base_mask1 = -1;
-    else
-        base_mask1 = ((limb_t)1 << shift) - 1;
-    n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl);
-    for(i = 0; i < n; i++) {
-        a0 = get_bits(taba, a_len, i * dpl);
-        if (dpl <= LIMB_BITS) {
-            a0 &= base_mask1;
-            a = a0;
-        } else {
-            a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS);
-            if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) {
-                a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS);
-            } else {
-                if (dpl > 2 * LIMB_BITS) {
-                    a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) &
-                        base_mask1;
-                } else {
-                    a1 &= base_mask1;
-                    a2 = 0;
-                }
-                //            printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0);
-                a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) |
-                    ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) |
-                    ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN));
-                a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1;
-            }
-        }
-        for(j = 0; j < nb_mods; j++) {
-            m = ntt_mods[first_m_idx + j];
-            m_inv = s->ntt_mods_div[first_m_idx + j];
-            r = mod_fast(a, m, m_inv);
-            if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) {
-                b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0;
-                r = mod_fast(b, m, m_inv);
-            }
-            tabr[i + j * fft_len] = int_to_ntt_limb(r, m);
-        }
-    }
-}
-
-#if defined(__AVX2__)
-
-#define VEC_LEN 4
-
-typedef union {
-    __m256d v;
-    double d[4];
-} VecUnion;
-
-static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
-                                  const NTTLimb *buf, int fft_len_log2, int dpl,
-                                  int nb_mods)
-{
-    const limb_t *mods = ntt_mods + NB_MODS - nb_mods;
-    const __m256d *mods_cr_vec, *mf, *m_inv;
-    VecUnion y[NB_MODS];
-    limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;
-    slimb_t i, len, pos;
-    int j, k, l, shift, n_limb1, p;
-    dlimb_t t;
-
-    j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;
-    mods_cr_vec = s->ntt_mods_cr_vec + j;
-    mf = s->ntt_mods_vec + NB_MODS - nb_mods;
-    m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods;
-
-    shift = dpl & (LIMB_BITS - 1);
-    if (shift == 0)
-        base_mask1 = -1;
-    else
-        base_mask1 = ((limb_t)1 << shift) - 1;
-    n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;
-    for(j = 0; j < NB_MODS; j++)
-        carry[j] = 0;
-    for(j = 0; j < NB_MODS; j++)
-        u[j] = 0; /* avoid warnings */
-    memset(tabr, 0, sizeof(limb_t) * r_len);
-    fft_len = (limb_t)1 << fft_len_log2;
-    len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);
-    len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1);
-    i = 0;
-    while (i < len) {
-        for(j = 0; j < nb_mods; j++)
-            y[j].v = *(__m256d *)&buf[i + fft_len * j];
-
-        /* Chinese remainder to get mixed radix representation */
-        l = 0;
-        for(j = 0; j < nb_mods - 1; j++) {
-            y[j].v = ntt_mod1(y[j].v, mf[j]);
-            for(k = j + 1; k < nb_mods; k++) {
-                y[k].v = ntt_mul_mod(y[k].v - y[j].v,
-                                     mods_cr_vec[l], mf[k], m_inv[k]);
-                l++;
-            }
-        }
-        y[j].v = ntt_mod1(y[j].v, mf[j]);
-
-        for(p = 0; p < VEC_LEN; p++) {
-            /* back to normal representation */
-            u[0] = (int64_t)y[nb_mods - 1].d[p];
-            l = 1;
-            for(j = nb_mods - 2; j >= 1; j--) {
-                r = (int64_t)y[j].d[p];
-                for(k = 0; k < l; k++) {
-                    t = (dlimb_t)u[k] * mods[j] + r;
-                    r = t >> LIMB_BITS;
-                    u[k] = t;
-                }
-                u[l] = r;
-                l++;
-            }
-            /* XXX: for nb_mods = 5, l should be 4 */
-
-            /* last step adds the carry */
-            r = (int64_t)y[0].d[p];
-            for(k = 0; k < l; k++) {
-                t = (dlimb_t)u[k] * mods[j] + r + carry[k];
-                r = t >> LIMB_BITS;
-                u[k] = t;
-            }
-            u[l] = r + carry[l];
-
-#if 0
-            printf("%" PRId64 ": ", i);
-            for(j = nb_mods - 1; j >= 0; j--) {
-                printf(" %019" PRIu64, u[j]);
-            }
-            printf("\n");
-#endif
-
-            /* write the digits */
-            pos = i * dpl;
-            for(j = 0; j < n_limb1; j++) {
-                put_bits(tabr, r_len, pos, u[j]);
-                pos += LIMB_BITS;
-            }
-            put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);
-            /* shift by dpl digits and set the carry */
-            if (shift == 0) {
-                for(j = n_limb1 + 1; j < nb_mods; j++)
-                    carry[j - (n_limb1 + 1)] = u[j];
-            } else {
-                for(j = n_limb1; j < nb_mods - 1; j++) {
-                    carry[j - n_limb1] = (u[j] >> shift) |
-                        (u[j + 1] << (LIMB_BITS - shift));
-                }
-                carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;
-            }
-            i++;
-        }
-    }
-}
-#else
-static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
-                                  const NTTLimb *buf, int fft_len_log2, int dpl,
-                                  int nb_mods)
-{
-    const limb_t *mods = ntt_mods + NB_MODS - nb_mods;
-    const limb_t *mods_cr, *mods_cr_inv;
-    limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;
-    slimb_t i, len, pos;
-    int j, k, l, shift, n_limb1;
-    dlimb_t t;
-
-    j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;
-    mods_cr = ntt_mods_cr + j;
-    mods_cr_inv = s->ntt_mods_cr_inv + j;
-
-    shift = dpl & (LIMB_BITS - 1);
-    if (shift == 0)
-        base_mask1 = -1;
-    else
-        base_mask1 = ((limb_t)1 << shift) - 1;
-    n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;
-    for(j = 0; j < NB_MODS; j++)
-        carry[j] = 0;
-    for(j = 0; j < NB_MODS; j++)
-        u[j] = 0; /* avoid warnings */
-    memset(tabr, 0, sizeof(limb_t) * r_len);
-    fft_len = (limb_t)1 << fft_len_log2;
-    len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);
-    for(i = 0; i < len; i++) {
-        for(j = 0; j < nb_mods; j++)  {
-            y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]);
-        }
-
-        /* Chinese remainder to get mixed radix representation */
-        l = 0;
-        for(j = 0; j < nb_mods - 1; j++) {
-            for(k = j + 1; k < nb_mods; k++) {
-                limb_t m;
-                m = mods[k];
-                /* Note: there is no overflow in the sub_mod() because
-                   the modulos are sorted by increasing order */
-                y[k] = mul_mod_fast2(y[k] - y[j] + m,
-                                     mods_cr[l], m, mods_cr_inv[l]);
-                l++;
-            }
-        }
-
-        /* back to normal representation */
-        u[0] = y[nb_mods - 1];
-        l = 1;
-        for(j = nb_mods - 2; j >= 1; j--) {
-            r = y[j];
-            for(k = 0; k < l; k++) {
-                t = (dlimb_t)u[k] * mods[j] + r;
-                r = t >> LIMB_BITS;
-                u[k] = t;
-            }
-            u[l] = r;
-            l++;
-        }
-
-        /* last step adds the carry */
-        r = y[0];
-        for(k = 0; k < l; k++) {
-            t = (dlimb_t)u[k] * mods[j] + r + carry[k];
-            r = t >> LIMB_BITS;
-            u[k] = t;
-        }
-        u[l] = r + carry[l];
-
-#if 0
-        printf("%" PRId64 ": ", (int64_t)i);
-        for(j = nb_mods - 1; j >= 0; j--) {
-            printf(" " FMT_LIMB, u[j]);
-        }
-        printf("\n");
-#endif
-
-        /* write the digits */
-        pos = i * dpl;
-        for(j = 0; j < n_limb1; j++) {
-            put_bits(tabr, r_len, pos, u[j]);
-            pos += LIMB_BITS;
-        }
-        put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);
-        /* shift by dpl digits and set the carry */
-        if (shift == 0) {
-            for(j = n_limb1 + 1; j < nb_mods; j++)
-                carry[j - (n_limb1 + 1)] = u[j];
-        } else {
-            for(j = n_limb1; j < nb_mods - 1; j++) {
-                carry[j - n_limb1] = (u[j] >> shift) |
-                    (u[j + 1] << (LIMB_BITS - shift));
-            }
-            carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;
-        }
-    }
-}
-#endif
-
-static int ntt_static_init(bf_context_t *s1)
-{
-    BFNTTState *s;
-    int inverse, i, j, k, l;
-    limb_t c, c_inv, c_inv2, m, m_inv;
-
-    if (s1->ntt_state)
-        return 0;
-#if defined(__AVX2__)
-    s = bf_aligned_malloc(s1, sizeof(*s), 64);
-#else
-    s = bf_malloc(s1, sizeof(*s));
-#endif
-    if (!s)
-        return -1;
-    memset(s, 0, sizeof(*s));
-    s1->ntt_state = s;
-    s->ctx = s1;
-
-    for(j = 0; j < NB_MODS; j++) {
-        m = ntt_mods[j];
-        m_inv = init_mul_mod_fast(m);
-        s->ntt_mods_div[j] = m_inv;
-#if defined(__AVX2__)
-        s->ntt_mods_vec[j] = _mm256_set1_pd(m);
-        s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m);
-#endif
-        c_inv2 = (m + 1) / 2; /* 1/2 */
-        c_inv = 1;
-        for(i = 0; i <= NTT_PROOT_2EXP; i++) {
-            s->ntt_len_inv[j][i][0] = c_inv;
-            s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m);
-            c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv);
-        }
-
-        for(inverse = 0; inverse < 2; inverse++) {
-            c = ntt_proot[inverse][j];
-            for(i = 0; i < NTT_PROOT_2EXP; i++) {
-                s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c;
-                s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] =
-                    init_mul_mod_fast2(c, m);
-                c = mul_mod_fast(c, c, m, m_inv);
-            }
-        }
-    }
-
-    l = 0;
-    for(j = 0; j < NB_MODS - 1; j++) {
-        for(k = j + 1; k < NB_MODS; k++) {
-#if defined(__AVX2__)
-            s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l],
-                                                                    ntt_mods[k]));
-#else
-            s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l],
-                                                       ntt_mods[k]);
-#endif
-            l++;
-        }
-    }
-    return 0;
-}
-
-int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)
-{
-    int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found;
-    int int_bits, nb_mods_found;
-    limb_t cost, min_cost;
-
-    min_cost = -1;
-    dpl_found = 0;
-    nb_mods_found = 4;
-    fft_len_log2_found = 0;
-    for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) {
-        int_bits = ntt_int_bits[NB_MODS - nb_mods];
-        dpl = bf_min((int_bits - 4) / 2,
-                     2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX);
-        for(;;) {
-            fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl);
-            if (fft_len_log2 > NTT_PROOT_2EXP)
-                goto next;
-            n_bits = fft_len_log2 + 2 * dpl;
-            if (n_bits <= int_bits) {
-                cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods;
-                //                printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost);
-                if (cost < min_cost) {
-                    min_cost = cost;
-                    dpl_found = dpl;
-                    nb_mods_found = nb_mods;
-                    fft_len_log2_found = fft_len_log2;
-                }
-                break;
-            }
-            dpl--;
-            if (dpl == 0)
-                break;
-        }
-    next: ;
-    }
-    if (!dpl_found)
-        abort();
-    /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */
-    if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) &&
-        ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >=
-        len * LIMB_BITS) {
-        dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN;
-    }
-    *pnb_mods = nb_mods_found;
-    *pdpl = dpl_found;
-    return fft_len_log2_found;
-}
-
-/* return 0 if OK, -1 if memory error */
-static no_inline int fft_mul(bf_context_t *s1,
-                             bf_t *res, limb_t *a_tab, limb_t a_len,
-                             limb_t *b_tab, limb_t b_len, int mul_flags)
-{
-    BFNTTState *s;
-    int dpl, fft_len_log2, j, nb_mods, reduced_mem;
-    slimb_t len, fft_len;
-    NTTLimb *buf1, *buf2, *ptr;
-#if defined(USE_MUL_CHECK)
-    limb_t ha, hb, hr, h_ref;
-#endif
-
-    if (ntt_static_init(s1))
-        return -1;
-    s = s1->ntt_state;
-
-    /* find the optimal number of digits per limb (dpl) */
-    len = a_len + b_len;
-    fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len);
-    fft_len = (uint64_t)1 << fft_len_log2;
-    //    printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl);
-#if defined(USE_MUL_CHECK)
-    ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0);
-    hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0);
-#endif
-    if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) {
-        if (!(mul_flags & FFT_MUL_R_NORESIZE))
-            bf_resize(res, 0);
-    } else if (mul_flags & FFT_MUL_R_OVERLAP_B) {
-        limb_t *tmp_tab, tmp_len;
-        /* it is better to free 'b' first */
-        tmp_tab = a_tab;
-        a_tab = b_tab;
-        b_tab = tmp_tab;
-        tmp_len = a_len;
-        a_len = b_len;
-        b_len = tmp_len;
-    }
-    buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);
-    if (!buf1)
-        return -1;
-    limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl,
-                NB_MODS - nb_mods, nb_mods);
-    if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) ==
-        FFT_MUL_R_OVERLAP_A) {
-        if (!(mul_flags & FFT_MUL_R_NORESIZE))
-            bf_resize(res, 0);
-    }
-    reduced_mem = (fft_len_log2 >= 14);
-    if (!reduced_mem) {
-        buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);
-        if (!buf2)
-            goto fail;
-        limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,
-                    NB_MODS - nb_mods, nb_mods);
-        if (!(mul_flags & FFT_MUL_R_NORESIZE))
-            bf_resize(res, 0); /* in case res == b */
-    } else {
-        buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len);
-        if (!buf2)
-            goto fail;
-    }
-    for(j = 0; j < nb_mods; j++) {
-        if (reduced_mem) {
-            limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,
-                        NB_MODS - nb_mods + j, 1);
-            ptr = buf2;
-        } else {
-            ptr = buf2 + fft_len * j;
-        }
-        if (ntt_conv(s, buf1 + fft_len * j, ptr,
-                     fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods))
-            goto fail;
-    }
-    if (!(mul_flags & FFT_MUL_R_NORESIZE))
-        bf_resize(res, 0); /* in case res == b and reduced mem */
-    ntt_free(s, buf2);
-    buf2 = NULL;
-    if (!(mul_flags & FFT_MUL_R_NORESIZE)) {
-        if (bf_resize(res, len))
-            goto fail;
-    }
-    ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods);
-    ntt_free(s, buf1);
-#if defined(USE_MUL_CHECK)
-    hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0);
-    h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD);
-    if (hr != h_ref) {
-        printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n",
-               len, fft_len_log2, dpl, nb_mods);
-        //        printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref);
-        exit(1);
-    }
-#endif
-    return 0;
- fail:
-    ntt_free(s, buf1);
-    ntt_free(s, buf2);
-    return -1;
-}
-
-#else /* USE_FFT_MUL */
-
-int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)
-{
-    return 0;
-}
-
-#endif /* !USE_FFT_MUL */
diff --git a/libbf.h b/libbf.h
deleted file mode 100644 (file)
index a1436ab..0000000
--- a/libbf.h
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * Tiny arbitrary precision floating point library
- *
- * Copyright (c) 2017-2021 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#ifndef LIBBF_H
-#define LIBBF_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
-#define LIMB_LOG2_BITS 6
-#else
-#define LIMB_LOG2_BITS 5
-#endif
-
-#define LIMB_BITS (1 << LIMB_LOG2_BITS)
-
-#if LIMB_BITS == 64
-typedef __int128 int128_t;
-typedef unsigned __int128 uint128_t;
-typedef int64_t slimb_t;
-typedef uint64_t limb_t;
-typedef uint128_t dlimb_t;
-#define BF_RAW_EXP_MIN INT64_MIN
-#define BF_RAW_EXP_MAX INT64_MAX
-
-#define LIMB_DIGITS 19
-#define BF_DEC_BASE UINT64_C(10000000000000000000)
-
-#else
-
-typedef int32_t slimb_t;
-typedef uint32_t limb_t;
-typedef uint64_t dlimb_t;
-#define BF_RAW_EXP_MIN INT32_MIN
-#define BF_RAW_EXP_MAX INT32_MAX
-
-#define LIMB_DIGITS 9
-#define BF_DEC_BASE 1000000000U
-
-#endif
-
-/* in bits */
-/* minimum number of bits for the exponent */
-#define BF_EXP_BITS_MIN 3
-/* maximum number of bits for the exponent */
-#define BF_EXP_BITS_MAX (LIMB_BITS - 3)
-/* extended range for exponent, used internally */
-#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1)
-/* minimum possible precision */
-#define BF_PREC_MIN 2
-/* minimum possible precision */
-#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2)
-/* some operations support infinite precision */
-#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
-
-#if LIMB_BITS == 64
-#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
-#else
-#define BF_CHKSUM_MOD 975620677U
-#endif
-
-#define BF_EXP_ZERO BF_RAW_EXP_MIN
-#define BF_EXP_INF (BF_RAW_EXP_MAX - 1)
-#define BF_EXP_NAN BF_RAW_EXP_MAX
-
-/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
-   +/-infinity is represented with expn = BF_EXP_INF and len = 0,
-   NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
- */
-typedef struct {
-    struct bf_context_t *ctx;
-    int sign;
-    slimb_t expn;
-    limb_t len;
-    limb_t *tab;
-} bf_t;
-
-typedef struct {
-    /* must be kept identical to bf_t */
-    struct bf_context_t *ctx;
-    int sign;
-    slimb_t expn;
-    limb_t len;
-    limb_t *tab;
-} bfdec_t;
-
-typedef enum {
-    BF_RNDN, /* round to nearest, ties to even */
-    BF_RNDZ, /* round to zero */
-    BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */
-    BF_RNDU, /* round to +inf */
-    BF_RNDNA, /* round to nearest, ties away from zero */
-    BF_RNDA, /* round away from zero */
-    BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
-                inexact flag is always set)  */
-} bf_rnd_t;
-
-/* allow subnormal numbers. Only available if the number of exponent
-   bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */
-#define BF_FLAG_SUBNORMAL (1 << 3)
-/* 'prec' is the precision after the radix point instead of the whole
-   mantissa. Can only be used with bf_round() and
-   bfdec_[add|sub|mul|div|sqrt|round](). */
-#define BF_FLAG_RADPNT_PREC (1 << 4)
-
-#define BF_RND_MASK 0x7
-#define BF_EXP_BITS_SHIFT 5
-#define BF_EXP_BITS_MASK 0x3f
-
-/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */
-#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)
-
-/* contains the rounding mode and number of exponents bits */
-typedef uint32_t bf_flags_t;
-
-typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
-
-typedef struct {
-    bf_t val;
-    limb_t prec;
-} BFConstCache;
-
-typedef struct bf_context_t {
-    void *realloc_opaque;
-    bf_realloc_func_t *realloc_func;
-    BFConstCache log2_cache;
-    BFConstCache pi_cache;
-    struct BFNTTState *ntt_state;
-} bf_context_t;
-
-static inline int bf_get_exp_bits(bf_flags_t flags)
-{
-    int e;
-    e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK;
-    if (e == BF_EXP_BITS_MASK)
-        return BF_EXP_BITS_MAX + 1;
-    else
-        return BF_EXP_BITS_MAX - e;
-}
-
-static inline bf_flags_t bf_set_exp_bits(int n)
-{
-    return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
-}
-
-/* returned status */
-#define BF_ST_INVALID_OP  (1 << 0)
-#define BF_ST_DIVIDE_ZERO (1 << 1)
-#define BF_ST_OVERFLOW    (1 << 2)
-#define BF_ST_UNDERFLOW   (1 << 3)
-#define BF_ST_INEXACT     (1 << 4)
-/* indicate that a memory allocation error occured. NaN is returned */
-#define BF_ST_MEM_ERROR   (1 << 5)
-
-#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
-
-static inline slimb_t bf_max(slimb_t a, slimb_t b)
-{
-    if (a > b)
-        return a;
-    else
-        return b;
-}
-
-static inline slimb_t bf_min(slimb_t a, slimb_t b)
-{
-    if (a < b)
-        return a;
-    else
-        return b;
-}
-
-void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
-                     void *realloc_opaque);
-void bf_context_end(bf_context_t *s);
-/* free memory allocated for the bf cache data */
-void bf_clear_cache(bf_context_t *s);
-
-static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
-{
-    return s->realloc_func(s->realloc_opaque, ptr, size);
-}
-
-/* 'size' must be != 0 */
-static inline void *bf_malloc(bf_context_t *s, size_t size)
-{
-    return bf_realloc(s, NULL, size);
-}
-
-static inline void bf_free(bf_context_t *s, void *ptr)
-{
-    /* must test ptr otherwise equivalent to malloc(0) */
-    if (ptr)
-        bf_realloc(s, ptr, 0);
-}
-
-void bf_init(bf_context_t *s, bf_t *r);
-
-static inline void bf_delete(bf_t *r)
-{
-    bf_context_t *s = r->ctx;
-    /* we accept to delete a zeroed bf_t structure */
-    if (s && r->tab) {
-        bf_realloc(s, r->tab, 0);
-    }
-}
-
-static inline void bf_neg(bf_t *r)
-{
-    r->sign ^= 1;
-}
-
-static inline int bf_is_finite(const bf_t *a)
-{
-    return (a->expn < BF_EXP_INF);
-}
-
-static inline int bf_is_nan(const bf_t *a)
-{
-    return (a->expn == BF_EXP_NAN);
-}
-
-static inline int bf_is_zero(const bf_t *a)
-{
-    return (a->expn == BF_EXP_ZERO);
-}
-
-static inline void bf_memcpy(bf_t *r, const bf_t *a)
-{
-    *r = *a;
-}
-
-int bf_set_ui(bf_t *r, uint64_t a);
-int bf_set_si(bf_t *r, int64_t a);
-void bf_set_nan(bf_t *r);
-void bf_set_zero(bf_t *r, int is_neg);
-void bf_set_inf(bf_t *r, int is_neg);
-int bf_set(bf_t *r, const bf_t *a);
-void bf_move(bf_t *r, bf_t *a);
-int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
-int bf_set_float64(bf_t *a, double d);
-
-int bf_cmpu(const bf_t *a, const bf_t *b);
-int bf_cmp_full(const bf_t *a, const bf_t *b);
-int bf_cmp(const bf_t *a, const bf_t *b);
-static inline int bf_cmp_eq(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b) == 0;
-}
-
-static inline int bf_cmp_le(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b) <= 0;
-}
-
-static inline int bf_cmp_lt(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b) < 0;
-}
-
-int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
-int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
-int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
-int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
-int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
-int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
-              bf_flags_t flags);
-int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
-int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
-#define BF_DIVREM_EUCLIDIAN BF_RNDF
-int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
-              limb_t prec, bf_flags_t flags, int rnd_mode);
-int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-           bf_flags_t flags, int rnd_mode);
-int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-              bf_flags_t flags, int rnd_mode);
-/* round to integer with infinite precision */
-int bf_rint(bf_t *r, int rnd_mode);
-int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
-int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
-int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-slimb_t bf_get_exp_min(const bf_t *a);
-int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
-int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
-int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
-
-/* additional flags for bf_atof */
-/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
-#define BF_ATOF_NO_HEX       (1 << 16)
-/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
-#define BF_ATOF_BIN_OCT      (1 << 17)
-/* Do not parse NaN or Inf */
-#define BF_ATOF_NO_NAN_INF   (1 << 18)
-/* return the exponent separately */
-#define BF_ATOF_EXPONENT       (1 << 19)
-
-int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
-            limb_t prec, bf_flags_t flags);
-/* this version accepts prec = BF_PREC_INF and returns the radix
-   exponent */
-int bf_atof2(bf_t *r, slimb_t *pexponent,
-             const char *str, const char **pnext, int radix,
-             limb_t prec, bf_flags_t flags);
-int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
-                     slimb_t expn, limb_t prec, bf_flags_t flags);
-
-
-/* Conversion of floating point number to string. Return a null
-   terminated string or NULL if memory error. *plen contains its
-   length if plen != NULL.  The exponent letter is "e" for base 10,
-   "p" for bases 2, 8, 16 with a binary exponent and "@" for the other
-   bases. */
-
-#define BF_FTOA_FORMAT_MASK (3 << 16)
-
-/* fixed format: prec significant digits rounded with (flags &
-   BF_RND_MASK). Exponential notation is used if too many zeros are
-   needed.*/
-#define BF_FTOA_FORMAT_FIXED (0 << 16)
-/* fractional format: prec digits after the decimal point rounded with
-   (flags & BF_RND_MASK) */
-#define BF_FTOA_FORMAT_FRAC  (1 << 16)
-/* free format:
-
-   For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
-   number of digits to represent 'a'. The precision and the rounding
-   mode are ignored.
-
-   For the non binary radices with bf_ftoa(): use as many digits as
-   necessary so that bf_atof() return the same number when using
-   precision 'prec', rounding to nearest and the subnormal
-   configuration of 'flags'. The result is meaningful only if 'a' is
-   already rounded to 'prec' bits. If the subnormal flag is set, the
-   exponent in 'flags' must also be set to the desired exponent range.
-*/
-#define BF_FTOA_FORMAT_FREE  (2 << 16)
-/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
-   (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for
-   binary radices with bf_ftoa() and for bfdec_ftoa(). */
-#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
-
-/* force exponential notation for fixed or free format */
-#define BF_FTOA_FORCE_EXP    (1 << 20)
-/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
-   base 2 if non zero value */
-#define BF_FTOA_ADD_PREFIX   (1 << 21)
-/* return "Infinity" instead of "Inf" and add a "+" for positive
-   exponents */
-#define BF_FTOA_JS_QUIRKS    (1 << 22)
-
-char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
-              bf_flags_t flags);
-
-/* modulo 2^n instead of saturation. NaN and infinity return 0 */
-#define BF_GET_INT_MOD (1 << 0)
-int bf_get_int32(int *pres, const bf_t *a, int flags);
-int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
-int bf_get_uint64(uint64_t *pres, const bf_t *a);
-
-/* the following functions are exported for testing only. */
-void mp_print_str(const char *str, const limb_t *tab, limb_t n);
-void bf_print_str(const char *str, const bf_t *a);
-int bf_resize(bf_t *r, limb_t len);
-int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
-int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
-int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
-slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
-                          int is_ceil1);
-int mp_mul(bf_context_t *s, limb_t *result,
-           const limb_t *op1, limb_t op1_size,
-           const limb_t *op2, limb_t op2_size);
-limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
-              limb_t n, limb_t carry);
-limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n);
-int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);
-int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n);
-limb_t bf_isqrt(limb_t a);
-
-/* transcendental functions */
-int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
-int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
-int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */
-int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
-int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
-             limb_t prec, bf_flags_t flags);
-int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
-
-/* decimal floating point */
-
-static inline void bfdec_init(bf_context_t *s, bfdec_t *r)
-{
-    bf_init(s, (bf_t *)r);
-}
-static inline void bfdec_delete(bfdec_t *r)
-{
-    bf_delete((bf_t *)r);
-}
-
-static inline void bfdec_neg(bfdec_t *r)
-{
-    r->sign ^= 1;
-}
-
-static inline int bfdec_is_finite(const bfdec_t *a)
-{
-    return (a->expn < BF_EXP_INF);
-}
-
-static inline int bfdec_is_nan(const bfdec_t *a)
-{
-    return (a->expn == BF_EXP_NAN);
-}
-
-static inline int bfdec_is_zero(const bfdec_t *a)
-{
-    return (a->expn == BF_EXP_ZERO);
-}
-
-static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a)
-{
-    bf_memcpy((bf_t *)r, (const bf_t *)a);
-}
-
-int bfdec_set_ui(bfdec_t *r, uint64_t a);
-int bfdec_set_si(bfdec_t *r, int64_t a);
-
-static inline void bfdec_set_nan(bfdec_t *r)
-{
-    bf_set_nan((bf_t *)r);
-}
-static inline void bfdec_set_zero(bfdec_t *r, int is_neg)
-{
-    bf_set_zero((bf_t *)r, is_neg);
-}
-static inline void bfdec_set_inf(bfdec_t *r, int is_neg)
-{
-    bf_set_inf((bf_t *)r, is_neg);
-}
-static inline int bfdec_set(bfdec_t *r, const bfdec_t *a)
-{
-    return bf_set((bf_t *)r, (bf_t *)a);
-}
-static inline void bfdec_move(bfdec_t *r, bfdec_t *a)
-{
-    bf_move((bf_t *)r, (bf_t *)a);
-}
-static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b)
-{
-    return bf_cmpu((const bf_t *)a, (const bf_t *)b);
-}
-static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b)
-{
-    return bf_cmp_full((const bf_t *)a, (const bf_t *)b);
-}
-static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b)
-{
-    return bf_cmp((const bf_t *)a, (const bf_t *)b);
-}
-static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b)
-{
-    return bfdec_cmp(a, b) == 0;
-}
-static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
-{
-    return bfdec_cmp(a, b) <= 0;
-}
-static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
-{
-    return bfdec_cmp(a, b) < 0;
-}
-
-int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags);
-int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags);
-int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
-                 bf_flags_t flags);
-int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags);
-int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
-                 bf_flags_t flags);
-int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags);
-int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
-                 limb_t prec, bf_flags_t flags, int rnd_mode);
-int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-              bf_flags_t flags, int rnd_mode);
-int bfdec_rint(bfdec_t *r, int rnd_mode);
-int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags);
-int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags);
-int bfdec_get_int32(int *pres, const bfdec_t *a);
-int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b);
-
-char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags);
-int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
-               limb_t prec, bf_flags_t flags);
-
-/* the following functions are exported for testing only. */
-extern const limb_t mp_pow_dec[LIMB_DIGITS + 1];
-void bfdec_print_str(const char *str, const bfdec_t *a);
-static inline int bfdec_resize(bfdec_t *r, limb_t len)
-{
-    return bf_resize((bf_t *)r, len);
-}
-int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags);
-
-#endif /* LIBBF_H */
diff --git a/qjs.c b/qjs.c
index 7103e11dec0a347a6a3914cc1181cb6df833b13e..f4efebe704140e0ba66bc71a9aa702c302b48df7 100644 (file)
--- a/qjs.c
+++ b/qjs.c
 
 extern const uint8_t qjsc_repl[];
 extern const uint32_t qjsc_repl_size;
-#ifdef CONFIG_BIGNUM
-extern const uint8_t qjsc_qjscalc[];
-extern const uint32_t qjsc_qjscalc_size;
-static int bignum_ext;
-#endif
 
 static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
                     const char *filename, int eval_flags)
@@ -112,14 +107,6 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
     ctx = JS_NewContext(rt);
     if (!ctx)
         return NULL;
-#ifdef CONFIG_BIGNUM
-    if (bignum_ext) {
-        JS_AddIntrinsicBigFloat(ctx);
-        JS_AddIntrinsicBigDecimal(ctx);
-        JS_AddIntrinsicOperators(ctx);
-        JS_EnableBignumExt(ctx, TRUE);
-    }
-#endif
     /* system modules */
     js_init_module_std(ctx, "std");
     js_init_module_os(ctx, "os");
@@ -283,10 +270,6 @@ void help(void)
            "    --script       load as ES6 script (default=autodetect)\n"
            "-I  --include file include an additional file\n"
            "    --std          make 'std' and 'os' available to the loaded script\n"
-#ifdef CONFIG_BIGNUM
-           "    --bignum       enable the bignum extensions (BigFloat, BigDecimal)\n"
-           "    --qjscalc      load the QJSCalc runtime (default if invoked as qjscalc)\n"
-#endif
            "-T  --trace        trace memory allocation\n"
            "-d  --dump         dump the memory usage stats\n"
            "    --memory-limit n       limit the memory usage to 'n' bytes\n"
@@ -313,23 +296,8 @@ int main(int argc, char **argv)
     size_t memory_limit = 0;
     char *include_list[32];
     int i, include_count = 0;
-#ifdef CONFIG_BIGNUM
-    int load_jscalc;
-#endif
     size_t stack_size = 0;
 
-#ifdef CONFIG_BIGNUM
-    /* load jscalc runtime if invoked as 'qjscalc' */
-    {
-        const char *p, *exename;
-        exename = argv[0];
-        p = strrchr(exename, '/');
-        if (p)
-            exename = p + 1;
-        load_jscalc = !strcmp(exename, "qjscalc");
-    }
-#endif
-
     /* cannot use getopt because we want to pass the command line to
        the script */
     optind = 1;
@@ -407,16 +375,6 @@ int main(int argc, char **argv)
                 dump_unhandled_promise_rejection = 1;
                 continue;
             }
-#ifdef CONFIG_BIGNUM
-            if (!strcmp(longopt, "bignum")) {
-                bignum_ext = 1;
-                continue;
-            }
-            if (!strcmp(longopt, "qjscalc")) {
-                load_jscalc = 1;
-                continue;
-            }
-#endif
             if (opt == 'q' || !strcmp(longopt, "quit")) {
                 empty_run++;
                 continue;
@@ -446,11 +404,6 @@ int main(int argc, char **argv)
         }
     }
 
-#ifdef CONFIG_BIGNUM
-    if (load_jscalc)
-        bignum_ext = 1;
-#endif
-
     if (trace_memory) {
         js_trace_malloc_init(&trace_data);
         rt = JS_NewRuntime2(&trace_mf, &trace_data);
@@ -482,11 +435,6 @@ int main(int argc, char **argv)
     }
 
     if (!empty_run) {
-#ifdef CONFIG_BIGNUM
-        if (load_jscalc) {
-            js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0);
-        }
-#endif
         js_std_add_helpers(ctx, argc - optind, argv + optind);
 
         /* make 'std' and 'os' visible to non module code */
diff --git a/qjsc.c b/qjsc.c
index 46f52a6f92f32bce86273ab57223dd6ee1ed77e1..7a56a3b0c8432310519279c7ccb24a0166d0956c 100644 (file)
--- a/qjsc.c
+++ b/qjsc.c
@@ -492,9 +492,6 @@ int main(int argc, char **argv)
     int module;
     OutputTypeEnum output_type;
     size_t stack_size;
-#ifdef CONFIG_BIGNUM
-    BOOL bignum_ext = FALSE;
-#endif
     namelist_t dynamic_module_list;
 
     out_filename = NULL;
@@ -547,13 +544,7 @@ int main(int argc, char **argv)
                     }
                     if (i == countof(feature_list))
                         goto bad_feature;
-                } else
-#ifdef CONFIG_BIGNUM
-                if (!strcmp(optarg, "bignum")) {
-                    bignum_ext = TRUE;
-                } else
-#endif
-                {
+                } else {
                 bad_feature:
                     fprintf(stderr, "unsupported feature: %s\n", optarg);
                     exit(1);
@@ -630,14 +621,6 @@ int main(int argc, char **argv)
 
     rt = JS_NewRuntime();
     ctx = JS_NewContext(rt);
-#ifdef CONFIG_BIGNUM
-    if (bignum_ext) {
-        JS_AddIntrinsicBigFloat(ctx);
-        JS_AddIntrinsicBigDecimal(ctx);
-        JS_AddIntrinsicOperators(ctx);
-        JS_EnableBignumExt(ctx, TRUE);
-    }
-#endif
 
     /* loader for ES6 modules */
     JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
@@ -686,15 +669,6 @@ int main(int argc, char **argv)
                         feature_list[i].init_name);
             }
         }
-#ifdef CONFIG_BIGNUM
-        if (bignum_ext) {
-            fprintf(fo,
-                    "  JS_AddIntrinsicBigFloat(ctx);\n"
-                    "  JS_AddIntrinsicBigDecimal(ctx);\n"
-                    "  JS_AddIntrinsicOperators(ctx);\n"
-                    "  JS_EnableBignumExt(ctx, 1);\n");
-        }
-#endif
         /* add the precompiled modules (XXX: could modify the module
            loader instead) */
         for(i = 0; i < init_module_list.count; i++) {
diff --git a/qjscalc.js b/qjscalc.js
deleted file mode 100644 (file)
index 1400dc0..0000000
+++ /dev/null
@@ -1,2657 +0,0 @@
-/*
- * QuickJS Javascript Calculator
- *
- * Copyright (c) 2017-2020 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-"use strict";
-"use math";
-
-var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series, Matrix;
-
-(function(global) {
-    global.Integer = global.BigInt;
-    global.Float = global.BigFloat;
-    global.algebraicMode = true;
-
-    /* add non enumerable properties */
-    function add_props(obj, props) {
-        var i, val, prop, tab, desc;
-        tab = Reflect.ownKeys(props);
-        for(i = 0; i < tab.length; i++) {
-            prop = tab[i];
-            desc = Object.getOwnPropertyDescriptor(props, prop);
-            desc.enumerable = false;
-            if ("value" in desc) {
-                if (typeof desc.value !== "function") {
-                    desc.writable = false;
-                    desc.configurable = false;
-                }
-            } else {
-                /* getter/setter */
-                desc.configurable = false;
-            }
-            Object.defineProperty(obj, prop, desc);
-        }
-    }
-
-    /* same as proto[Symbol.operatorSet] = Operators.create(..op_list)
-       but allow shortcuts: left: [], right: [] or both
-    */
-    function operators_set(proto, ...op_list)
-    {
-        var new_op_list, i, a, j, b, k, obj, tab;
-        var fields = [ "left", "right" ];
-        new_op_list = [];
-        for(i = 0; i < op_list.length; i++) {
-            a = op_list[i];
-            if (a.left || a.right) {
-                tab = [ a.left, a.right ];
-                delete a.left;
-                delete a.right;
-                for(k = 0; k < 2; k++) {
-                    obj = tab[k];
-                    if (obj) {
-                        if (!Array.isArray(obj)) {
-                            obj = [ obj ];
-                        }
-                        for(j = 0; j < obj.length; j++) {
-                            b = {};
-                            Object.assign(b, a);
-                            b[fields[k]] = obj[j];
-                            new_op_list.push(b);
-                        }
-                    }
-                }
-            } else {
-                new_op_list.push(a);
-            }
-        }
-        proto[Symbol.operatorSet] =
-            Operators.create.call(null, ...new_op_list);
-    }
-
-    /* Integer */
-
-    function generic_pow(a, b) {
-        var r, is_neg, i;
-        if (!Integer.isInteger(b)) {
-            return exp(log(a) * b);
-        }
-        if (Array.isArray(a) && !(a instanceof Polynomial ||
-                                  a instanceof Series)) {
-            r = idn(Matrix.check_square(a));
-        } else {
-            r = 1;
-        }
-        if (b == 0)
-            return r;
-        is_neg = false;
-        if (b < 0) {
-            is_neg = true;
-            b = -b;
-        }
-        r = a;
-        for(i = Integer.floorLog2(b) - 1; i >= 0; i--) {
-            r *= r;
-            if ((b >> i) & 1)
-                r *= a;
-        }
-        if (is_neg) {
-            if (typeof r.inverse != "function")
-                throw "negative powers are not supported for this type";
-            r = r.inverse();
-        }
-        return r;
-    }
-
-    var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ];
-
-    function miller_rabin_test(n, t) {
-        var d, r, s, i, j, a;
-        d = n - 1;
-        s = 0;
-        while ((d & 1) == 0) {
-            d >>= 1;
-            s++;
-        }
-        if (small_primes.length < t)
-            t = small_primes.length;
-        loop: for(j = 0; j < t; j++) {
-            a = small_primes[j];
-            r = Integer.pmod(a, d, n);
-            if (r == 1 || r == (n - 1))
-                continue;
-            for(i = 1; i < s; i++) {
-                r = (r * r) % n;
-                if (r == 1)
-                    return false;
-                if (r == (n - 1))
-                    continue loop;
-            }
-            return false; /* n is composite */
-        }
-        return true; /* n is probably prime with probability (1-0.5^t) */
-    }
-
-    function fact_rec(a, b) {  /* assumes a <= b */
-        var i, r;
-        if ((b - a) <= 5) {
-            r = a;
-            for(i = a + 1; i <= b; i++)
-                r *= i;
-            return r;
-        } else {
-            /* to avoid a quadratic running time it is better to
-               multiply numbers of similar size */
-            i = (a + b) >> 1;
-            return fact_rec(a, i) * fact_rec(i + 1, b);
-        }
-    }
-
-    /* math mode specific quirk to overload the integer division and power */
-    Operators.updateBigIntOperators(
-        {
-            "/"(a, b) {
-                if (algebraicMode) {
-                    return Fraction.toFraction(a, b);
-                } else {
-                    return Float(a) / Float(b);
-                }
-            },
-            "**"(a, b) {
-                if (algebraicMode) {
-                    return generic_pow(a, b);
-                } else {
-                    return Float(a) ** Float(b);
-                }
-            }
-        });
-
-    add_props(Integer, {
-        isInteger(a) {
-            /* integers are represented either as bigint or as number */
-            return typeof a === "bigint" ||
-                (typeof a === "number" && Number.isSafeInteger(a));
-        },
-        gcd(a, b) {
-            var r;
-            while (b != 0) {
-                r = a % b;
-                a = b;
-                b = r;
-            }
-            return a;
-        },
-        fact(n) {
-            return n <= 0 ? 1 : fact_rec(1, n);
-        },
-        /* binomial coefficient */
-        comb(n, k) {
-            if (k < 0 || k > n)
-                return 0;
-            if (k > n - k)
-                k = n - k;
-            if (k == 0)
-                return 1;
-            return Integer.tdiv(fact_rec(n - k + 1, n), fact_rec(1, k));
-        },
-        /* inverse of x modulo y */
-        invmod(x, y) {
-            var q, u, v, a, c, t;
-            u = x;
-            v = y;
-            c = 1;
-            a = 0;
-            while (u != 0) {
-                t = Integer.fdivrem(v, u);
-                q = t[0];
-                v = u;
-                u = t[1];
-                t = c;
-                c = a - q * c;
-                a = t;
-            }
-            /* v = gcd(x, y) */
-            if (v != 1)
-                throw RangeError("not invertible");
-            return a % y;
-        },
-        /* return a ^ b modulo m */
-        pmod(a, b, m) {
-            var r;
-            if (b == 0)
-                return 1;
-            if (b < 0) {
-                a = Integer.invmod(a, m);
-                b = -b;
-            }
-            r = 1;
-            for(;;) {
-                if (b & 1) {
-                    r = (r * a) % m;
-                }
-                b >>= 1;
-                if (b == 0)
-                    break;
-                a = (a * a) % m;
-            }
-            return r;
-        },
-
-        /* return true if n is prime (or probably prime with
-           probability 1-0.5^t) */
-        isPrime(n, t) {
-            var i, d, n1;
-            if (!Integer.isInteger(n))
-                throw TypeError("invalid type");
-            if (n <= 1)
-                return false;
-            n1 = small_primes.length;
-            /* XXX: need Integer.sqrt() */
-            for(i = 0; i < n1; i++) {
-                d = small_primes[i];
-                if (d == n)
-                    return true;
-                if (d > n)
-                    return false;
-                if ((n % d) == 0)
-                    return false;
-            }
-            if (n < d * d)
-                return true;
-            if (typeof t == "undefined")
-                t = 64;
-            return miller_rabin_test(n, t);
-        },
-        nextPrime(n) {
-            if (!Integer.isInteger(n))
-                throw TypeError("invalid type");
-            if (n < 1)
-                n = 1;
-            for(;;) {
-                n++;
-                if (Integer.isPrime(n))
-                    return n;
-            }
-        },
-        factor(n) {
-            var r, d;
-            if (!Integer.isInteger(n))
-                throw TypeError("invalid type");
-            r = [];
-            if (abs(n) <= 1) {
-                r.push(n);
-                return r;
-            }
-            if (n < 0) {
-                r.push(-1);
-                n = -n;
-            }
-
-            while ((n % 2) == 0) {
-                n >>= 1;
-                r.push(2);
-            }
-
-            d = 3;
-            while (n != 1) {
-                if (Integer.isPrime(n)) {
-                    r.push(n);
-                    break;
-                }
-                /* we are sure there is at least one divisor, so one test */
-                for(;;) {
-                    if ((n % d) == 0)
-                        break;
-                    d += 2;
-                }
-                for(;;) {
-                    r.push(d);
-                    n = Integer.tdiv(n, d);
-                    if ((n % d) != 0)
-                        break;
-                }
-            }
-            return r;
-        },
-    });
-
-    add_props(Integer.prototype, {
-        inverse() {
-            return 1 / this;
-        },
-        norm2() {
-            return this * this;
-        },
-        abs() {
-            var v = this;
-            if (v < 0)
-                v = -v;
-            return v;
-        },
-        conj() {
-            return this;
-        },
-        arg() {
-            if (this >= 0)
-                return 0;
-            else
-                return Float.PI;
-        },
-        exp() {
-            if (this == 0)
-                return 1;
-            else
-                return Float.exp(this);
-        },
-        log() {
-            if (this == 1)
-                return 0;
-            else
-                return Float(this).log();
-        },
-    });
-
-    /* Fraction */
-
-    Fraction = function Fraction(a, b)
-    {
-        var d, r, obj;
-
-        if (new.target)
-            throw TypeError("not a constructor");
-        if (a instanceof Fraction)
-            return a;
-        if (!Integer.isInteger(a))
-            throw TypeError("integer expected");
-        if (typeof b === "undefined") {
-            b = 1;
-        } else {
-            if (!Integer.isInteger(b))
-                throw TypeError("integer expected");
-            if (b == 0)
-                throw RangeError("division by zero");
-            d = Integer.gcd(a, b);
-            if (d != 1) {
-                a = Integer.tdiv(a, d);
-                b = Integer.tdiv(b, d);
-            }
-
-            /* the fractions are normalized with den > 0 */
-            if (b < 0) {
-                a = -a;
-                b = -b;
-            }
-        }
-        obj = Object.create(Fraction.prototype);
-        obj.num = a;
-        obj.den = b;
-        return obj;
-    }
-
-    function fraction_add(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den);
-    }
-    function fraction_sub(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den);
-    }
-    function fraction_mul(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        return Fraction.toFraction(a.num * b.num, a.den * b.den);
-    }
-    function fraction_div(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        return Fraction.toFraction(a.num * b.den, a.den * b.num);
-    }
-    function fraction_mod(a, b) {
-        var a1 = Fraction(a);
-        var b1 = Fraction(b);
-        return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b;
-    }
-    function fraction_eq(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        /* we assume the fractions are normalized */
-        return (a.num == b.num && a.den == b.den);
-    }
-    function fraction_lt(a, b) {
-        a = Fraction(a);
-        b = Fraction(b);
-        return (a.num * b.den < b.num * a.den);
-    }
-
-    /* operators are needed for fractions */
-    function float_add(a, b) {
-        return Float(a) + Float(b);
-    }
-    function float_sub(a, b) {
-        return Float(a) - Float(b);
-    }
-    function float_mul(a, b) {
-        return Float(a) * Float(b);
-    }
-    function float_div(a, b) {
-        return Float(a) / Float(b);
-    }
-    function float_mod(a, b) {
-        return Float(a) % Float(b);
-    }
-    function float_pow(a, b) {
-        return Float(a) ** Float(b);
-    }
-    function float_eq(a, b) {
-        /* XXX: may be better to use infinite precision for the comparison */
-        return Float(a) === Float(b);
-    }
-    function float_lt(a, b) {
-        a = Float(a);
-        b = Float(b);
-        /* XXX: may be better to use infinite precision for the comparison */
-        if (Float.isNaN(a) || Float.isNaN(b))
-            return undefined;
-        else
-            return a < b;
-    }
-
-    operators_set(Fraction.prototype,
-        {
-            "+": fraction_add,
-            "-": fraction_sub,
-            "*": fraction_mul,
-            "/": fraction_div,
-            "%": fraction_mod,
-            "**": generic_pow,
-            "==": fraction_eq,
-            "<": fraction_lt,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                return Fraction(-a.num, a.den);
-            },
-        },
-        {
-            left: [Number, BigInt],
-            right: [Number, BigInt],
-            "+": fraction_add,
-            "-": fraction_sub,
-            "*": fraction_mul,
-            "/": fraction_div,
-            "%": fraction_mod,
-            "**": generic_pow,
-            "==": fraction_eq,
-            "<": fraction_lt,
-        },
-        {
-            left: Float,
-            right: Float,
-            "+": float_add,
-            "-": float_sub,
-            "*": float_mul,
-            "/": float_div,
-            "%": float_mod,
-            "**": float_pow,
-            "==": float_eq,
-            "<": float_lt,
-        });
-
-    add_props(Fraction, {
-        /* (internal use) simplify 'a' to an integer when possible */
-        toFraction(a, b) {
-            var r = Fraction(a, b);
-            if (algebraicMode && r.den == 1)
-                return r.num;
-            else
-                return r;
-        },
-    });
-
-    add_props(Fraction.prototype, {
-        [Symbol.toPrimitive](hint) {
-            if (hint === "string") {
-                return this.toString();
-            } else {
-                return Float(this.num) / this.den;
-            }
-        },
-        inverse() {
-            return Fraction(this.den, this.num);
-        },
-        toString() {
-            return this.num + "/" + this.den;
-        },
-        norm2() {
-            return this * this;
-        },
-        abs() {
-            if (this.num < 0)
-                return -this;
-            else
-                return this;
-        },
-        conj() {
-            return this;
-        },
-        arg() {
-            if (this.num >= 0)
-                return 0;
-            else
-                return Float.PI;
-        },
-        exp() {
-            return Float.exp(Float(this));
-        },
-        log() {
-            return Float(this).log();
-        },
-    });
-
-    /* Number (Float64) */
-
-    add_props(Number.prototype, {
-        inverse() {
-            return 1 / this;
-        },
-        norm2() {
-            return this * this;
-        },
-        abs() {
-            return Math.abs(this);
-        },
-        conj() {
-            return this;
-        },
-        arg() {
-            if (this >= 0)
-                return 0;
-            else
-                return Float.PI;
-        },
-        exp() {
-            return Float.exp(this);
-        },
-        log() {
-            if (this < 0) {
-                return Complex(this).log();
-            } else {
-                return Float.log(this);
-            }
-        },
-    });
-
-    /* Float */
-
-    var const_tab = [];
-
-    /* we cache the constants for small precisions */
-    function get_const(n) {
-        var t, c, p;
-        t = const_tab[n];
-        p = BigFloatEnv.prec;
-        if (t && t.prec == p) {
-            return t.val;
-        } else {
-            switch(n) {
-            case 0: c = Float.exp(1); break;
-            case 1: c = Float.log(10); break;
-//            case 2: c = Float.log(2); break;
-            case 3: c = 1/Float.log(2); break;
-            case 4: c = 1/Float.log(10); break;
-//            case 5: c = Float.atan(1) * 4; break;
-            case 6: c = Float.sqrt(0.5); break;
-            case 7: c = Float.sqrt(2); break;
-            }
-            if (p <= 1024) {
-                const_tab[n] = { prec: p, val: c };
-            }
-            return c;
-        }
-    }
-
-    add_props(Float, {
-        isFloat(a) {
-            return typeof a === "number" || typeof a === "bigfloat";
-        },
-        bestappr(u, b) {
-            var num1, num0, den1, den0, u, num, den, n;
-
-            if (typeof b === "undefined")
-                throw TypeError("second argument expected");
-            num1 = 1;
-            num0 = 0;
-            den1 = 0;
-            den0 = 1;
-            for(;;) {
-                n = Integer(Float.floor(u));
-                num = n * num1 + num0;
-                den = n * den1 + den0;
-                if (den > b)
-                    break;
-                u = 1.0 / (u - n);
-                num0 = num1;
-                num1 = num;
-                den0 = den1;
-                den1 = den;
-            }
-            return Fraction(num1, den1);
-        },
-        /* similar constants as Math.x */
-        get E() { return get_const(0); },
-        get LN10() { return get_const(1); },
-//        get LN2() { return get_const(2); },
-        get LOG2E() { return get_const(3); },
-        get LOG10E() { return get_const(4); },
-//        get PI() { return get_const(5); },
-        get SQRT1_2() { return get_const(6); },
-        get SQRT2() { return get_const(7); },
-    });
-
-    add_props(Float.prototype, {
-        inverse() {
-            return 1.0 / this;
-        },
-        norm2() {
-            return this * this;
-        },
-        abs() {
-            return Float.abs(this);
-        },
-        conj() {
-            return this;
-        },
-        arg() {
-            if (this >= 0)
-                return 0;
-            else
-                return Float.PI;
-        },
-        exp() {
-            return Float.exp(this);
-        },
-        log() {
-            if (this < 0) {
-                return Complex(this).log();
-            } else {
-                return Float.log(this);
-            }
-        },
-    });
-
-    /* Complex */
-
-    Complex = function Complex(re, im)
-    {
-        var obj;
-        if (new.target)
-            throw TypeError("not a constructor");
-        if (re instanceof Complex)
-            return re;
-        if (typeof im === "undefined") {
-            im = 0;
-        }
-        obj = Object.create(Complex.prototype);
-        obj.re = re;
-        obj.im = im;
-        return obj;
-    }
-
-
-    function complex_add(a, b) {
-        a = Complex(a);
-        b = Complex(b);
-        return Complex.toComplex(a.re + b.re, a.im + b.im);
-    }
-    function complex_sub(a, b) {
-        a = Complex(a);
-        b = Complex(b);
-        return Complex.toComplex(a.re - b.re, a.im - b.im);
-    }
-    function complex_mul(a, b) {
-        a = Complex(a);
-        b = Complex(b);
-        return Complex.toComplex(a.re * b.re - a.im * b.im,
-                                 a.re * b.im + a.im * b.re);
-    }
-    function complex_div(a, b) {
-        a = Complex(a);
-        b = Complex(b);
-        return a * b.inverse();
-    }
-    function complex_eq(a, b) {
-        a = Complex(a);
-        b = Complex(b);
-        return a.re == b.re && a.im == b.im;
-    }
-
-    operators_set(Complex.prototype,
-        {
-            "+": complex_add,
-            "-": complex_sub,
-            "*": complex_mul,
-            "/": complex_div,
-            "**": generic_pow,
-            "==": complex_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                return Complex(-a.re, -a.im);
-            }
-        },
-        {
-            left: [Number, BigInt, Float, Fraction],
-            right: [Number, BigInt, Float, Fraction],
-            "+": complex_add,
-            "-": complex_sub,
-            "*": complex_mul,
-            "/": complex_div,
-            "**": generic_pow,
-            "==": complex_eq,
-        });
-
-    add_props(Complex, {
-        /* simplify to real number when possible */
-        toComplex(re, im) {
-            if (algebraicMode && im == 0)
-                return re;
-            else
-                return Complex(re, im);
-        },
-    });
-
-    add_props(Complex.prototype, {
-        inverse() {
-            var c = this.norm2();
-            return Complex(this.re / c, -this.im / c);
-        },
-        toString() {
-            var v, s = "", a = this;
-            if (a.re != 0)
-                s += a.re.toString();
-            if (a.im == 1) {
-                if (s != "")
-                    s += "+";
-                s += "I";
-            } else if (a.im == -1) {
-                s += "-I";
-            } else {
-                v = a.im.toString();
-                if (v[0] != "-" && s != "")
-                    s += "+";
-                s += v + "*I";
-            }
-            return s;
-        },
-        norm2() {
-            return this.re * this.re + this.im * this.im;
-        },
-        abs() {
-            return Float.sqrt(norm2(this));
-        },
-        conj() {
-            return Complex(this.re, -this.im);
-        },
-        arg() {
-            return Float.atan2(this.im, this.re);
-        },
-        exp() {
-            var arg = this.im, r = this.re.exp();
-            return Complex(r * cos(arg), r * sin(arg));
-        },
-        log() {
-            return Complex(abs(this).log(), atan2(this.im, this.re));
-        },
-    });
-
-    /* Mod */
-
-    Mod = function Mod(a, m) {
-        var obj, t;
-        if (new.target)
-            throw TypeError("not a constructor");
-        obj = Object.create(Mod.prototype);
-        if (Integer.isInteger(m)) {
-            if (m <= 0)
-                throw RangeError("the modulo cannot be <= 0");
-            if (Integer.isInteger(a)) {
-                a %= m;
-            } else if (a instanceof Fraction) {
-                return Mod(a.num, m) / a.den;
-            } else {
-                throw TypeError("invalid types");
-            }
-        } else {
-            throw TypeError("invalid types");
-        }
-        obj.res = a;
-        obj.mod = m;
-        return obj;
-    };
-
-    function mod_add(a, b) {
-        if (!(a instanceof Mod)) {
-            return Mod(a + b.res, b.mod);
-        } else if (!(b instanceof Mod)) {
-            return Mod(a.res + b, a.mod);
-        } else {
-            if (a.mod != b.mod)
-                throw TypeError("different modulo for binary operator");
-            return Mod(a.res + b.res, a.mod);
-        }
-    }
-    function mod_sub(a, b) {
-        if (!(a instanceof Mod)) {
-            return Mod(a - b.res, b.mod);
-        } else if (!(b instanceof Mod)) {
-            return Mod(a.res - b, a.mod);
-        } else {
-            if (a.mod != b.mod)
-                throw TypeError("different modulo for binary operator");
-            return Mod(a.res - b.res, a.mod);
-        }
-    }
-    function mod_mul(a, b) {
-        if (!(a instanceof Mod)) {
-            return Mod(a * b.res, b.mod);
-        } else if (!(b instanceof Mod)) {
-            return Mod(a.res * b, a.mod);
-        } else {
-            if (a.mod != b.mod)
-                throw TypeError("different modulo for binary operator");
-            return Mod(a.res * b.res, a.mod);
-        }
-    }
-    function mod_div(a, b) {
-        if (!(b instanceof Mod))
-            b = Mod(b, a.mod);
-        return mod_mul(a, b.inverse());
-    }
-    function mod_eq(a, b) {
-        return (a.mod == b.mod && a.res == b.res);
-    }
-
-    operators_set(Mod.prototype,
-        {
-            "+": mod_add,
-            "-": mod_sub,
-            "*": mod_mul,
-            "/": mod_div,
-            "**": generic_pow,
-            "==": mod_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                return Mod(-a.res, a.mod);
-            }
-        },
-        {
-            left: [Number, BigInt, Float, Fraction],
-            right: [Number, BigInt, Float, Fraction],
-            "+": mod_add,
-            "-": mod_sub,
-            "*": mod_mul,
-            "/": mod_div,
-            "**": generic_pow,
-        });
-
-    add_props(Mod.prototype, {
-        inverse() {
-            var a = this, m = a.mod;
-            if (Integer.isInteger(m)) {
-                return Mod(Integer.invmod(a.res, m), m);
-            } else {
-                throw TypeError("unsupported type");
-            }
-        },
-        toString() {
-            return "Mod(" + this.res + "," + this.mod + ")";
-        },
-    });
-
-    /* Polynomial */
-
-    function polynomial_is_scalar(a)
-    {
-        if (typeof a === "number" ||
-            typeof a === "bigint" ||
-            typeof a === "bigfloat")
-            return true;
-        if (a instanceof Fraction ||
-            a instanceof Complex ||
-            a instanceof Mod)
-            return true;
-        return false;
-    }
-
-    Polynomial = function Polynomial(a)
-    {
-        if (new.target)
-            throw TypeError("not a constructor");
-        if (a instanceof Polynomial) {
-            return a;
-        } else if (Array.isArray(a)) {
-            if (a.length == 0)
-                a = [ 0 ];
-            Object.setPrototypeOf(a, Polynomial.prototype);
-            return a.trim();
-        } else if (polynomial_is_scalar(a)) {
-            a = [a];
-            Object.setPrototypeOf(a, Polynomial.prototype);
-            return a;
-        } else {
-            throw TypeError("invalid type");
-        }
-    }
-
-    function number_need_paren(c)
-    {
-        return !(Integer.isInteger(c) ||
-                 Float.isFloat(c) ||
-                 c instanceof Fraction ||
-                 (c instanceof Complex && c.re == 0));
-    }
-
-    /* string for c*X^i */
-    function monomial_toString(c, i)
-    {
-        var str1;
-        if (i == 0) {
-            str1 = c.toString();
-        } else {
-            if (c == 1) {
-                str1 = "";
-            } else if (c == -1) {
-                str1 = "-";
-            } else {
-                if (number_need_paren(c)) {
-                    str1 = "(" + c + ")";
-                } else {
-                    str1 = String(c);
-                }
-                str1 += "*";
-            }
-            str1 += "X";
-            if (i != 1) {
-                str1 += "^" + i;
-            }
-        }
-        return str1;
-    }
-
-    /* find one complex root of 'p' starting from z at precision eps using
-       at most max_it iterations. Return null if could not find root. */
-    function poly_root_laguerre1(p, z, max_it)
-    {
-        var p1, p2, i, z0, z1, z2, d, t0, t1, d1, d2, e, el, zl;
-
-        d = p.deg();
-        if (d == 1) {
-            /* monomial case */
-            return -p[0] / p[1];
-        }
-        /* trivial zero */
-        if (p[0] == 0)
-            return 0.0;
-
-        p1 = p.deriv();
-        p2 = p1.deriv();
-        el = 0.0;
-        zl = 0.0;
-        for(i = 0; i < max_it; i++) {
-            z0 = p.apply(z);
-            if (z0 == 0)
-                return z; /* simple exit case */
-
-            /* Ward stopping criteria */
-            e = abs(z - zl);
-//            print("e", i, e);
-            if (i >= 2 && e >= el) {
-                if (abs(zl) < 1e-4) {
-                    if (e < 1e-7)
-                        return zl;
-                } else {
-                    if (e < abs(zl) * 1e-3)
-                        return zl;
-                }
-            }
-            el = e;
-            zl = z;
-
-            z1 = p1.apply(z);
-            z2 = p2.apply(z);
-            t0 = (d - 1) * z1;
-            t0 = t0 * t0;
-            t1 = d * (d - 1) * z0 * z2;
-            t0 = sqrt(t0 - t1);
-            d1 = z1 + t0;
-            d2 = z1 - t0;
-            if (norm2(d2) > norm2(d1))
-                d1 = d2;
-            if (d1 == 0)
-                return null;
-            z = z - d * z0 / d1;
-        }
-        return null;
-    }
-
-    function poly_roots(p)
-    {
-        var d, i, roots, j, z, eps;
-        var start_points = [ 0.1, -1.4, 1.7 ];
-
-        if (!(p instanceof Polynomial))
-            throw TypeError("polynomial expected");
-        d = p.deg();
-        if (d <= 0)
-            return [];
-        eps = 2.0 ^ (-BigFloatEnv.prec);
-        roots = [];
-        for(i = 0; i < d; i++) {
-            /* XXX: should select another start point if error */
-            for(j = 0; j < 3; j++) {
-                z = poly_root_laguerre1(p, start_points[j], 100);
-                if (z !== null)
-                    break;
-            }
-            if (j == 3)
-                throw RangeError("error in root finding algorithm");
-            roots[i] = z;
-            p = Polynomial.divrem(p, X - z)[0];
-        }
-        return roots;
-    }
-
-    add_props(Polynomial.prototype, {
-        trim() {
-            var a = this, i;
-            i = a.length;
-            while (i > 1 && a[i - 1] == 0)
-                i--;
-            a.length = i;
-            return a;
-        },
-        conj() {
-            var r, i, n, a;
-            a = this;
-            n = a.length;
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = a[i].conj();
-            return Polynomial(r);
-        },
-        inverse() {
-            return RationalFunction(Polynomial([1]), this);
-        },
-        toString() {
-            var i, str, str1, c, a = this;
-            if (a.length == 1) {
-                return a[0].toString();
-            }
-            str="";
-            for(i = a.length - 1; i >= 0; i--) {
-                c = a[i];
-                if (c == 0 ||
-                    (c instanceof Mod) && c.res == 0)
-                    continue;
-                str1 = monomial_toString(c, i);
-                if (str1[0] != "-") {
-                    if (str != "")
-                        str += "+";
-                }
-                str += str1;
-            }
-            return str;
-        },
-        deg() {
-            if (this.length == 1 && this[0] == 0)
-                return -Infinity;
-            else
-                return this.length - 1;
-        },
-        apply(b) {
-            var i, n, r, a = this;
-            n = a.length - 1;
-            r = a[n];
-            while (n > 0) {
-                n--;
-                r = r * b + a[n];
-            }
-            return r;
-        },
-        deriv() {
-            var a = this, n, r, i;
-            n = a.length;
-            if (n == 1) {
-                return Polynomial(0);
-            } else {
-                r = [];
-                for(i = 1; i < n; i++) {
-                    r[i - 1] = i * a[i];
-                }
-                return Polynomial(r);
-            }
-        },
-        integ() {
-            var a = this, n, r, i;
-            n = a.length;
-            r = [0];
-            for(i = 0; i < n; i++) {
-                r[i + 1] = a[i] / (i + 1);
-            }
-            return Polynomial(r);
-        },
-        norm2() {
-            var a = this, n, r, i;
-            n = a.length;
-            r = 0;
-            for(i = 0; i < n; i++) {
-                r += a[i].norm2();
-            }
-            return r;
-        },
-    });
-
-
-    function polynomial_add(a, b) {
-        var tmp, r, i, n1, n2;
-        a = Polynomial(a);
-        b = Polynomial(b);
-        if (a.length < b.length) {
-            tmp = a;
-            a = b;
-            b = tmp;
-        }
-        n1 = b.length;
-        n2 = a.length;
-        r = [];
-        for(i = 0; i < n1; i++)
-            r[i] = a[i] + b[i];
-        for(i = n1; i < n2; i++)
-            r[i] = a[i];
-        return Polynomial(r);
-    }
-    function polynomial_sub(a, b) {
-        return polynomial_add(a, -b);
-    }
-    function polynomial_mul(a, b) {
-        var i, j, n1, n2, n, r;
-        a = Polynomial(a);
-        b = Polynomial(b);
-        n1 = a.length;
-        n2 = b.length;
-        n = n1 + n2 - 1;
-        r = [];
-        for(i = 0; i < n; i++)
-            r[i] = 0;
-        for(i = 0; i < n1; i++) {
-            for(j = 0; j < n2; j++) {
-                r[i + j] += a[i] * b[j];
-            }
-        }
-        return Polynomial(r);
-    }
-    function polynomial_div_scalar(a, b) {
-        return a * (1 / b);
-    }
-    function polynomial_div(a, b)
-    {
-        return RationalFunction(Polynomial(a),
-                                Polynomial(b));
-    }
-    function polynomial_mod(a, b) {
-        return Polynomial.divrem(a, b)[1];
-    }
-    function polynomial_eq(a, b) {
-        var n, i;
-        n = a.length;
-        if (n != b.length)
-            return false;
-        for(i = 0; i < n; i++) {
-            if (a[i] != b[i])
-                return false;
-        }
-        return true;
-    }
-
-    operators_set(Polynomial.prototype,
-        {
-            "+": polynomial_add,
-            "-": polynomial_sub,
-            "*": polynomial_mul,
-            "/": polynomial_div,
-            "**": generic_pow,
-            "==": polynomial_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                var r, i, n, a;
-                n = a.length;
-                r = [];
-                for(i = 0; i < n; i++)
-                r[i] = -a[i];
-                return Polynomial(r);
-            },
-        },
-        {
-            left: [Number, BigInt, Float, Fraction, Complex, Mod],
-            "+": polynomial_add,
-            "-": polynomial_sub,
-            "*": polynomial_mul,
-            "/": polynomial_div,
-            "**": generic_pow, /* XXX: only for integer */
-        },
-        {
-            right: [Number, BigInt, Float, Fraction, Complex, Mod],
-            "+": polynomial_add,
-            "-": polynomial_sub,
-            "*": polynomial_mul,
-            "/": polynomial_div_scalar,
-            "**": generic_pow, /* XXX: only for integer */
-        });
-
-    add_props(Polynomial, {
-        divrem(a, b) {
-            var n1, n2, i, j, q, r, n, c;
-            if (b.deg() < 0)
-                throw RangeError("division by zero");
-            n1 = a.length;
-            n2 = b.length;
-            if (n1 < n2)
-                return [Polynomial([0]), a];
-            r = Array.prototype.dup.call(a);
-            q = [];
-            n2--;
-            n = n1 - n2;
-            for(i = 0; i < n; i++)
-                q[i] = 0;
-            for(i = n - 1; i >= 0; i--) {
-                c = r[i + n2];
-                if (c != 0) {
-                    c = c / b[n2];
-                    r[i + n2] = 0;
-                    for(j = 0; j < n2; j++) {
-                        r[i + j] -= b[j] * c;
-                    }
-                    q[i] = c;
-                }
-            }
-            return [Polynomial(q), Polynomial(r)];
-        },
-        gcd(a, b) {
-            var t;
-            while (b.deg() >= 0) {
-                t = Polynomial.divrem(a, b);
-                a = b;
-                b = t[1];
-            }
-            /* convert to monic form */
-            return a / a[a.length - 1];
-        },
-        invmod(x, y) {
-            var q, u, v, a, c, t;
-            u = x;
-            v = y;
-            c = Polynomial([1]);
-            a = Polynomial([0]);
-            while (u.deg() >= 0) {
-                t = Polynomial.divrem(v, u);
-                q = t[0];
-                v = u;
-                u = t[1];
-                t = c;
-                c = a - q * c;
-                a = t;
-            }
-            /* v = gcd(x, y) */
-            if (v.deg() > 0)
-                throw RangeError("not invertible");
-            return Polynomial.divrem(a, y)[1];
-        },
-        roots(p) {
-            return poly_roots(p);
-        }
-    });
-
-    /* Polynomial Modulo Q */
-
-    PolyMod = function PolyMod(a, m) {
-        var obj, t;
-        if (new.target)
-            throw TypeError("not a constructor");
-        obj = Object.create(PolyMod.prototype);
-        if (m instanceof Polynomial) {
-            if (m.deg() <= 0)
-                throw RangeError("the modulo cannot have a degree <= 0");
-            if (a instanceof RationalFunction) {
-                return PolyMod(a.num, m) / a.den;
-            } else {
-                a = Polynomial(a);
-                t = Polynomial.divrem(a, m);
-                a = t[1];
-            }
-        } else {
-            throw TypeError("invalid types");
-        }
-        obj.res = a;
-        obj.mod = m;
-        return obj;
-    };
-
-    function polymod_add(a, b) {
-        if (!(a instanceof PolyMod)) {
-            return PolyMod(a + b.res, b.mod);
-        } else if (!(b instanceof PolyMod)) {
-            return PolyMod(a.res + b, a.mod);
-        } else {
-            if (a.mod != b.mod)
-                throw TypeError("different modulo for binary operator");
-            return PolyMod(a.res + b.res, a.mod);
-        }
-    }
-    function polymod_sub(a, b) {
-        return polymod_add(a, -b);
-    }
-    function polymod_mul(a, b) {
-        if (!(a instanceof PolyMod)) {
-            return PolyMod(a * b.res, b.mod);
-        } else if (!(b instanceof PolyMod)) {
-            return PolyMod(a.res * b, a.mod);
-        } else {
-            if (a.mod != b.mod)
-                    throw TypeError("different modulo for binary operator");
-            return PolyMod(a.res * b.res, a.mod);
-        }
-    }
-    function polymod_div(a, b) {
-        if (!(b instanceof PolyMod))
-            b = PolyMod(b, a.mod);
-        return polymod_mul(a, b.inverse());
-    }
-    function polymod_eq(a, b) {
-        return (a.mod == b.mod && a.res == b.res);
-    }
-
-    operators_set(PolyMod.prototype,
-        {
-            "+": polymod_add,
-            "-": polymod_sub,
-            "*": polymod_mul,
-            "/": polymod_div,
-            "**": generic_pow,
-            "==": polymod_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                return PolyMod(-a.res, a.mod);
-            },
-        },
-        {
-            left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            "+": polymod_add,
-            "-": polymod_sub,
-            "*": polymod_mul,
-            "/": polymod_div,
-            "**": generic_pow, /* XXX: only for integer */
-        });
-
-    add_props(PolyMod.prototype, {
-        inverse() {
-            var a = this, m = a.mod;
-            if (m instanceof Polynomial) {
-                return PolyMod(Polynomial.invmod(a.res, m), m);
-            } else {
-                throw TypeError("unsupported type");
-            }
-        },
-        toString() {
-            return "PolyMod(" + this.res + "," + this.mod + ")";
-        },
-    });
-
-    /* Rational function */
-
-    RationalFunction = function RationalFunction(a, b)
-    {
-        var t, r, d, obj;
-        if (new.target)
-            throw TypeError("not a constructor");
-        if (!(a instanceof Polynomial) ||
-            !(b instanceof Polynomial))
-            throw TypeError("polynomial expected");
-        t = Polynomial.divrem(a, b);
-        r = t[1];
-        if (r.deg() < 0)
-            return t[0]; /* no need for a fraction */
-        d = Polynomial.gcd(b, r);
-        if (d.deg() > 0) {
-            a = Polynomial.divrem(a, d)[0];
-            b = Polynomial.divrem(b, d)[0];
-        }
-        obj = Object.create(RationalFunction.prototype);
-        obj.num = a;
-        obj.den = b;
-        return obj;
-    }
-
-    add_props(RationalFunction.prototype, {
-        inverse() {
-            return RationalFunction(this.den, this.num);
-        },
-        conj() {
-            return RationalFunction(this.num.conj(), this.den.conj());
-        },
-        toString() {
-            var str;
-            if (this.num.deg() <= 0 &&
-                !number_need_paren(this.num[0]))
-                str = this.num.toString();
-            else
-                str = "(" + this.num.toString() + ")";
-            str += "/(" + this.den.toString() + ")"
-            return str;
-        },
-        apply(b) {
-            return this.num.apply(b) / this.den.apply(b);
-        },
-        deriv() {
-            var n = this.num, d = this.den;
-            return RationalFunction(n.deriv() * d - n * d.deriv(), d * d);
-        },
-    });
-
-    function ratfunc_add(a, b) {
-        a = RationalFunction.toRationalFunction(a);
-        b = RationalFunction.toRationalFunction(b);
-        return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den);
-    }
-    function ratfunc_sub(a, b) {
-        a = RationalFunction.toRationalFunction(a);
-        b = RationalFunction.toRationalFunction(b);
-        return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den);
-    }
-    function ratfunc_mul(a, b) {
-        a = RationalFunction.toRationalFunction(a);
-        b = RationalFunction.toRationalFunction(b);
-        return RationalFunction(a.num * b.num, a.den * b.den);
-    }
-    function ratfunc_div(a, b) {
-        a = RationalFunction.toRationalFunction(a);
-        b = RationalFunction.toRationalFunction(b);
-        return RationalFunction(a.num * b.den, a.den * b.num);
-    }
-    function ratfunc_eq(a, b) {
-        a = RationalFunction.toRationalFunction(a);
-        b = RationalFunction.toRationalFunction(b);
-        /* we assume the fractions are normalized */
-        return (a.num == b.num && a.den == b.den);
-    }
-
-    operators_set(RationalFunction.prototype,
-        {
-            "+": ratfunc_add,
-            "-": ratfunc_sub,
-            "*": ratfunc_mul,
-            "/": ratfunc_div,
-            "**": generic_pow,
-            "==": ratfunc_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                return RationalFunction(-this.num, this.den);
-            },
-        },
-        {
-            left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            "+": ratfunc_add,
-            "-": ratfunc_sub,
-            "*": ratfunc_mul,
-            "/": ratfunc_div,
-            "**": generic_pow, /* should only be used with integers */
-        });
-
-    add_props(RationalFunction, {
-        /* This function always return a RationalFunction object even
-           if it could simplified to a polynomial, so it is not
-           equivalent to RationalFunction(a) */
-        toRationalFunction(a) {
-            var obj;
-            if (a instanceof RationalFunction) {
-                return a;
-            } else {
-                obj = Object.create(RationalFunction.prototype);
-                obj.num = Polynomial(a);
-                obj.den = Polynomial(1);
-                return obj;
-            }
-        },
-    });
-
-    /* Power series */
-
-    /* 'a' is an array */
-    function get_emin(a) {
-        var i, n;
-        n = a.length;
-        for(i = 0; i < n; i++) {
-            if (a[i] != 0)
-                return i;
-        }
-        return n;
-    };
-
-    function series_is_scalar_or_polynomial(a)
-    {
-        return polynomial_is_scalar(a) ||
-            (a instanceof Polynomial);
-    }
-
-    /* n is the maximum number of terms if 'a' is not a serie */
-    Series = function Series(a, n) {
-        var emin, r, i;
-
-        if (a instanceof Series) {
-            return a;
-        } else if (series_is_scalar_or_polynomial(a)) {
-            if (n <= 0) {
-                /* XXX: should still use the polynomial degree */
-                return Series.zero(0, 0);
-            } else {
-                a = Polynomial(a);
-                emin = get_emin(a);
-                r = Series.zero(n, emin);
-                n = Math.min(a.length - emin, n);
-                for(i = 0; i < n; i++)
-                    r[i] = a[i + emin];
-                return r;
-            }
-        } else if (a instanceof RationalFunction) {
-            return Series(a.num, n) / a.den;
-        } else {
-            throw TypeError("invalid type");
-        }
-    };
-
-    function series_add(v1, v2) {
-        var tmp, d, emin, n, r, i, j, v2_emin, c1, c2;
-        if (!(v1 instanceof Series)) {
-            tmp = v1;
-            v1 = v2;
-            v2 = tmp;
-        }
-        d = v1.emin + v1.length;
-        if (series_is_scalar_or_polynomial(v2)) {
-            v2 = Polynomial(v2);
-            if (d <= 0)
-                return v1;
-            v2_emin = 0;
-        } else if (v2 instanceof RationalFunction) {
-            /* compute the emin of the rational fonction */
-            i = get_emin(v2.num) - get_emin(v2.den);
-            if (d <= i)
-                return v1;
-            /* compute the serie with the required terms */
-            v2 = Series(v2, d - i);
-            v2_emin = v2.emin;
-        } else {
-            v2_emin = v2.emin;
-            d = Math.min(d, v2_emin + v2.length);
-        }
-        emin = Math.min(v1.emin, v2_emin);
-        n = d - emin;
-        r = Series.zero(n, emin);
-        /* XXX: slow */
-        for(i = emin; i < d; i++) {
-            j = i - v1.emin;
-            if (j >= 0 && j < v1.length)
-                c1 = v1[j];
-            else
-                c1 = 0;
-            j = i - v2_emin;
-            if (j >= 0 && j < v2.length)
-                c2 = v2[j];
-            else
-                c2 = 0;
-            r[i - emin] = c1 + c2;
-        }
-        return r.trim();
-    }
-    function series_sub(a, b) {
-        return series_add(a, -b);
-    }
-    function series_mul(v1, v2) {
-        var n, i, j, r, n, emin, n1, n2, k;
-        if (!(v1 instanceof Series))
-            v1 = Series(v1, v2.length);
-        else if (!(v2 instanceof Series))
-            v2 = Series(v2, v1.length);
-        emin = v1.emin + v2.emin;
-        n = Math.min(v1.length, v2.length);
-        n1 = v1.length;
-        n2 = v2.length;
-        r = Series.zero(n, emin);
-        for(i = 0; i < n1; i++) {
-            k = Math.min(n2, n - i);
-            for(j = 0; j < k; j++) {
-                r[i + j] += v1[i] * v2[j];
-            }
-        }
-        return r.trim();
-    }
-    function series_div(v1, v2) {
-        if (!(v2 instanceof Series))
-            v2 = Series(v2, v1.length);
-        return series_mul(v1, v2.inverse());
-    }
-    function series_pow(a, b) {
-        if (Integer.isInteger(b)) {
-            return generic_pow(a, b);
-        } else {
-            if (!(a instanceof Series))
-                a = Series(a, b.length);
-            return exp(log(a) * b);
-        }
-    }
-    function series_eq(a, b) {
-        var n, i;
-        if (a.emin != b.emin)
-            return false;
-        n = a.length;
-        if (n != b.length)
-            return false;
-        for(i = 0; i < n; i++) {
-            if (a[i] != b[i])
-                return false;
-        }
-        return true;
-    }
-
-    operators_set(Series.prototype,
-        {
-            "+": series_add,
-            "-": series_sub,
-            "*": series_mul,
-            "/": series_div,
-            "**": series_pow,
-            "==": series_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                var obj, n, i;
-                n = a.length;
-                obj = Series.zero(a.length, a.emin);
-                for(i = 0; i < n; i++) {
-                    obj[i] = -a[i];
-                }
-                return obj;
-            },
-        },
-        {
-            left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial],
-            "+": series_add,
-            "-": series_sub,
-            "*": series_mul,
-            "/": series_div,
-            "**": series_pow,
-        });
-
-    add_props(Series.prototype, {
-        conj() {
-            var obj, n, i;
-            n = this.length;
-            obj = Series.zero(this.length, this.emin);
-            for(i = 0; i < n; i++) {
-                obj[i] = this[i].conj();
-            }
-            return obj;
-        },
-        inverse() {
-            var r, n, i, j, sum, v1 = this;
-            n = v1.length;
-            if (n == 0)
-                throw RangeError("division by zero");
-            r = Series.zero(n, -v1.emin);
-            r[0] = 1 / v1[0];
-            for(i = 1; i < n; i++) {
-                sum = 0;
-                for(j = 1; j <= i; j++) {
-                    sum += v1[j] * r[i - j];
-                }
-                r[i] = -sum * r[0];
-            }
-            return r;
-        },
-        /* remove leading zero terms */
-        trim() {
-            var i, j, n, r, v1 = this;
-            n = v1.length;
-            i = 0;
-            while (i < n && v1[i] == 0)
-                i++;
-            if (i == 0)
-                return v1;
-            for(j = i; j < n; j++)
-                v1[j - i] = v1[j];
-            v1.length = n - i;
-            v1.__proto__.emin += i;
-            return v1;
-        },
-        toString() {
-            var i, j, str, str1, c, a = this, emin, n;
-            str="";
-            emin = this.emin;
-            n = this.length;
-            for(j = 0; j < n; j++) {
-                i = j + emin;
-                c = a[j];
-                if (c != 0) {
-                    str1 = monomial_toString(c, i);
-                    if (str1[0] != "-") {
-                        if (str != "")
-                            str += "+";
-                    }
-                    str += str1;
-                }
-            }
-            if (str != "")
-                str += "+";
-            str += "O(" + monomial_toString(1, n + emin) + ")";
-            return str;
-        },
-        apply(b) {
-            var i, n, r, a = this;
-            n = a.length;
-            if (n == 0)
-                return 0;
-            r = a[--n];
-            while (n > 0) {
-                n--;
-                r = r * b + a[n];
-            }
-            if (a.emin != 0)
-                r *= b ^ a.emin;
-            return r;
-        },
-        deriv() {
-            var a = this, n = a.length, emin = a.emin, r, i, j;
-            if (n == 0 && emin == 0) {
-                return Series.zero(0, 0);
-            } else {
-                r = Series.zero(n, emin - 1);
-                for(i = 0; i < n; i++) {
-                    j = emin + i;
-                    if (j == 0)
-                        r[i] = 0;
-                    else
-                        r[i] = j * a[i];
-                }
-                return r.trim();
-            }
-        },
-        integ() {
-            var a = this, n = a.length, emin = a.emin, i, j, r;
-            r = Series.zero(n, emin + 1);
-            for(i = 0; i < n; i++) {
-                j = emin + i;
-                if (j == -1) {
-                    if (a[i] != 0)
-                        throw RangeError("cannot represent integ(1/X)");
-                } else {
-                    r[i] = a[i] / (j + 1);
-                }
-            }
-            return r.trim();
-        },
-        exp() {
-            var c, i, r, n, a = this;
-            if (a.emin < 0)
-                throw RangeError("negative exponent in exp");
-            n = a.emin + a.length;
-            if (a.emin > 0 || a[0] == 0) {
-                c = 1;
-            } else {
-                c = global.exp(a[0]);
-                a -= a[0];
-            }
-            r = Series.zero(n, 0);
-            for(i = 0; i < n; i++) {
-                r[i] = c / fact(i);
-            }
-            return r.apply(a);
-        },
-        log() {
-            var a = this, r;
-            if (a.emin != 0)
-                throw RangeError("log argument must have a non zero constant term");
-            r = integ(deriv(a) / a);
-            /* add the constant term */
-            r += global.log(a[0]);
-            return r;
-        },
-    });
-
-    add_props(Series, {
-        /* new series of length n and first exponent emin */
-        zero(n, emin) {
-            var r, i, obj;
-
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = 0;
-            /* we return an array and store emin in its prototype */
-            obj = Object.create(Series.prototype);
-            obj.emin = emin;
-            Object.setPrototypeOf(r, obj);
-            return r;
-        },
-        O(a) {
-            function ErrorO() {
-                return TypeError("invalid O() argument");
-            }
-            var n;
-            if (series_is_scalar_or_polynomial(a)) {
-                a = Polynomial(a);
-                n = a.deg();
-                if (n < 0)
-                    throw ErrorO();
-            } else if (a instanceof RationalFunction) {
-                if (a.num.deg() != 0)
-                    throw ErrorO();
-                n = a.den.deg();
-                if (n < 0)
-                    throw ErrorO();
-                n = -n;
-            } else
-                throw ErrorO();
-            return Series.zero(0, n);
-        },
-    });
-
-    /* Array (Matrix) */
-
-    Matrix = function Matrix(h, w) {
-        var i, j, r, rl;
-        if (typeof w === "undefined")
-            w = h;
-        r = [];
-        for(i = 0; i < h; i++) {
-            rl = [];
-            for(j = 0; j < w; j++)
-                rl[j] = 0;
-            r[i] = rl;
-        }
-        return r;
-    };
-
-    add_props(Matrix, {
-        idn(n) {
-            var r, i;
-            r = Matrix(n, n);
-            for(i = 0; i < n; i++)
-                r[i][i] = 1;
-            return r;
-        },
-        diag(a) {
-            var r, i, n;
-            n = a.length;
-            r = Matrix(n, n);
-            for(i = 0; i < n; i++)
-                r[i][i] = a[i];
-            return r;
-        },
-        hilbert(n) {
-            var i, j, r;
-            r = Matrix(n);
-            for(i = 0; i < n; i++) {
-                for(j = 0; j < n; j++) {
-                    r[i][j] = 1 / (1 + i + j);
-                }
-            }
-            return r;
-        },
-        trans(a) {
-            var h, w, r, i, j;
-            if (!Array.isArray(a))
-                throw TypeError("matrix expected");
-            h = a.length;
-            if (!Array.isArray(a[0])) {
-                w = 1;
-                r = Matrix(w, h);
-                for(i = 0; i < h; i++) {
-                    r[0][i] = a[i];
-                }
-            } else {
-                w = a[0].length;
-                r = Matrix(w, h);
-                for(i = 0; i < h; i++) {
-                    for(j = 0; j < w; j++) {
-                        r[j][i] = a[i][j];
-                    }
-                }
-            }
-            return r;
-        },
-        check_square(a) {
-            var a, n;
-            if (!Array.isArray(a))
-                throw TypeError("array expected");
-            n = a.length;
-            if (!Array.isArray(a[0]) || n != a[0].length)
-                throw TypeError("square matrix expected");
-            return n;
-        },
-        trace(a) {
-            var n, r, i;
-            n = Matrix.check_square(a);
-            r = a[0][0];
-            for(i = 1; i < n; i++) {
-                r += a[i][i];
-            }
-            return r;
-        },
-        charpoly(a) {
-            var n, p, c, i, j, coef;
-            n = Matrix.check_square(a);
-            p = [];
-            for(i = 0; i < n + 1; i++)
-                p[i] = 0;
-            p[n] = 1;
-            c = Matrix.idn(n);
-            for(i = 0; i < n; i++) {
-                c = c * a;
-                coef = -trace(c) / (i + 1);
-                p[n - i - 1] = coef;
-                for(j = 0; j < n; j++)
-                    c[j][j] += coef;
-            }
-            return Polynomial(p);
-        },
-        eigenvals(a) {
-            return Polynomial.roots(Matrix.charpoly(a));
-        },
-        det(a) {
-            var n, i, j, k, s, src, v, c;
-
-            n = Matrix.check_square(a);
-            s = 1;
-            src = a.dup();
-            for(i=0;i<n;i++) {
-                for(j = i; j < n; j++) {
-                    if (src[j][i] != 0)
-                        break;
-                }
-                if (j == n)
-                    return 0;
-                if (j != i) {
-                    for(k = 0;k < n; k++) {
-                        v = src[j][k];
-                        src[j][k] = src[i][k];
-                        src[i][k] = v;
-                    }
-                    s = -s;
-                }
-                c = src[i][i].inverse();
-                for(j = i + 1; j < n; j++) {
-                    v = c * src[j][i];
-                    for(k = 0;k < n; k++) {
-                        src[j][k] -= src[i][k] * v;
-                    }
-                }
-            }
-            c = s;
-            for(i=0;i<n;i++)
-                c *= src[i][i];
-            return c;
-        },
-        inverse(a) {
-            var n, dst, src, i, j, k, n2, r, c, v;
-            n = Matrix.check_square(a);
-            src = a.dup();
-            dst = Matrix.idn(n);
-            for(i=0;i<n;i++) {
-                for(j = i; j < n; j++) {
-                    if (src[j][i] != 0)
-                        break;
-                }
-                if (j == n)
-                    throw RangeError("matrix is not invertible");
-                if (j != i) {
-                    /* swap lines in src and dst */
-                    v = src[j];
-                    src[j] = src[i];
-                    src[i] = v;
-                    v = dst[j];
-                    dst[j] = dst[i];
-                    dst[i] = v;
-                }
-
-                c = src[i][i].inverse();
-                for(k = 0; k < n; k++) {
-                    src[i][k] *= c;
-                    dst[i][k] *= c;
-                }
-
-                for(j = 0; j < n; j++) {
-                    if (j != i) {
-                        c = src[j][i];
-                        for(k = i; k < n; k++) {
-                            src[j][k] -= src[i][k] * c;
-                        }
-                        for(k = 0; k < n; k++) {
-                            dst[j][k] -= dst[i][k] * c;
-                        }
-                    }
-                }
-            }
-            return dst;
-        },
-        rank(a) {
-            var src, i, j, k, w, h, l, c;
-
-            if (!Array.isArray(a) ||
-                !Array.isArray(a[0]))
-                throw TypeError("matrix expected");
-            h = a.length;
-            w = a[0].length;
-            src = a.dup();
-            l = 0;
-            for(i=0;i<w;i++) {
-                for(j = l; j < h; j++) {
-                    if (src[j][i] != 0)
-                        break;
-                }
-                if (j == h)
-                    continue;
-                if (j != l) {
-                    /* swap lines */
-                    for(k = 0; k < w; k++) {
-                        v = src[j][k];
-                        src[j][k] = src[l][k];
-                        src[l][k] = v;
-                    }
-                }
-
-                c = src[l][i].inverse();
-                for(k = 0; k < w; k++) {
-                    src[l][k] *= c;
-                }
-
-                for(j = l + 1; j < h; j++) {
-                    c = src[j][i];
-                    for(k = i; k < w; k++) {
-                        src[j][k] -= src[l][k] * c;
-                    }
-                }
-                l++;
-            }
-            return l;
-        },
-        ker(a) {
-            var src, i, j, k, w, h, l, m, r, im_cols, ker_dim, c;
-
-            if (!Array.isArray(a) ||
-                !Array.isArray(a[0]))
-                throw TypeError("matrix expected");
-            h = a.length;
-            w = a[0].length;
-            src = a.dup();
-            im_cols = [];
-            l = 0;
-            for(i=0;i<w;i++) {
-                im_cols[i] = false;
-                for(j = l; j < h; j++) {
-                    if (src[j][i] != 0)
-                        break;
-                }
-                if (j == h)
-                    continue;
-                im_cols[i] = true;
-                if (j != l) {
-                    /* swap lines */
-                    for(k = 0; k < w; k++) {
-                        v = src[j][k];
-                        src[j][k] = src[l][k];
-                        src[l][k] = v;
-                    }
-                }
-
-                c = src[l][i].inverse();
-                for(k = 0; k < w; k++) {
-                    src[l][k] *= c;
-                }
-
-                for(j = 0; j < h; j++) {
-                    if (j != l) {
-                        c = src[j][i];
-                        for(k = i; k < w; k++) {
-                            src[j][k] -= src[l][k] * c;
-                        }
-                    }
-                }
-                l++;
-                //        log_str("m=" + cval_toString(v1) + "\n");
-            }
-            //    log_str("im cols="+im_cols+"\n");
-
-            /* build the kernel vectors */
-            ker_dim = w - l;
-            r = Matrix(w, ker_dim);
-            k = 0;
-            for(i = 0; i < w; i++) {
-                if (!im_cols[i]) {
-                    /* select this column from the matrix */
-                    l = 0;
-                    m = 0;
-                    for(j = 0; j < w; j++) {
-                        if (im_cols[j]) {
-                            r[j][k] = -src[m][i];
-                            m++;
-                        } else {
-                            if (l == k) {
-                                r[j][k] = 1;
-                            } else {
-                                r[j][k] = 0;
-                            }
-                            l++;
-                        }
-                    }
-                    k++;
-                }
-            }
-            return r;
-        },
-        dp(a, b) {
-            var i, n, r;
-            n = a.length;
-            if (n != b.length)
-                throw TypeError("incompatible array length");
-            /* XXX: could do complex product */
-            r = 0;
-            for(i = 0; i < n; i++) {
-                r += a[i] * b[i];
-            }
-            return r;
-        },
-        /* cross product */
-        cp(v1, v2) {
-            var r;
-            if (v1.length != 3 || v2.length != 3)
-                throw TypeError("vectors must have 3 elements");
-            r = [];
-            r[0] = v1[1] * v2[2] - v1[2] * v2[1];
-            r[1] = v1[2] * v2[0] - v1[0] * v2[2];
-            r[2] = v1[0] * v2[1] - v1[1] * v2[0];
-            return r;
-        },
-    });
-
-    function array_add(a, b) {
-        var r, i, n;
-        n = a.length;
-        if (n != b.length)
-            throw TypeError("incompatible array size");
-        r = [];
-        for(i = 0; i < n; i++)
-            r[i] = a[i] + b[i];
-        return r;
-    }
-    function array_sub(a, b) {
-        var r, i, n;
-        n = a.length;
-        if (n != b.length)
-            throw TypeError("incompatible array size");
-        r = [];
-        for(i = 0; i < n; i++)
-            r[i] = a[i] - b[i];
-        return r;
-    }
-    function array_scalar_mul(a, b) {
-        var r, i, n;
-        n = a.length;
-        r = [];
-        for(i = 0; i < n; i++)
-            r[i] = a[i] * b;
-        return r;
-    }
-    function array_mul(a, b) {
-        var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat;
-        h = a.length;
-        a_mat = Array.isArray(a[0]);
-        if (a_mat) {
-            l = a[0].length;
-        } else {
-            l = 1;
-        }
-        if (l != b.length)
-            throw RangeError("incompatible matrix size");
-        b_mat = Array.isArray(b[0]);
-        if (b_mat)
-            w = b[0].length;
-        else
-            w = 1;
-        r = [];
-        if (a_mat && b_mat) {
-            for(i = 0; i < h; i++) {
-                rl = [];
-                for(j = 0; j < w; j++) {
-                    sum = 0;
-                    for(k = 0; k < l; k++) {
-                        sum += a[i][k] * b[k][j];
-                    }
-                    rl[j] = sum;
-                }
-                r[i] = rl;
-            }
-        } else if (a_mat && !b_mat) {
-            for(i = 0; i < h; i++) {
-                sum = 0;
-                for(k = 0; k < l; k++) {
-                    sum += a[i][k] * b[k];
-                }
-                r[i] = sum;
-            }
-        } else if (!a_mat && b_mat) {
-            for(i = 0; i < h; i++) {
-                rl = [];
-                for(j = 0; j < w; j++) {
-                    rl[j] = a[i] * b[0][j];
-                }
-                r[i] = rl;
-            }
-        } else {
-            for(i = 0; i < h; i++) {
-                r[i] = a[i] * b[0];
-            }
-        }
-        return r;
-    }
-    function array_div(a, b) {
-        return array_mul(a, b.inverse());
-    }
-    function array_element_wise_inverse(a) {
-        var r, i, n;
-        n = a.length;
-        r = [];
-        for(i = 0; i < n; i++)
-            r[i] = a[i].inverse();
-        return r;
-    }
-    function array_eq(a, b) {
-        var n, i;
-        n = a.length;
-        if (n != b.length)
-            return false;
-        for(i = 0; i < n; i++) {
-            if (a[i] != b[i])
-                return false;
-        }
-        return true;
-    }
-
-    operators_set(Array.prototype,
-        {
-            "+": array_add,
-            "-": array_sub,
-            "*": array_mul,
-            "/": array_div,
-            "==": array_eq,
-            "pos"(a) {
-                return a;
-            },
-            "neg"(a) {
-                var i, n, r;
-                n = a.length;
-                r = [];
-                for(i = 0; i < n; i++)
-                    r[i] = -a[i];
-                return r;
-            }
-        },
-        {
-            right: [Number, BigInt, Float, Fraction, Complex, Mod,
-                    Polynomial, PolyMod, RationalFunction, Series],
-            "*": array_scalar_mul,
-            "/"(a, b) { return a * b.inverse(); },
-            "**": generic_pow, /* XXX: only for integer */
-        },
-        {
-            left: [Number, BigInt, Float, Fraction, Complex, Mod,
-                   Polynomial, PolyMod, RationalFunction, Series],
-            "*"(a, b) { return array_scalar_mul(b, a); },
-            "/"(a, b) { return a * array_element_wise_inverse(b); },
-        });
-
-    add_props(Array.prototype, {
-        conj() {
-            var i, n, r;
-            n = this.length;
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = this[i].conj();
-            return r;
-        },
-        dup() {
-            var r, i, n, el, a = this;
-            r = [];
-            n = a.length;
-            for(i = 0; i < n; i++) {
-                el = a[i];
-                if (Array.isArray(el))
-                    el = el.dup();
-                r[i] = el;
-            }
-            return r;
-        },
-        inverse() {
-            return Matrix.inverse(this);
-        },
-        norm2: Polynomial.prototype.norm2,
-    });
-
-})(this);
-
-/* global definitions */
-var I = Complex(0, 1);
-var X = Polynomial([0, 1]);
-var O = Series.O;
-
-Object.defineProperty(this, "PI", { get: function () { return Float.PI } });
-
-/* put frequently used functions in the global context */
-var gcd = Integer.gcd;
-var fact = Integer.fact;
-var comb = Integer.comb;
-var pmod = Integer.pmod;
-var invmod = Integer.invmod;
-var factor = Integer.factor;
-var isprime = Integer.isPrime;
-var nextprime = Integer.nextPrime;
-
-function deriv(a)
-{
-    return a.deriv();
-}
-
-function integ(a)
-{
-    return a.integ();
-}
-
-function norm2(a)
-{
-    return a.norm2();
-}
-
-function abs(a)
-{
-    return a.abs();
-}
-
-function conj(a)
-{
-    return a.conj();
-}
-
-function arg(a)
-{
-    return a.arg();
-}
-
-function inverse(a)
-{
-    return a.inverse();
-}
-
-function trunc(a)
-{
-    if (Integer.isInteger(a)) {
-        return a;
-    } else if (a instanceof Fraction) {
-        return Integer.tdiv(a.num, a.den);
-    } else if (a instanceof Polynomial) {
-        return a;
-    } else if (a instanceof RationalFunction) {
-        return Polynomial.divrem(a.num, a.den)[0];
-    } else {
-        return Float.ceil(a);
-    }
-}
-
-function floor(a)
-{
-    if (Integer.isInteger(a)) {
-        return a;
-    } else if (a instanceof Fraction) {
-        return Integer.fdiv(a.num, a.den);
-    } else {
-        return Float.floor(a);
-    }
-}
-
-function ceil(a)
-{
-    if (Integer.isInteger(a)) {
-        return a;
-    } else if (a instanceof Fraction) {
-        return Integer.cdiv(a.num, a.den);
-    } else {
-        return Float.ceil(a);
-    }
-}
-
-function sqrt(a)
-{
-    var t, u, re, im;
-    if (a instanceof Series) {
-        return a ^ (1/2);
-    } else if (a instanceof Complex) {
-        t = abs(a);
-        u = a.re;
-        re = sqrt((t + u) / 2);
-        im = sqrt((t - u) / 2);
-        if (a.im < 0)
-            im = -im;
-        return Complex.toComplex(re, im);
-    } else {
-        a = Float(a);
-        if (a < 0) {
-            return Complex(0, Float.sqrt(-a));
-        } else {
-            return Float.sqrt(a);
-        }
-    }
-}
-
-function exp(a)
-{
-    return a.exp();
-}
-
-function log(a)
-{
-    return a.log();
-}
-
-function log2(a)
-{
-    return log(a) * Float.LOG2E;
-}
-
-function log10(a)
-{
-    return log(a) * Float.LOG10E;
-}
-
-function todb(a)
-{
-    return log10(a) * 10;
-}
-
-function fromdb(a)
-{
-    return 10 ^ (a / 10);
-}
-
-function sin(a)
-{
-    var t;
-    if (a instanceof Complex || a instanceof Series) {
-        t = exp(a * I);
-        return (t - 1/t) / (2 * I);
-    } else {
-        return Float.sin(Float(a));
-    }
-}
-
-function cos(a)
-{
-    var t;
-    if (a instanceof Complex || a instanceof Series) {
-        t = exp(a * I);
-        return (t + 1/t) / 2;
-    } else {
-        return Float.cos(Float(a));
-    }
-}
-
-function tan(a)
-{
-    if (a instanceof Complex || a instanceof Series) {
-        return sin(a) / cos(a);
-    } else {
-        return Float.tan(Float(a));
-    }
-}
-
-function asin(a)
-{
-    return Float.asin(Float(a));
-}
-
-function acos(a)
-{
-    return Float.acos(Float(a));
-}
-
-function atan(a)
-{
-    return Float.atan(Float(a));
-}
-
-function atan2(a, b)
-{
-    return Float.atan2(Float(a), Float(b));
-}
-
-function sinc(a)
-{
-    if (a == 0) {
-        return 1;
-    } else {
-        a *= Float.PI;
-        return sin(a) / a;
-    }
-}
-
-function todeg(a)
-{
-    return a * 180 / Float.PI;
-}
-
-function fromdeg(a)
-{
-    return a * Float.PI / 180;
-}
-
-function sinh(a)
-{
-    var e = Float.exp(Float(a));
-    return (e - 1/e) * 0.5;
-}
-
-function cosh(a)
-{
-    var e = Float.exp(Float(a));
-    return (e + 1/e) * 0.5;
-}
-
-function tanh(a)
-{
-    var e = Float.exp(Float(a) * 2);
-    return (e - 1) / (e + 1);
-}
-
-function asinh(a)
-{
-    var x = Float(a);
-    return log(sqrt(x * x + 1) + x);
-}
-
-function acosh(a)
-{
-    var x = Float(a);
-    return log(sqrt(x * x - 1) + x);
-}
-
-function atanh(a)
-{
-    var x = Float(a);
-    return 0.5 * log((1 + x) / (1 - x));
-}
-
-function sigmoid(x)
-{
-    x = Float(x);
-    return 1 / (1 + exp(-x));
-}
-
-function lerp(a, b, t)
-{
-    return a + (b - a) * t;
-}
-
-var idn = Matrix.idn;
-var diag = Matrix.diag;
-var trans = Matrix.trans;
-var trace = Matrix.trace;
-var charpoly = Matrix.charpoly;
-var eigenvals = Matrix.eigenvals;
-var det = Matrix.det;
-var rank = Matrix.rank;
-var ker = Matrix.ker;
-var cp = Matrix.cp;
-var dp = Matrix.dp;
-
-var polroots = Polynomial.roots;
-var bestappr = Float.bestappr;
index f4d5838d4fcca6f0a5d4044f0d9994114a38f470..628588ec6c60ed4317f54fa8d6b4aa51e4f947d5 100644 (file)
@@ -172,13 +172,6 @@ DEF(status, "status")
 DEF(reason, "reason")
 DEF(globalThis, "globalThis")
 DEF(bigint, "bigint")
-#ifdef CONFIG_BIGNUM
-DEF(bigfloat, "bigfloat")
-DEF(bigdecimal, "bigdecimal")
-DEF(roundingMode, "roundingMode")
-DEF(maximumSignificantDigits, "maximumSignificantDigits")
-DEF(maximumFractionDigits, "maximumFractionDigits")
-#endif
 /* the following 3 atoms are only used with CONFIG_ATOMICS */
 DEF(not_equal, "not-equal")
 DEF(timed_out, "timed-out")
@@ -217,13 +210,6 @@ DEF(Float32Array, "Float32Array")
 DEF(Float64Array, "Float64Array")
 DEF(DataView, "DataView")
 DEF(BigInt, "BigInt")
-#ifdef CONFIG_BIGNUM
-DEF(BigFloat, "BigFloat")
-DEF(BigFloatEnv, "BigFloatEnv")
-DEF(BigDecimal, "BigDecimal")
-DEF(OperatorSet, "OperatorSet")
-DEF(Operators, "Operators")
-#endif
 DEF(Map, "Map")
 DEF(Set, "Set") /* Map + 1 */
 DEF(WeakMap, "WeakMap") /* Map + 2 */
@@ -266,8 +252,5 @@ DEF(Symbol_hasInstance, "Symbol.hasInstance")
 DEF(Symbol_species, "Symbol.species")
 DEF(Symbol_unscopables, "Symbol.unscopables")
 DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
-#ifdef CONFIG_BIGNUM
-DEF(Symbol_operatorSet, "Symbol.operatorSet")
-#endif
 
 #endif /* DEF */
index 1e1821259fdc9739df47a011301d12508ca4fd10..02ef4a78337433fbcdb725f27e0a19c68f427448 100644 (file)
@@ -258,10 +258,7 @@ DEF(            xor, 1, 2, 1, none)
 DEF(             or, 1, 2, 1, none)
 DEF(is_undefined_or_null, 1, 1, 1, none)
 DEF(     private_in, 1, 2, 1, none)
-#ifdef CONFIG_BIGNUM
-DEF(      mul_pow10, 1, 2, 1, none)
-DEF(       math_mod, 1, 2, 1, none)
-#endif
+DEF(push_bigint_i32, 5, 0, 1, i32)
 /* must be the last non short and non temporary opcode */
 DEF(            nop, 1, 0, 0, none)
 
index 7537fb7b64d6977a95507b522dba00386bd48e4c..1003dd45842350f2ffbbac7980bfa7b64419fb73 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -1,8 +1,8 @@
 /*
  * QuickJS Javascript Engine
  *
- * Copyright (c) 2017-2021 Fabrice Bellard
- * Copyright (c) 2017-2021 Charlie Gordon
+ * Copyright (c) 2017-2025 Fabrice Bellard
+ * Copyright (c) 2017-2025 Charlie Gordon
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -45,7 +45,6 @@
 #include "quickjs.h"
 #include "libregexp.h"
 #include "libunicode.h"
-#include "libbf.h"
 
 #define OPTIMIZE         1
 #define SHORT_OPCODES    1
@@ -150,12 +149,6 @@ enum {
     JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */
     JS_CLASS_DATAVIEW,          /* u.typed_array */
     JS_CLASS_BIG_INT,           /* u.object_data */
-#ifdef CONFIG_BIGNUM
-    JS_CLASS_BIG_FLOAT,         /* u.object_data */
-    JS_CLASS_FLOAT_ENV,         /* u.float_env */
-    JS_CLASS_BIG_DECIMAL,       /* u.object_data */
-    JS_CLASS_OPERATOR_SET,      /* u.operator_set */
-#endif
     JS_CLASS_MAP,               /* u.map_state */
     JS_CLASS_SET,               /* u.map_state */
     JS_CLASS_WEAKMAP,           /* u.map_state */
@@ -216,24 +209,6 @@ typedef enum {
 
 typedef enum OPCodeEnum OPCodeEnum;
 
-/* function pointers are used for numeric operations so that it is
-   possible to remove some numeric types */
-typedef struct {
-    JSValue (*to_string)(JSContext *ctx, JSValueConst val);
-    JSValue (*from_string)(JSContext *ctx, const char *buf,
-                           int radix, int flags, slimb_t *pexponent);
-    int (*unary_arith)(JSContext *ctx,
-                       JSValue *pres, OPCodeEnum op, JSValue op1);
-    int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
-                        JSValue *pres, JSValue op1, JSValue op2);
-    int (*compare)(JSContext *ctx, OPCodeEnum op,
-                   JSValue op1, JSValue op2);
-    /* only for bigfloat: */
-    JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
-                                    int64_t exponent);
-    int (*mul_pow10)(JSContext *ctx, JSValue *sp);
-} JSNumericOperations;
-
 struct JSRuntime {
     JSMallocFunctions mf;
     JSMallocState malloc_state;
@@ -296,13 +271,6 @@ struct JSRuntime {
     int shape_hash_size;
     int shape_hash_count; /* number of hashed shapes */
     JSShape **shape_hash;
-    bf_context_t bf_ctx;
-    JSNumericOperations bigint_ops;
-#ifdef CONFIG_BIGNUM
-    JSNumericOperations bigfloat_ops;
-    JSNumericOperations bigdecimal_ops;
-    uint32_t operator_count;
-#endif
     void *user_opaque;
 };
 
@@ -377,26 +345,45 @@ typedef struct JSVarRef {
     };
 } JSVarRef;
 
-/* the same structure is used for big integers and big floats. Big
-   integers are never infinite or NaNs */
-typedef struct JSBigFloat {
-    JSRefCountHeader header; /* must come first, 32-bit */
-    bf_t num;
-} JSBigFloat;
+/* bigint */
 
-#ifdef CONFIG_BIGNUM
-typedef struct JSFloatEnv {
-    limb_t prec;
-    bf_flags_t flags;
-    unsigned int status;
-} JSFloatEnv;
+#if JS_LIMB_BITS == 32
+
+typedef int32_t js_slimb_t;
+typedef uint32_t js_limb_t;
+typedef int64_t js_sdlimb_t;
+typedef uint64_t js_dlimb_t;
+
+#define JS_LIMB_DIGITS 9
+
+#else
+
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+typedef int64_t js_slimb_t;
+typedef uint64_t js_limb_t;
+typedef int128_t js_sdlimb_t;
+typedef uint128_t js_dlimb_t;
+
+#define JS_LIMB_DIGITS 19
 
-typedef struct JSBigDecimal {
-    JSRefCountHeader header; /* must come first, 32-bit */
-    bfdec_t num;
-} JSBigDecimal;
 #endif
 
+typedef struct JSBigInt {
+    JSRefCountHeader header; /* must come first, 32-bit */
+    uint32_t len; /* number of limbs, >= 1 */
+    js_limb_t tab[]; /* two's complement representation, always
+                        normalized so that 'len' is the minimum
+                        possible length >= 1 */
+} JSBigInt;
+
+/* this bigint structure can hold a 64 bit integer */
+typedef struct {
+    JSBigInt big_int;
+    /* must come just after */
+    js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS];
+} JSBigIntBuf;
+    
 typedef enum {
     JS_AUTOINIT_ID_PROTOTYPE,
     JS_AUTOINIT_ID_MODULE_NS,
@@ -434,12 +421,7 @@ struct JSContext {
     JSValue global_var_obj; /* contains the global let/const definitions */
 
     uint64_t random_state;
-    bf_context_t *bf_ctx;   /* points to rt->bf_ctx, shared by all contexts */
-#ifdef CONFIG_BIGNUM
-    JSFloatEnv fp_env; /* global FP environment */
-    BOOL bignum_ext : 8; /* enable math mode */
-    BOOL allow_operator_overloading : 8;
-#endif
+
     /* when the counter reaches zero, JSRutime.interrupt_handler is called */
     int interrupt_counter;
 
@@ -911,10 +893,6 @@ struct JSObject {
         struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
         struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
         struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
-#ifdef CONFIG_BIGNUM
-        struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
-        struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
-#endif
         struct JSMapState *map_state;   /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
         struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
         struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
@@ -1097,11 +1075,6 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val,
 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
                                 JS_MarkFunc *mark_func);
-#ifdef CONFIG_BIGNUM
-static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
-static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
-                                 JS_MarkFunc *mark_func);
-#endif
 
 #define HINT_STRING  0
 #define HINT_NUMBER  1
@@ -1136,37 +1109,7 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
 static JSProperty *add_property(JSContext *ctx,
                                 JSObject *p, JSAtom prop, int prop_flags);
-static JSValue JS_NewBigInt(JSContext *ctx);
-static inline bf_t *JS_GetBigInt(JSValueConst val)
-{
-    JSBigFloat *p = JS_VALUE_GET_PTR(val);
-    return &p->num;
-}
-static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
-                                 BOOL convert_to_safe_integer);
-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
-static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
-#ifdef CONFIG_BIGNUM
-static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
-static JSValue JS_NewBigFloat(JSContext *ctx);
-static inline bf_t *JS_GetBigFloat(JSValueConst val)
-{
-    JSBigFloat *p = JS_VALUE_GET_PTR(val);
-    return &p->num;
-}
-static JSValue JS_NewBigDecimal(JSContext *ctx);
-static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
-{
-    JSBigDecimal *p = JS_VALUE_GET_PTR(val);
-    return &p->num;
-}
-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
-static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
-                                   BOOL allow_null_or_undefined);
-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
-#endif
 JSValue JS_ThrowOutOfMemory(JSContext *ctx);
 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
@@ -1337,13 +1280,6 @@ void *js_mallocz_rt(JSRuntime *rt, size_t size)
     return memset(ptr, 0, size);
 }
 
-/* called by libbf */
-static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
-{
-    JSRuntime *rt = opaque;
-    return js_realloc_rt(rt, ptr, size);
-}
-
 /* Throw out of memory in case of error */
 void *js_malloc(JSContext *ctx, size_t size)
 {
@@ -1503,12 +1439,6 @@ static JSClassShortDef const js_std_class_def[] = {
     { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */
     { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */
     { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark },      /* JS_CLASS_BIG_INT */
-#ifdef CONFIG_BIGNUM
-    { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_FLOAT */
-    { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL },      /* JS_CLASS_FLOAT_ENV */
-    { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_DECIMAL */
-    { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark },    /* JS_CLASS_OPERATOR_SET */
-#endif
     { JS_ATOM_Map, js_map_finalizer, js_map_mark },             /* JS_CLASS_MAP */
     { JS_ATOM_Set, js_map_finalizer, js_map_mark },             /* JS_CLASS_SET */
     { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKMAP */
@@ -1538,61 +1468,6 @@ static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
     return 0;
 }
 
-static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
-{
-    return JS_ThrowTypeError(ctx, "unsupported operation");
-}
-
-static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
-{
-    return JS_ThrowUnsupportedOperation(ctx);
-}
-
-static JSValue invalid_from_string(JSContext *ctx, const char *buf,
-                                   int radix, int flags, slimb_t *pexponent)
-{
-    return JS_NAN;
-}
-
-static int invalid_unary_arith(JSContext *ctx,
-                               JSValue *pres, OPCodeEnum op, JSValue op1)
-{
-    JS_FreeValue(ctx, op1);
-    JS_ThrowUnsupportedOperation(ctx);
-    return -1;
-}
-
-static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
-                                JSValue *pres, JSValue op1, JSValue op2)
-{
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    JS_ThrowUnsupportedOperation(ctx);
-    return -1;
-}
-
-static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
-                                            int64_t exponent)
-{
-    return JS_ThrowUnsupportedOperation(ctx);
-}
-
-static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
-{
-    JS_ThrowUnsupportedOperation(ctx);
-    return -1;
-}
-
-static void set_dummy_numeric_ops(JSNumericOperations *ops)
-{
-    ops->to_string = invalid_to_string;
-    ops->from_string = invalid_from_string;
-    ops->unary_arith = invalid_unary_arith;
-    ops->binary_arith = invalid_binary_arith;
-    ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
-    ops->mul_pow10 = invalid_mul_pow10;
-}
-
 #if !defined(CONFIG_STACK_CHECK)
 /* no stack limitation */
 static inline uintptr_t js_get_stack_pointer(void)
@@ -1640,13 +1515,6 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
     rt->malloc_state = ms;
     rt->malloc_gc_threshold = 256 * 1024;
 
-    bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
-    set_dummy_numeric_ops(&rt->bigint_ops);
-#ifdef CONFIG_BIGNUM
-    set_dummy_numeric_ops(&rt->bigfloat_ops);
-    set_dummy_numeric_ops(&rt->bigdecimal_ops);
-#endif
-
     init_list_head(&rt->context_list);
     init_list_head(&rt->gc_obj_list);
     init_list_head(&rt->gc_zero_ref_count_list);
@@ -2003,8 +1871,6 @@ void JS_FreeRuntime(JSRuntime *rt)
     }
     js_free_rt(rt, rt->class_array);
 
-    bf_context_end(&rt->bf_ctx);
-
 #ifdef DUMP_LEAKS
     /* only the atoms defined in JS_InitAtoms() should be left */
     {
@@ -2141,11 +2007,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
     }
     ctx->rt = rt;
     list_add_tail(&ctx->link, &rt->context_list);
-    ctx->bf_ctx = &rt->bf_ctx;
-#ifdef CONFIG_BIGNUM
-    ctx->fp_env.prec = 113;
-    ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
-#endif
     for(i = 0; i < rt->class_count; i++)
         ctx->class_proto[i] = JS_NULL;
     ctx->array_ctor = JS_NULL;
@@ -2375,19 +2236,6 @@ static inline BOOL is_strict_mode(JSContext *ctx)
     return (sf && (sf->js_mode & JS_MODE_STRICT));
 }
 
-#ifdef CONFIG_BIGNUM
-static inline BOOL is_math_mode(JSContext *ctx)
-{
-    JSStackFrame *sf = ctx->rt->current_stack_frame;
-    return (sf && (sf->js_mode & JS_MODE_MATH));
-}
-#else
-static inline BOOL is_math_mode(JSContext *ctx)
-{
-    return FALSE;
-}
-#endif
-
 /* JSAtom support */
 
 #define JS_ATOM_TAG_INT (1U << 31)
@@ -4832,10 +4680,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
     case JS_CLASS_SYMBOL:
     case JS_CLASS_DATE:
     case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_CLASS_BIG_FLOAT:
-    case JS_CLASS_BIG_DECIMAL:
-#endif
         p->u.object_data = JS_UNDEFINED;
         goto set_exotic;
     case JS_CLASS_REGEXP:
@@ -4895,10 +4739,6 @@ static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
         case JS_CLASS_SYMBOL:
         case JS_CLASS_DATE:
         case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
-        case JS_CLASS_BIG_FLOAT:
-        case JS_CLASS_BIG_DECIMAL:
-#endif
             return JS_DupValue(ctx, p->u.object_data);
         }
     }
@@ -4919,10 +4759,6 @@ static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
         case JS_CLASS_SYMBOL:
         case JS_CLASS_DATE:
         case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
-        case JS_CLASS_BIG_FLOAT:
-        case JS_CLASS_BIG_DECIMAL:
-#endif
             JS_FreeValue(ctx, p->u.object_data);
             p->u.object_data = val;
             return 0;
@@ -5560,24 +5396,11 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
         abort(); /* never freed here */
         break;
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-#endif
-        {
-            JSBigFloat *bf = JS_VALUE_GET_PTR(v);
-            bf_delete(&bf->num);
-            js_free_rt(rt, bf);
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_DECIMAL:
         {
-            JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
-            bfdec_delete(&bf->num);
-            js_free_rt(rt, bf);
+            JSBigInt *p = JS_VALUE_GET_PTR(v);
+            js_free_rt(rt, p);
         }
         break;
-#endif
     case JS_TAG_SYMBOL:
         {
             JSAtomStruct *p = JS_VALUE_GET_PTR(v);
@@ -5949,11 +5772,7 @@ static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
         compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
         break;
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-    case JS_TAG_BIG_DECIMAL:
-#endif
-        /* should track JSBigFloat usage */
+        /* should track JSBigInt usage */
         break;
     }
 }
@@ -6079,10 +5898,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
         case JS_CLASS_SYMBOL:            /* u.object_data */
         case JS_CLASS_DATE:              /* u.object_data */
         case JS_CLASS_BIG_INT:           /* u.object_data */
-#ifdef CONFIG_BIGNUM
-        case JS_CLASS_BIG_FLOAT:         /* u.object_data */
-        case JS_CLASS_BIG_DECIMAL:         /* u.object_data */
-#endif
             compute_value_size(p->u.object_data, hp);
             break;
         case JS_CLASS_C_FUNCTION:        /* u.cfunc */
@@ -6176,9 +5991,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
         case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */
         case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */
         case JS_CLASS_DATAVIEW:          /* u.typed_array */
-#ifdef CONFIG_BIGNUM
-        case JS_CLASS_FLOAT_ENV:         /* u.float_env */
-#endif
         case JS_CLASS_MAP:               /* u.map_state */
         case JS_CLASS_SET:               /* u.map_state */
         case JS_CLASS_WEAKMAP:           /* u.map_state */
@@ -6248,11 +6060,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
 
 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
 {
-    fprintf(fp, "QuickJS memory usage -- "
-#ifdef CONFIG_BIGNUM
-            "BigNum "
-#endif
-            CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
+    fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
             (int)sizeof(void *) * 8, s->malloc_limit);
 #if 1
     if (rt) {
@@ -6942,17 +6750,10 @@ int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
 {
     switch(JS_VALUE_GET_NORM_TAG(val)) {
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
         val = ctx->class_proto[JS_CLASS_BIG_INT];
         break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
-        break;
-#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         val = ctx->class_proto[JS_CLASS_NUMBER];
@@ -9979,27 +9780,27 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val)
             JS_FreeValue(ctx, val);
             return ret;
         }
+    case JS_TAG_SHORT_BIG_INT:
+        return JS_VALUE_GET_SHORT_BIG_INT(val) != 0;
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-#endif
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            BOOL ret;
-            ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
-            JS_FreeValue(ctx, val);
-            return ret;
-        }
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_DECIMAL:
         {
-            JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+            JSBigInt *p = JS_VALUE_GET_PTR(val);
             BOOL ret;
-            ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+            int i;
+            
+            /* fail safe: we assume it is not necessarily
+               normalized. Beginning from the MSB ensures that the
+               test is fast. */
+            ret = FALSE;
+            for(i = p->len - 1; i >= 0; i--) {
+                if (p->tab[i] != 0) {
+                    ret = TRUE;
+                    break;
+                }
+            }
             JS_FreeValue(ctx, val);
             return ret;
         }
-#endif
     case JS_TAG_OBJECT:
         {
             JSObject *p = JS_VALUE_GET_OBJ(val);
@@ -10059,1300 +9860,1290 @@ static inline int to_digit(int c)
         return 36;
 }
 
-/* XXX: remove */
-static double js_strtod(const char *str, int radix, BOOL is_float)
-{
-    double d;
-    int c;
+/* bigint support */
 
-    if (!is_float || radix != 10) {
-        const char *p = str;
-        uint64_t n_max, n;
-        int int_exp, is_neg;
+#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */
 
-        is_neg = 0;
-        if (*p == '-') {
-            is_neg = 1;
-            p++;
-        }
+/* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */
+#if JS_SHORT_BIG_INT_BITS == 32
+#define JS_SHORT_BIG_INT_MIN INT32_MIN
+#define JS_SHORT_BIG_INT_MAX INT32_MAX
+#elif JS_SHORT_BIG_INT_BITS == 64
+#define JS_SHORT_BIG_INT_MIN INT64_MIN
+#define JS_SHORT_BIG_INT_MAX INT64_MAX
+#else
+#error unsupported
+#endif
 
-        /* skip leading zeros */
-        while (*p == '0')
-            p++;
-        n = 0;
-        if (radix == 10)
-            n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
-        else
-            n_max = ((uint64_t)-1 - (radix - 1)) / radix;
-        /* XXX: could be more precise */
-        int_exp = 0;
-        while (*p != '\0') {
-            c = to_digit((uint8_t)*p);
-            if (c >= radix)
-                break;
-            if (n <= n_max) {
-                n = n * radix + c;
-            } else {
-                if (radix == 10)
-                    goto strtod_case;
-                int_exp++;
-            }
-            p++;
-        }
-        d = n;
-        if (int_exp != 0) {
-            d *= pow(radix, int_exp);
-        }
-        if (is_neg)
-            d = -d;
-    } else {
-    strtod_case:
-        d = strtod(str, NULL);
-    }
-    return d;
-}
+#define ADDC(res, carry_out, op1, op2, carry_in)        \
+do {                                                    \
+    js_limb_t __v, __a, __k, __k1;                      \
+    __v = (op1);                                        \
+    __a = __v + (op2);                                  \
+    __k1 = __a < __v;                                   \
+    __k = (carry_in);                                   \
+    __a = __a + __k;                                    \
+    carry_out = (__a < __k) | __k1;                     \
+    res = __a;                                          \
+} while (0)
 
-#define ATOD_INT_ONLY        (1 << 0)
-/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
-#define ATOD_ACCEPT_BIN_OCT  (1 << 2)
-/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
-#define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)
-/* accept _ between digits as a digit separator */
-#define ATOD_ACCEPT_UNDERSCORES  (1 << 5)
-/* allow a suffix to override the type */
-#define ATOD_ACCEPT_SUFFIX    (1 << 6)
-/* default type */
-#define ATOD_TYPE_MASK        (3 << 7)
-#define ATOD_TYPE_FLOAT64     (0 << 7)
-#define ATOD_TYPE_BIG_INT     (1 << 7)
-#ifdef CONFIG_BIGNUM
-#define ATOD_TYPE_BIG_FLOAT   (2 << 7)
-#define ATOD_TYPE_BIG_DECIMAL (3 << 7)
-/* assume bigint mode: floats are parsed as integers if no decimal
-   point nor exponent */
-#define ATOD_MODE_BIGINT      (1 << 9)
+#if JS_LIMB_BITS == 32
+/* a != 0 */
+static inline js_limb_t js_limb_clz(js_limb_t a)
+{
+    return clz32(a);
+}
+#else
+static inline js_limb_t js_limb_clz(js_limb_t a)
+{
+    return clz64(a);
+}
 #endif
-/* accept -0x1 */
-#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
 
-static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
-                                   int radix, int flags, slimb_t *pexponent)
+static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
+                     js_limb_t n, js_limb_t carry)
 {
-    bf_t a_s, *a = &a_s;
-    int ret;
-    JSValue val;
-    val = JS_NewBigInt(ctx);
-    if (JS_IsException(val))
-        return val;
-    a = JS_GetBigInt(val);
-    ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
-    if (ret & BF_ST_MEM_ERROR) {
-        JS_FreeValue(ctx, val);
-        return JS_ThrowOutOfMemory(ctx);
+    int i;
+    for(i = 0;i < n; i++) {
+        ADDC(res[i], carry, op1[i], op2[i], carry);
     }
-#ifdef CONFIG_BIGNUM
-    val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
-#else
-    val = JS_CompactBigInt1(ctx, val, FALSE);
-#endif
-    return val;
+    return carry;
 }
 
-#ifdef CONFIG_BIGNUM
-static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
-                                     int radix, int flags, slimb_t *pexponent)
+static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
+                        int n, js_limb_t carry)
 {
-    bf_t *a;
-    int ret;
-    JSValue val;
+    int i;
+    js_limb_t k, a, v, k1;
 
-    val = JS_NewBigFloat(ctx);
-    if (JS_IsException(val))
-        return val;
-    a = JS_GetBigFloat(val);
-    if (flags & ATOD_ACCEPT_SUFFIX) {
-        /* return the exponent to get infinite precision */
-        ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
-                       BF_RNDZ | BF_ATOF_EXPONENT);
-    } else {
-        ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
-                      ctx->fp_env.flags);
-    }
-    if (ret & BF_ST_MEM_ERROR) {
-        JS_FreeValue(ctx, val);
-        return JS_ThrowOutOfMemory(ctx);
+    k = carry;
+    for(i=0;i<n;i++) {
+        v = op1[i];
+        a = v - op2[i];
+        k1 = a > v;
+        v = a - k;
+        k = (v > a) | k1;
+        res[i] = v;
     }
-    return val;
+    return k;
 }
 
-static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
-                                       int radix, int flags, slimb_t *pexponent)
+/* compute 0 - op2. carry = 0 or 1. */
+static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n)
 {
-    bfdec_t *a;
-    int ret;
-    JSValue val;
+    int i;
+    js_limb_t v, carry;
 
-    val = JS_NewBigDecimal(ctx);
-    if (JS_IsException(val))
-        return val;
-    a = JS_GetBigDecimal(val);
-    ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
-                     BF_RNDZ | BF_ATOF_NO_NAN_INF);
-    if (ret & BF_ST_MEM_ERROR) {
-        JS_FreeValue(ctx, val);
-        return JS_ThrowOutOfMemory(ctx);
+    carry = 1;
+    for(i=0;i<n;i++) {
+        v = ~op2[i] + carry;
+        carry = v < carry;
+        res[i] = v;
     }
-    return val;
+    return carry;
 }
-#endif
 
-/* return an exception in case of memory error. Return JS_NAN if
-   invalid syntax */
-#ifdef CONFIG_BIGNUM
-static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
-                        int radix, int flags, slimb_t *pexponent)
-#else
-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
-                       int radix, int flags)
-#endif
+/* tabr[] = taba[] * b + l. Return the high carry */
+static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+                      js_limb_t b, js_limb_t l)
 {
-    const char *p, *p_start;
-    int sep, is_neg;
-    BOOL is_float, has_legacy_octal;
-    int atod_type = flags & ATOD_TYPE_MASK;
-    char buf1[64], *buf;
-    int i, j, len;
-    BOOL buf_allocated = FALSE;
-    JSValue val;
-
-    /* optional separator between digits */
-    sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
-    has_legacy_octal = FALSE;
+    js_limb_t i;
+    js_dlimb_t t;
 
-    p = str;
-    p_start = p;
-    is_neg = 0;
-    if (p[0] == '+') {
-        p++;
-        p_start++;
-        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
-            goto no_radix_prefix;
-    } else if (p[0] == '-') {
-        p++;
-        p_start++;
-        is_neg = 1;
-        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
-            goto no_radix_prefix;
-    }
-    if (p[0] == '0') {
-        if ((p[1] == 'x' || p[1] == 'X') &&
-            (radix == 0 || radix == 16)) {
-            p += 2;
-            radix = 16;
-        } else if ((p[1] == 'o' || p[1] == 'O') &&
-                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
-            p += 2;
-            radix = 8;
-        } else if ((p[1] == 'b' || p[1] == 'B') &&
-                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
-            p += 2;
-            radix = 2;
-        } else if ((p[1] >= '0' && p[1] <= '9') &&
-                   radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
-            int i;
-            has_legacy_octal = TRUE;
-            sep = 256;
-            for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
-                continue;
-            if (p[i] == '8' || p[i] == '9')
-                goto no_prefix;
-            p += 1;
-            radix = 8;
-        } else {
-            goto no_prefix;
-        }
-        /* there must be a digit after the prefix */
-        if (to_digit((uint8_t)*p) >= radix)
-            goto fail;
-    no_prefix: ;
-    } else {
- no_radix_prefix:
-        if (!(flags & ATOD_INT_ONLY) &&
-            (atod_type == ATOD_TYPE_FLOAT64
-#ifdef CONFIG_BIGNUM
-             || atod_type == ATOD_TYPE_BIG_FLOAT
-#endif
-             ) &&
-            strstart(p, "Infinity", &p)) {
-#ifdef CONFIG_BIGNUM
-            if (atod_type == ATOD_TYPE_BIG_FLOAT) {
-                bf_t *a;
-                val = JS_NewBigFloat(ctx);
-                if (JS_IsException(val))
-                    goto done;
-                a = JS_GetBigFloat(val);
-                bf_set_inf(a, is_neg);
-            } else
-#endif
-            {
-                double d = 1.0 / 0.0;
-                if (is_neg)
-                    d = -d;
-                val = JS_NewFloat64(ctx, d);
-            }
-            goto done;
-        }
-    }
-    if (radix == 0)
-        radix = 10;
-    is_float = FALSE;
-    p_start = p;
-    while (to_digit((uint8_t)*p) < radix
-           ||  (*p == sep && (radix != 10 ||
-                              p != p_start + 1 || p[-1] != '0') &&
-                to_digit((uint8_t)p[1]) < radix)) {
-        p++;
-    }
-    if (!(flags & ATOD_INT_ONLY)) {
-        if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
-            is_float = TRUE;
-            p++;
-            if (*p == sep)
-                goto fail;
-            while (to_digit((uint8_t)*p) < radix ||
-                   (*p == sep && to_digit((uint8_t)p[1]) < radix))
-                p++;
-        }
-        if (p > p_start &&
-            (((*p == 'e' || *p == 'E') && radix == 10) ||
-             ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
-            const char *p1 = p + 1;
-            is_float = TRUE;
-            if (*p1 == '+') {
-                p1++;
-            } else if (*p1 == '-') {
-                p1++;
-            }
-            if (is_digit((uint8_t)*p1)) {
-                p = p1 + 1;
-                while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
-                    p++;
-            }
-        }
+    for(i = 0; i < n; i++) {
+        t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l;
+        tabr[i] = t;
+        l = t >> JS_LIMB_BITS;
     }
-    if (p == p_start)
-        goto fail;
+    return l;
+}
 
-    buf = buf1;
-    buf_allocated = FALSE;
-    len = p - p_start;
-    if (unlikely((len + 2) > sizeof(buf1))) {
-        buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
-        if (!buf)
-            goto mem_error;
-        buf_allocated = TRUE;
-    }
-    /* remove the separators and the radix prefixes */
-    j = 0;
-    if (is_neg)
-        buf[j++] = '-';
-    for (i = 0; i < len; i++) {
-        if (p_start[i] != '_')
-            buf[j++] = p_start[i];
+static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+                      js_limb_t b, js_limb_t r)
+{
+    js_slimb_t i;
+    js_dlimb_t a1;
+    for(i = n - 1; i >= 0; i--) {
+        a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
+        tabr[i] = a1 / b;
+        r = a1 % b;
     }
-    buf[j] = '\0';
+    return r;
+}
 
-    if (flags & ATOD_ACCEPT_SUFFIX) {
-        if (*p == 'n') {
-            p++;
-            atod_type = ATOD_TYPE_BIG_INT;
-        } else
-#ifdef CONFIG_BIGNUM
-        if (*p == 'l') {
-            p++;
-            atod_type = ATOD_TYPE_BIG_FLOAT;
-        } else if (*p == 'm') {
-            p++;
-            atod_type = ATOD_TYPE_BIG_DECIMAL;
-        } else if (flags & ATOD_MODE_BIGINT) {
-            if (!is_float)
-                atod_type = ATOD_TYPE_BIG_INT;
-            if (has_legacy_octal)
-                goto fail;
-        } else
-#endif
-        {
-            if (is_float && radix != 10)
-                goto fail;
-        }
-    } else {
-        if (atod_type == ATOD_TYPE_FLOAT64) {
-#ifdef CONFIG_BIGNUM
-            if (flags & ATOD_MODE_BIGINT) {
-                if (!is_float)
-                    atod_type = ATOD_TYPE_BIG_INT;
-                if (has_legacy_octal)
-                    goto fail;
-            } else
-#endif
-            {
-                if (is_float && radix != 10)
-                    goto fail;
-            }
-        }
-    }
+/* tabr[] += taba[] * b, return the high word. */
+static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+                          js_limb_t b)
+{
+    js_limb_t i, l;
+    js_dlimb_t t;
 
-    switch(atod_type) {
-    case ATOD_TYPE_FLOAT64:
-        {
-            double d;
-            d = js_strtod(buf, radix, is_float);
-            /* return int or float64 */
-            val = JS_NewFloat64(ctx, d);
-        }
-        break;
-    case ATOD_TYPE_BIG_INT:
-        if (has_legacy_octal || is_float)
-            goto fail;
-        val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
-        break;
-#ifdef CONFIG_BIGNUM
-    case ATOD_TYPE_BIG_FLOAT:
-        if (has_legacy_octal)
-            goto fail;
-        val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
-                                                pexponent);
-        break;
-    case ATOD_TYPE_BIG_DECIMAL:
-        if (radix != 10)
-            goto fail;
-        val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
-        break;
-#endif
-    default:
-        abort();
+    l = 0;
+    for(i = 0; i < n; i++) {
+        t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i];
+        tabr[i] = t;
+        l = t >> JS_LIMB_BITS;
     }
+    return l;
+}
 
-done:
-    if (buf_allocated)
-        js_free_rt(ctx->rt, buf);
-    if (pp)
-        *pp = p;
-    return val;
- fail:
-    val = JS_NAN;
-    goto done;
- mem_error:
-    val = JS_ThrowOutOfMemory(ctx);
-    goto done;
+/* size of the result : op1_size + op2_size. */
+static void mp_mul_basecase(js_limb_t *result,
+                            const js_limb_t *op1, js_limb_t op1_size,
+                            const js_limb_t *op2, js_limb_t op2_size)
+{
+    int i;
+    js_limb_t r;
+    
+    result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
+    for(i=1;i<op2_size;i++) {
+        r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
+        result[i + op1_size] = r;
+    }
 }
 
-#ifdef CONFIG_BIGNUM
-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
-                       int radix, int flags)
+/* tabr[] -= taba[] * b. Return the value to substract to the high
+   word. */
+static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+                          js_limb_t b)
 {
-    return js_atof2(ctx, str, pp, radix, flags, NULL);
+    js_limb_t i, l;
+    js_dlimb_t t;
+
+    l = 0;
+    for(i = 0; i < n; i++) {
+        t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l;
+        tabr[i] = t;
+        l = -(t >> JS_LIMB_BITS);
+    }
+    return l;
+}
+
+/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */
+static inline js_limb_t udiv1norm_init(js_limb_t d)
+{
+    js_limb_t a0, a1;
+    a1 = -d - 1;
+    a0 = -1;
+    return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d;
+}
+
+/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0
+   / d' with 0 <= a1 < d. */
+static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0,
+                                js_limb_t d, js_limb_t d_inv)
+{
+    js_limb_t n1m, n_adj, q, r, ah;
+    js_dlimb_t a;
+    n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1));
+    n_adj = a0 + (n1m & d);
+    a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj;
+    q = (a >> JS_LIMB_BITS) + a1;
+    /* compute a - q * r and update q so that the remainder is\
+       between 0 and d - 1 */
+    a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0;
+    a = a - (js_dlimb_t)q * d - d;
+    ah = a >> JS_LIMB_BITS;
+    q += 1 + ah;
+    r = (js_limb_t)a + (ah & d);
+    *pr = r;
+    return q;
 }
-#endif
 
-typedef enum JSToNumberHintEnum {
-    TON_FLAG_NUMBER,
-    TON_FLAG_NUMERIC,
-} JSToNumberHintEnum;
+#define UDIV1NORM_THRESHOLD 3
 
-static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
-                                   JSToNumberHintEnum flag)
+/* b must be >= 1 << (JS_LIMB_BITS - 1) */
+static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+                          js_limb_t b, js_limb_t r)
 {
-    uint32_t tag;
-    JSValue ret;
+    js_slimb_t i;
 
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_BIG_INT:
-        if (flag != TON_FLAG_NUMERIC) {
-            JS_FreeValue(ctx, val);
-            return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+    if (n >= UDIV1NORM_THRESHOLD) {
+        js_limb_t b_inv;
+        b_inv = udiv1norm_init(b);
+        for(i = n - 1; i >= 0; i--) {
+            tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
         }
-        ret = val;
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_DECIMAL:
-        if (flag != TON_FLAG_NUMERIC) {
-            JS_FreeValue(ctx, val);
-            return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
+    } else {
+        js_dlimb_t a1;
+        for(i = n - 1; i >= 0; i--) {
+            a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
+            tabr[i] = a1 / b;
+            r = a1 % b;
         }
-        ret = val;
-        break;
-    case JS_TAG_BIG_FLOAT:
-        if (flag != TON_FLAG_NUMERIC) {
-            JS_FreeValue(ctx, val);
-            return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
+    }
+    return r;
+}
+
+/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
+   - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba'
+   is modified and contains the remainder (nb limbs). tabq[0..na-nb]
+   contains the quotient with tabq[na - nb] <= 1. */
+static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na,
+                       const js_limb_t *tabb, js_limb_t nb)
+{
+    js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
+    int i, j;
+
+    b1 = tabb[nb - 1];
+    if (nb == 1) {
+        taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
+        return;
+    }
+    n = na - nb;
+
+    if (n >= UDIV1NORM_THRESHOLD)
+        b1_inv = udiv1norm_init(b1);
+    else
+        b1_inv = 0;
+
+    /* first iteration: the quotient is only 0 or 1 */
+    q = 1;
+    for(j = nb - 1; j >= 0; j--) {
+        if (taba[n + j] != tabb[j]) {
+            if (taba[n + j] < tabb[j])
+                q = 0;
+            break;
         }
-        ret = val;
-        break;
-#endif
-    case JS_TAG_FLOAT64:
-    case JS_TAG_INT:
-    case JS_TAG_EXCEPTION:
-        ret = val;
-        break;
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
-        break;
-    case JS_TAG_UNDEFINED:
-        ret = JS_NAN;
-        break;
-    case JS_TAG_OBJECT:
-        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
-        if (JS_IsException(val))
-            return JS_EXCEPTION;
-        goto redo;
-    case JS_TAG_STRING:
-        {
-            const char *str;
-            const char *p;
-            size_t len;
+    }
+    tabq[n] = q;
+    if (q) {
+        mp_sub(taba + n, taba + n, tabb, nb, 0);
+    }
 
-            str = JS_ToCStringLen(ctx, &len, val);
-            JS_FreeValue(ctx, val);
-            if (!str)
-                return JS_EXCEPTION;
-            p = str;
-            p += skip_spaces(p);
-            if ((p - str) == len) {
-                ret = JS_NewInt32(ctx, 0);
-            } else {
-                int flags = ATOD_ACCEPT_BIN_OCT;
-                ret = js_atof(ctx, p, &p, 0, flags);
-                if (!JS_IsException(ret)) {
-                    p += skip_spaces(p);
-                    if ((p - str) != len) {
-                        JS_FreeValue(ctx, ret);
-                        ret = JS_NAN;
+    for(i = n - 1; i >= 0; i--) {
+        if (unlikely(taba[i + nb] >= b1)) {
+            q = -1;
+        } else if (b1_inv) {
+            q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
+        } else {
+            js_dlimb_t al;
+            al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1];
+            q = al / b1;
+            r = al % b1;
+        }
+        r = mp_sub_mul1(taba + i, tabb, nb, q);
+
+        v = taba[i + nb];
+        a = v - r;
+        c = (a > v);
+        taba[i + nb] = a;
+
+        if (c != 0) {
+            /* negative result */
+            for(;;) {
+                q--;
+                c = mp_add(taba + i, taba + i, tabb, nb, 0);
+                /* propagate carry and test if positive result */
+                if (c != 0) {
+                    if (++taba[i + nb] == 0) {
+                        break;
                     }
                 }
             }
-            JS_FreeCString(ctx, str);
         }
-        break;
-    case JS_TAG_SYMBOL:
-        JS_FreeValue(ctx, val);
-        return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
-    default:
-        JS_FreeValue(ctx, val);
-        ret = JS_NAN;
-        break;
+        tabq[i] = q;
     }
-    return ret;
 }
 
-static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
+/* 1 <= shift <= JS_LIMB_BITS - 1 */
+static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n,
+                        int shift)
 {
-    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
+    int i;
+    js_limb_t l, v;
+    l = 0;
+    for(i = 0; i < n; i++) {
+        v = taba[i];
+        tabr[i] = (v << shift) | l;
+        l = v >> (JS_LIMB_BITS - shift);
+    }
+    return l;
 }
 
-static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
+/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). 
+   1 <= shift <= LIMB_BITS - 1 */
+static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n,
+                        int shift, js_limb_t high)
 {
-    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
+    int i;
+    js_limb_t l, a;
+
+    l = high;
+    for(i = n - 1; i >= 0; i--) {
+        a = tab[i];
+        tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift));
+        l = a;
+    }
+    return l & (((js_limb_t)1 << shift) - 1);
 }
 
-static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+static JSBigInt *js_bigint_new(JSContext *ctx, int len)
 {
-    return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+    JSBigInt *r;
+    if (len > JS_BIGINT_MAX_SIZE) {
+        JS_ThrowRangeError(ctx, "BigInt is too large to allocate");
+        return NULL;
+    }
+    r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t));
+    if (!r)
+        return NULL;
+    r->header.ref_count = 1;
+    r->len = len;
+    return r;
 }
 
-static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
-                                          JSValue val)
+static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a)
 {
-    double d;
-    uint32_t tag;
+    JSBigInt *r = &buf->big_int;
+    r->len = 1;
+    r->tab[0] = a;
+    return r;
+}
 
-    val = JS_ToNumberFree(ctx, val);
-    if (JS_IsException(val)) {
-        *pres = JS_FLOAT64_NAN;
-        return -1;
-    }
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-        d = JS_VALUE_GET_INT(val);
-        break;
-    case JS_TAG_FLOAT64:
-        d = JS_VALUE_GET_FLOAT64(val);
-        break;
-    case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
+/* val must be a short big int */
+static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val)
+{
+    return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val));
+}
+
+static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str,
+                                           const js_limb_t *tab, int len)
+{
+    int i;
+    printf("%s: ", str);
+    for(i = len - 1; i >= 0; i--) {
+#if JS_LIMB_BITS == 32
+        printf(" %08x", tab[i]);
+#else
+        printf(" %016" PRIx64, tab[i]);
 #endif
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            /* XXX: there can be a double rounding issue with some
-               primitives (such as JS_ToUint8ClampFree()), but it is
-               not critical to fix it. */
-            bf_get_float64(&p->num, &d, BF_RNDN);
-            JS_FreeValue(ctx, val);
-        }
-        break;
-    default:
-        abort();
     }
-    *pres = d;
-    return 0;
+    printf("\n");
 }
 
-static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str,
+                                          const JSBigInt *p)
 {
-    uint32_t tag;
+    js_bigint_dump1(ctx, str, p->tab, p->len);
+}
 
-    tag = JS_VALUE_GET_TAG(val);
-    if (tag <= JS_TAG_NULL) {
-        *pres = JS_VALUE_GET_INT(val);
-        return 0;
-    } else if (JS_TAG_IS_FLOAT64(tag)) {
-        *pres = JS_VALUE_GET_FLOAT64(val);
-        return 0;
+static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a)
+{
+    JSBigInt *r;
+    r = js_bigint_new(ctx, 1);
+    if (!r)
+        return NULL;
+    r->tab[0] = a;
+    return r;
+}
+
+static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a)
+{
+#if JS_LIMB_BITS == 64
+    return js_bigint_new_si(ctx, a);
+#else
+    if (a >= INT32_MIN && a <= INT32_MAX) {
+        return js_bigint_new_si(ctx, a);
     } else {
-        return __JS_ToFloat64Free(ctx, pres, val);
+        JSBigInt *r;
+        r = js_bigint_new(ctx, 2);
+        if (!r)
+            return NULL;
+        r->tab[0] = a;
+        r->tab[1] = a >> 32;
+        return r;
     }
+#endif
 }
 
-int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
+static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a)
 {
-    return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+    if (a <= INT64_MAX) {
+        return js_bigint_new_si64(ctx, a);
+    } else {
+        JSBigInt *r;
+        r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS);
+        if (!r)
+            return NULL;
+#if JS_LIMB_BITS == 64
+        r->tab[0] = a;
+        r->tab[1] = 0;
+#else
+        r->tab[0] = a;
+        r->tab[1] = a >> 32;
+        r->tab[2] = 0;
+#endif
+        return r;
+    }
 }
 
-static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a)
 {
-    return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+    JSBigInt *r;
+    if (a == (js_slimb_t)a) {
+        r = js_bigint_new(ctx, 1);
+        if (!r)
+            return NULL;
+        r->tab[0] = a;
+    } else {
+        r = js_bigint_new(ctx, 2);
+        if (!r)
+            return NULL;
+        r->tab[0] = a;
+        r->tab[1] = a >> JS_LIMB_BITS;
+    }
+    return r;
 }
 
-/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
-static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+/* Remove redundant high order limbs. Warning: 'a' may be
+   reallocated. Can never fail.
+*/
+static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l)
 {
-    uint32_t tag;
-    JSValue ret;
-
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
-        break;
-    case JS_TAG_FLOAT64:
-        {
-            double d = JS_VALUE_GET_FLOAT64(val);
-            if (isnan(d)) {
-                ret = JS_NewInt32(ctx, 0);
-            } else {
-                /* convert -0 to +0 */
-                d = trunc(d) + 0.0;
-                ret = JS_NewFloat64(ctx, d);
-            }
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            bf_t a_s, *a, r_s, *r = &r_s;
-            BOOL is_nan;
+    js_limb_t v;
 
-            a = JS_ToBigFloat(ctx, &a_s, val);
-            if (!a) {
-                JS_FreeValue(ctx, val);
-                return JS_EXCEPTION;
-            }
-            if (!bf_is_finite(a)) {
-                is_nan = bf_is_nan(a);
-                if (is_nan)
-                    ret = JS_NewInt32(ctx, 0);
-                else
-                    ret = JS_DupValue(ctx, val);
-            } else {
-                ret = JS_NewBigInt(ctx);
-                if (!JS_IsException(ret)) {
-                    r = JS_GetBigInt(ret);
-                    bf_set(r, a);
-                    bf_rint(r, BF_RNDZ);
-                    ret = JS_CompactBigInt(ctx, ret);
-                }
-            }
-            if (a == &a_s)
-                bf_delete(a);
-            JS_FreeValue(ctx, val);
+    assert(a->header.ref_count == 1);
+    while (l > 1) {
+        v = a->tab[l - 1];
+        if ((v != 0 && v != -1) ||
+            (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) {
+            break;
         }
-        break;
-#endif
-    default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val))
-            return val;
-        goto redo;
+        l--;
     }
-    return ret;
+    if (l != a->len) {
+        JSBigInt *a1;
+        /* realloc to reduce the size */
+        a->len = l;
+        a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t));
+        if (a1)
+            a = a1;
+    }
+    return a;
 }
 
-/* Note: the integer value is satured to 32 bits */
-static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
+static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a)
 {
-    uint32_t tag;
-    int ret;
-
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        ret = JS_VALUE_GET_INT(val);
-        break;
-    case JS_TAG_EXCEPTION:
-        *pres = 0;
-        return -1;
-    case JS_TAG_FLOAT64:
-        {
-            double d = JS_VALUE_GET_FLOAT64(val);
-            if (isnan(d)) {
-                ret = 0;
-            } else {
-                if (d < INT32_MIN)
-                    ret = INT32_MIN;
-                else if (d > INT32_MAX)
-                    ret = INT32_MAX;
-                else
-                    ret = (int)d;
-            }
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_get_int32(&ret, &p->num, 0);
-            JS_FreeValue(ctx, val);
-        }
-        break;
-#endif
-    default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val)) {
-            *pres = 0;
-            return -1;
-        }
-        goto redo;
-    }
-    *pres = ret;
-    return 0;
+    return js_bigint_normalize1(ctx, a, a->len);
 }
 
-int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+/* return 0 or 1 depending on the sign */
+static inline int js_bigint_sign(const JSBigInt *a)
 {
-    return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+    return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1);
 }
 
-int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
-                    int min, int max, int min_offset)
+static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a)
 {
-    int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
-    if (res == 0) {
-        if (*pres < min) {
-            *pres += min_offset;
-            if (*pres < min)
-                *pres = min;
-        } else {
-            if (*pres > max)
-                *pres = max;
-        }
+    if (a->len == 1) {
+        return a->tab[0];
+    } else {
+#if JS_LIMB_BITS == 32
+        if (js_bigint_sign(a))
+            return INT32_MIN;
+        else
+            return INT32_MAX;
+#else
+        if (js_bigint_sign(a))
+            return INT64_MIN;
+        else
+            return INT64_MAX;
+#endif
     }
-    return res;
 }
 
-static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
+/* add the op1 limb */
+static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r,
+                                  js_limb_t op1)
 {
-    uint32_t tag;
-
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        *pres = JS_VALUE_GET_INT(val);
-        return 0;
-    case JS_TAG_EXCEPTION:
-        *pres = 0;
-        return -1;
-    case JS_TAG_FLOAT64:
-        {
-            double d = JS_VALUE_GET_FLOAT64(val);
-            if (isnan(d)) {
-                *pres = 0;
-            } else {
-                if (d < INT64_MIN)
-                    *pres = INT64_MIN;
-                else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
-                    *pres = INT64_MAX;
-                else
-                    *pres = (int64_t)d;
-            }
+    int n2 = r->len;
+    if ((op1 != 0 && op1 != -1) ||
+        (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) {
+        JSBigInt *r1;
+        r1 = js_realloc(ctx, r,
+                        sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t));
+        if (!r1) {
+            js_free(ctx, r);
+            return NULL;
         }
-        return 0;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_get_int64(pres, &p->num, 0);
-            JS_FreeValue(ctx, val);
+        r = r1;
+        r->len = n2 + 1;
+        r->tab[n2] = op1;
+    } else {
+        /* otherwise still need to normalize the result */
+        r = js_bigint_normalize(ctx, r);
+    }
+    return r;
+}
+
+/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b
+   (b_neg = 1) */
+/* XXX: optimize */
+static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a,
+                               const JSBigInt *b, int b_neg)
+{
+    JSBigInt *r;
+    int n1, n2, i;
+    js_limb_t carry, op1, op2, a_sign, b_sign;
+    
+    n2 = max_int(a->len, b->len);
+    n1 = min_int(a->len, b->len);
+    r = js_bigint_new(ctx, n2);
+    if (!r)
+        return NULL;
+    /* XXX: optimize */
+    /* common part */
+    carry = b_neg;
+    for(i = 0; i < n1; i++) {
+        op1 = a->tab[i];
+        op2 = b->tab[i] ^ (-b_neg);
+        ADDC(r->tab[i], carry, op1, op2, carry);
+    }
+    a_sign = -js_bigint_sign(a);
+    b_sign = (-js_bigint_sign(b)) ^ (-b_neg);
+    /* part with sign extension of one operand  */
+    if (a->len > b->len) {
+        for(i = n1; i < n2; i++) {
+            op1 = a->tab[i];
+            ADDC(r->tab[i], carry, op1, b_sign, carry);
         }
-        return 0;
-#endif
-    default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val)) {
-            *pres = 0;
-            return -1;
+    } else if (a->len < b->len) {
+        for(i = n1; i < n2; i++) {
+            op2 = b->tab[i] ^ (-b_neg);
+            ADDC(r->tab[i], carry, a_sign, op2, carry);
         }
-        goto redo;
     }
+
+    /* part with sign extension for both operands. Extend the result
+       if necessary */
+    return js_bigint_extend(ctx, r, a_sign + b_sign + carry);
 }
 
-int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
+/* XXX: optimize */
+static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a)
 {
-    return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+    JSBigIntBuf buf;
+    JSBigInt *b;
+    b = js_bigint_set_si(&buf, 0);
+    return js_bigint_add(ctx, b, a, 1);
 }
 
-int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
-                    int64_t min, int64_t max, int64_t neg_offset)
+static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a,
+                               const JSBigInt *b)
 {
-    int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
-    if (res == 0) {
-        if (*pres < 0)
-            *pres += neg_offset;
-        if (*pres < min)
-            *pres = min;
-        else if (*pres > max)
-            *pres = max;
+    JSBigInt *r;
+    
+    r = js_bigint_new(ctx, a->len + b->len);
+    if (!r)
+        return NULL;
+    mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len);
+    /* correct the result if negative operands (no overflow is
+       possible) */
+    if (js_bigint_sign(a))
+        mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0);
+    if (js_bigint_sign(b))
+        mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0);
+    return js_bigint_normalize(ctx, r);
+}
+
+/* return the division or the remainder. 'b' must be != 0. return NULL
+   in case of exception (division by zero or memory error) */
+static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a,
+                                  const JSBigInt *b, BOOL is_rem)
+{
+    JSBigInt *r, *q;
+    js_limb_t *tabb, h;
+    int na, nb, a_sign, b_sign, shift;
+    
+    if (b->len == 1 && b->tab[0] == 0) {
+        JS_ThrowRangeError(ctx, "BigInt division by zero");
+        return NULL;
+    }
+    
+    a_sign = js_bigint_sign(a);
+    b_sign = js_bigint_sign(b);
+    na = a->len;
+    nb = b->len;
+
+    r = js_bigint_new(ctx, na + 2); 
+    if (!r)
+        return NULL;
+    if (a_sign) {
+        mp_neg(r->tab, a->tab, na);
+    } else {
+        memcpy(r->tab, a->tab, na * sizeof(a->tab[0]));
+    }
+    /* normalize */
+    while (na > 1 && r->tab[na - 1] == 0)
+        na--;
+
+    tabb = js_malloc(ctx, nb * sizeof(tabb[0]));
+    if (!tabb) {
+        js_free(ctx, r);
+        return NULL;
+    }
+    if (b_sign) {
+        mp_neg(tabb, b->tab, nb);
+    } else {
+        memcpy(tabb, b->tab, nb * sizeof(tabb[0]));
+    }
+    /* normalize */
+    while (nb > 1 && tabb[nb - 1] == 0)
+        nb--;
+
+    /* trivial case if 'a' is small */
+    if (na < nb) {
+        js_free(ctx, r);
+        js_free(ctx, tabb);
+        if (is_rem) {
+            /* r = a */
+            r = js_bigint_new(ctx, a->len);
+            if (!r)
+                return NULL;
+            memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); 
+            return r;
+        } else {
+            /* q = 0 */
+            return js_bigint_new_si(ctx, 0);
+        }
+    }
+
+    /* normalize 'b' */
+    shift = js_limb_clz(tabb[nb - 1]);
+    if (shift != 0) {
+        mp_shl(tabb, tabb, nb, shift);
+        h = mp_shl(r->tab, r->tab, na, shift);
+        if (h != 0)
+            r->tab[na++] = h;
+    }
+
+    q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */
+    if (!q) {
+        js_free(ctx, r);
+        js_free(ctx, tabb);
+        return NULL;
+    }
+
+    //    js_bigint_dump1(ctx, "a", r->tab, na);
+    //    js_bigint_dump1(ctx, "b", tabb, nb);
+    mp_divnorm(q->tab, r->tab, na, tabb, nb);
+    js_free(ctx, tabb);
+
+    if (is_rem) {
+        js_free(ctx, q);
+        if (shift != 0)
+            mp_shr(r->tab, r->tab, nb, shift, 0);
+        r->tab[nb++] = 0;
+        if (a_sign)
+            mp_neg(r->tab, r->tab, nb);
+        r = js_bigint_normalize1(ctx, r, nb);
+        return r;
+    } else {
+        js_free(ctx, r);
+        q->tab[na - nb + 1] = 0;
+        if (a_sign ^ b_sign) {
+            mp_neg(q->tab, q->tab, q->len);
+        }
+        q = js_bigint_normalize(ctx, q);
+        return q;
     }
-    return res;
 }
 
-/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
-   in case of exception */
-static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+/* and, or, xor */
+static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a,
+                                 const JSBigInt *b, OPCodeEnum op)
 {
-    uint32_t tag;
-    int64_t ret;
+    JSBigInt *r;
+    js_limb_t b_sign;
+    int a_len, b_len, i;
 
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        ret = JS_VALUE_GET_INT(val);
+    if (a->len < b->len) {
+        const JSBigInt *tmp;
+        tmp = a;
+        a = b;
+        b = tmp;
+    }
+    /* a_len >= b_len */
+    a_len = a->len;
+    b_len = b->len;
+    b_sign = -js_bigint_sign(b);
+
+    r = js_bigint_new(ctx, a_len);
+    if (!r)
+        return NULL;
+    switch(op) {
+    case OP_or:
+        for(i = 0; i < b_len; i++) {
+            r->tab[i] = a->tab[i] | b->tab[i];
+        }
+        for(i = b_len; i < a_len; i++) {
+            r->tab[i] = a->tab[i] | b_sign;
+        }
         break;
-    case JS_TAG_FLOAT64:
-        {
-            JSFloat64Union u;
-            double d;
-            int e;
-            d = JS_VALUE_GET_FLOAT64(val);
-            u.d = d;
-            /* we avoid doing fmod(x, 2^64) */
-            e = (u.u64 >> 52) & 0x7ff;
-            if (likely(e <= (1023 + 62))) {
-                /* fast case */
-                ret = (int64_t)d;
-            } else if (e <= (1023 + 62 + 53)) {
-                uint64_t v;
-                /* remainder modulo 2^64 */
-                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
-                ret = v << ((e - 1023) - 52);
-                /* take the sign into account */
-                if (u.u64 >> 63)
-                    ret = -ret;
-            } else {
-                ret = 0; /* also handles NaN and +inf */
-            }
+    case OP_and:
+        for(i = 0; i < b_len; i++) {
+            r->tab[i] = a->tab[i] & b->tab[i];
+        }
+        for(i = b_len; i < a_len; i++) {
+            r->tab[i] = a->tab[i] & b_sign;
         }
         break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
-            JS_FreeValue(ctx, val);
+    case OP_xor:
+        for(i = 0; i < b_len; i++) {
+            r->tab[i] = a->tab[i] ^ b->tab[i];
+        }
+        for(i = b_len; i < a_len; i++) {
+            r->tab[i] = a->tab[i] ^ b_sign;
         }
         break;
-#endif
     default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val)) {
-            *pres = 0;
-            return -1;
-        }
-        goto redo;
+        abort();
     }
-    *pres = ret;
-    return 0;
+    return js_bigint_normalize(ctx, r);
 }
 
-int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a)
 {
-    return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
+    JSBigInt *r;
+    int i;
+    
+    r = js_bigint_new(ctx, a->len);
+    if (!r)
+        return NULL;
+    for(i = 0; i < a->len; i++) {
+        r->tab[i] = ~a->tab[i];
+    }
+    /* no normalization is needed */
+    return r;
 }
 
-int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a,
+                               unsigned int shift1)
 {
-    if (JS_IsBigInt(ctx, val))
-        return JS_ToBigInt64(ctx, pres, val);
-    else
-        return JS_ToInt64(ctx, pres, val);
+    int d, i, shift;
+    JSBigInt *r;
+    js_limb_t l;
+
+    if (a->len == 1 && a->tab[0] == 0)
+        return js_bigint_new_si(ctx, 0); /* zero case */
+    d = shift1 / JS_LIMB_BITS;
+    shift = shift1 % JS_LIMB_BITS;
+    r = js_bigint_new(ctx, a->len + d);
+    if (!r)
+        return NULL;
+    for(i = 0; i < d; i++)
+        r->tab[i] = 0;
+    if (shift == 0) {
+        for(i = 0; i < a->len; i++) {
+            r->tab[i + d] = a->tab[i];
+        }
+    } else {
+        l = mp_shl(r->tab + d, a->tab, a->len, shift);
+        if (js_bigint_sign(a))
+            l |= (js_limb_t)(-1) << shift;
+        r = js_bigint_extend(ctx, r, l);
+    }
+    return r;
 }
 
-/* return (<0, 0) in case of exception */
-static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
+static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a,
+                               unsigned int shift1)
 {
-    uint32_t tag;
-    int32_t ret;
+    int d, i, shift, a_sign, n1;
+    JSBigInt *r;
 
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        ret = JS_VALUE_GET_INT(val);
-        break;
-    case JS_TAG_FLOAT64:
-        {
-            JSFloat64Union u;
-            double d;
-            int e;
-            d = JS_VALUE_GET_FLOAT64(val);
-            u.d = d;
-            /* we avoid doing fmod(x, 2^32) */
-            e = (u.u64 >> 52) & 0x7ff;
-            if (likely(e <= (1023 + 30))) {
-                /* fast case */
-                ret = (int32_t)d;
-            } else if (e <= (1023 + 30 + 53)) {
-                uint64_t v;
-                /* remainder modulo 2^32 */
-                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
-                v = v << ((e - 1023) - 52 + 32);
-                ret = v >> 32;
-                /* take the sign into account */
-                if (u.u64 >> 63)
-                    ret = -ret;
-            } else {
-                ret = 0; /* also handles NaN and +inf */
-            }
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
-            JS_FreeValue(ctx, val);
-        }
-        break;
-#endif
-    default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val)) {
-            *pres = 0;
-            return -1;
+    d = shift1 / JS_LIMB_BITS;
+    shift = shift1 % JS_LIMB_BITS;
+    a_sign = js_bigint_sign(a);
+    if (d >= a->len)
+        return js_bigint_new_si(ctx, -a_sign);
+    n1 = a->len - d;
+    r = js_bigint_new(ctx, n1);
+    if (!r)
+        return NULL;
+    if (shift == 0) {
+        for(i = 0; i < n1; i++) {
+            r->tab[i] = a->tab[i + d];
         }
-        goto redo;
+        /* no normalization is needed */
+    } else {
+        mp_shr(r->tab, a->tab + d, n1, shift, -a_sign);
+        r = js_bigint_normalize(ctx, r);
     }
-    *pres = ret;
-    return 0;
+    return r;
 }
 
-int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b)
 {
-    return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
-}
+    uint32_t e;
+    int n_bits, i;
+    JSBigInt *r, *r1;
+    
+    /* b must be >= 0 */
+    if (js_bigint_sign(b)) {
+        JS_ThrowRangeError(ctx, "BigInt negative exponent");
+        return NULL;
+    }
+    if (b->len == 1 && b->tab[0] == 0) {
+        /* a^0 = 1 */
+        return js_bigint_new_si(ctx, 1);
+    } else if (a->len == 1) {
+        js_limb_t v;
+        BOOL is_neg;
+
+        v = a->tab[0];
+        if (v <= 1)
+            return js_bigint_new_si(ctx, v);
+        else if (v == -1)
+            return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1));
+        is_neg = (js_slimb_t)v < 0;
+        if (is_neg)
+            v = -v;
+        if ((v & (v - 1)) == 0) {
+            uint64_t e1;
+            int n;
+            /* v = 2^n */
+            n = JS_LIMB_BITS - 1 - js_limb_clz(v);
+            if (b->len > 1)
+                goto overflow;
+            if (b->tab[0] > INT32_MAX)
+                goto overflow;
+            e = b->tab[0];
+            e1 = (uint64_t)e * n;
+            if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS)
+                goto overflow;
+            e = e1;
+            if (is_neg)
+                is_neg = b->tab[0] & 1;
+            r = js_bigint_new(ctx,
+                              (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS);
+            if (!r)
+                return NULL;
+            memset(r->tab, 0, sizeof(r->tab[0]) * r->len);
+            r->tab[e / JS_LIMB_BITS] =
+                (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS);
+            return r;
+        }
+    }
+    if (b->len > 1)
+        goto overflow;
+    if (b->tab[0] > INT32_MAX)
+        goto overflow;
+    e = b->tab[0];
+    n_bits = 32 - clz32(e);
 
-static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
-{
-    return JS_ToInt32Free(ctx, (int32_t *)pres, val);
+    r = js_bigint_new(ctx, a->len);
+    if (!r)
+        return NULL;
+    memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
+    for(i = n_bits - 2; i >= 0; i--) {
+        r1 = js_bigint_mul(ctx, r, r);
+        if (!r1)
+            return NULL;
+        js_free(ctx, r);
+        r = r1;
+        if ((e >> i) & 1) {
+            r1 = js_bigint_mul(ctx, r, a);
+            if (!r1)
+                return NULL;
+            js_free(ctx, r);
+            r = r1;
+        }
+    }
+    return r;
+ overflow:
+    JS_ThrowRangeError(ctx, "BigInt is too large");
+    return NULL;
 }
 
-static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
-{
-    uint32_t tag;
-    int res;
-
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-        res = JS_VALUE_GET_INT(val);
-#ifdef CONFIG_BIGNUM
-    int_clamp:
-#endif
-        res = max_int(0, min_int(255, res));
-        break;
-    case JS_TAG_FLOAT64:
-        {
-            double d = JS_VALUE_GET_FLOAT64(val);
-            if (isnan(d)) {
-                res = 0;
-            } else {
-                if (d < 0)
-                    res = 0;
-                else if (d > 255)
-                    res = 255;
-                else
-                    res = lrint(d);
-            }
+/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits -
+   1). a must be != 0. */
+static uint64_t js_bigint_get_mant_exp(JSContext *ctx,
+                                       int *pexp, const JSBigInt *a)
+{
+    js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits;
+    int n1, n2, sgn, shift, i, j, e;
+    uint64_t a1, a0;
+
+    n2 = 4 - JS_LIMB_BITS / 32;
+    n1 = a->len - n2;
+    sgn = js_bigint_sign(a);
+
+    /* low_bits != 0 if there are a non zero low bit in abs(a) */
+    low_bits = 0;
+    carry = sgn;
+    for(i = 0; i < n1; i++) {
+        v = (a->tab[i] ^ (-sgn)) + carry;
+        carry = v < carry;
+        low_bits |= v;
+    }
+    /* get the n2 high limbs of abs(a) */
+    for(j = 0; j < n2; j++) {
+        i = j + n1;
+        if (i < 0) {
+            v = 0;
+        } else {
+            v = (a->tab[i] ^ (-sgn)) + carry;
+            carry = v < carry;
         }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_t r_s, *r = &r_s;
-            bf_init(ctx->bf_ctx, r);
-            bf_set(r, &p->num);
-            bf_rint(r, BF_RNDN);
-            bf_get_int32(&res, r, 0);
-            bf_delete(r);
-            JS_FreeValue(ctx, val);
+        t[j] = v;
+    }
+    
+#if JS_LIMB_BITS == 32
+    a1 = ((uint64_t)t[2] << 32) | t[1];
+    a0 = (uint64_t)t[0] << 32;
+#else
+    a1 = t[1];
+    a0 = t[0];
+#endif
+    a0 |= (low_bits != 0);
+    /* normalize */
+    if (a1 == 0) {
+        /* JS_LIMB_BITS = 64 bit only */
+        shift = 64;
+        a1 = a0;
+        a0 = 0;
+    } else {
+        shift = clz64(a1);
+        if (shift != 0) {
+            a1 = (a1 << shift) | (a0 >> (64 - shift));
+            a0 <<= shift;
+        }
+    }
+    a1 |= (a0 != 0); /* keep the bits for the final rounding */
+    /* compute the exponent */
+    e = a->len * JS_LIMB_BITS - shift - 1;
+    *pexp = e;
+    return a1;
+}
+
+/* shift left with round to nearest, ties to even. n >= 1 */
+static uint64_t shr_rndn(uint64_t a, int n)
+{
+    uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1);
+    return (a + addend) >> n;
+}
+
+/* convert to float64 with round to nearest, ties to even. Return
+   +/-infinity if too large. */
+static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a)
+{
+    int sgn, e;
+    uint64_t mant;
+
+    if (a->len == 1) {
+        /* fast case, including zero */
+        return (double)(js_slimb_t)a->tab[0];
+    }
+
+    sgn = js_bigint_sign(a);
+    mant = js_bigint_get_mant_exp(ctx, &e, a);
+    if (e > 1023) {
+        /* overflow: return infinity */
+        mant = 0;
+        e = 1024;
+    } else {
+        mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */
+        mant = shr_rndn(mant, 10);
+        /* rounding can cause an overflow */
+        if (mant >= ((uint64_t)1 << 53)) {
+            mant >>= 1;
+            e++;
+        }
+        mant &= (((uint64_t)1 << 52) - 1);
+    }
+    return uint64_as_float64(((uint64_t)sgn << 63) |
+                             ((uint64_t)(e + 1023) << 52) |
+                             mant);
+}
+
+/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity,
+   (0, n) if an integer, (0, NULL) in case of memory error */
+static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1)
+{
+    uint64_t a = float64_as_uint64(a1);
+    int sgn, e, shift;
+    uint64_t mant;
+    JSBigIntBuf buf;
+    JSBigInt *r, *r1;
+    
+    sgn = a >> 63;
+    e = (a >> 52) & ((1 << 11) - 1);
+    mant = a & (((uint64_t)1 << 52) - 1);
+    if (e == 2047) {
+        /* NaN, Infinity */
+        *pres = 2;
+        return NULL;
+    }
+    if (e == 0 && mant == 0) {
+        /* zero */
+        *pres = 0;
+        return js_bigint_new_si(ctx, 0);
+    }
+    e -= 1023;
+    /* 0 < a < 1 : not an integer */
+    if (e < 0)
+        goto not_an_integer;
+    mant |= (uint64_t)1 << 52;
+    if (e < 52) {
+        shift = 52 - e;
+        /* check that there is no fractional part */
+        if (mant & (((uint64_t)1 << shift) - 1)) {
+        not_an_integer:
+            *pres = 1;
+            return NULL;
         }
-        goto int_clamp;
+        mant >>= shift;
+        e = 0;
+    } else {
+        e -= 52;
+    }
+    
+    /* the integer is mant*2^e */
+    r = &buf.big_int;
+#if JS_LIMB_BITS == 64
+    r->len = 1;
+    r->tab[0] = mant;
+#else
+    if (mant <= INT32_MAX) {
+        r->len = 1;
+        r->tab[0] = mant;
+    } else {
+        r->len = 2;
+        r->tab[0] = mant;
+        r->tab[1] = mant >> 32;
+    }
 #endif
-    default:
-        val = JS_ToNumberFree(ctx, val);
-        if (JS_IsException(val)) {
-            *pres = 0;
-            return -1;
+    /* XXX: optimize */
+    if (sgn) {
+        r = js_bigint_neg(ctx, r);
+        if (!r)
+            goto fail;
+        r1 = js_bigint_shl(ctx, r, e);
+        js_free(ctx, r);
+        if (!r1)
+            goto fail;
+        r = r1;
+    } else {
+        r = js_bigint_shl(ctx, r, e);
+    }
+    *pres = 0;
+    return r;
+ fail:
+    *pres = 0;
+    return NULL;
+}
+
+/* return -1, 0, 1 or (2) (unordered) */
+static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a,
+                                 double b)
+{
+    int b_sign, a_sign, e, f;
+    uint64_t mant, b1, a_mant;
+    
+    b1 = float64_as_uint64(b);
+    b_sign = b1 >> 63;
+    e = (b1 >> 52) & ((1 << 11) - 1);
+    mant = b1 & (((uint64_t)1 << 52) - 1);
+    a_sign = js_bigint_sign(a);
+    if (e == 2047) {
+        if (mant != 0) {
+            /* NaN */
+            return 2;
+        } else {
+            /* +/- infinity */
+            return 2 * b_sign - 1;
+        }
+    } else if (e == 0 && mant == 0) {
+        /* b = +/-0 */
+        if (a->len == 1 && a->tab[0] == 0)
+            return 0;
+        else
+            return 1 - 2 * a_sign;
+    } else if (a->len == 1 && a->tab[0] == 0) {
+        /* a = 0, b != 0 */
+        return 2 * b_sign - 1;
+    } else if (a_sign != b_sign) {
+        return 1 - 2 * a_sign;
+    } else {
+        e -= 1023;
+        /* Note: handling denormals is not necessary because we
+           compare to integers hence f >= 0 */
+        /* compute f so that 2^f <= abs(a) < 2^(f+1) */
+        a_mant = js_bigint_get_mant_exp(ctx, &f, a);
+        if (f != e) {
+            if (f < e)
+                return -1;
+            else
+                return 1;
+        } else {
+            mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */
+            if (a_mant < mant)
+                return 2 * a_sign - 1;
+            else if (a_mant > mant)
+                return 1 - 2 * a_sign;
+            else
+                return 0;
         }
-        goto redo;
     }
-    *pres = res;
-    return 0;
 }
 
-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
-                                            JSValue val, BOOL is_array_ctor)
+/* return -1, 0 or 1 */
+static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a,
+                         const JSBigInt *b)
 {
-    uint32_t tag, len;
-
-    tag = JS_VALUE_GET_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-        {
-            int v;
-            v = JS_VALUE_GET_INT(val);
-            if (v < 0)
-                goto fail;
-            len = v;
-        }
-        break;
-    case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-#endif
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            bf_t a;
-            BOOL res;
-            bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
-            bf_init(ctx->bf_ctx, &a);
-            bf_set_ui(&a, len);
-            res = bf_cmp_eq(&a, &p->num);
-            bf_delete(&a);
-            JS_FreeValue(ctx, val);
-            if (!res)
-                goto fail;
-        }
-        break;
-    default:
-        if (JS_TAG_IS_FLOAT64(tag)) {
-            double d;
-            d = JS_VALUE_GET_FLOAT64(val);
-            if (!(d >= 0 && d <= UINT32_MAX))
-                goto fail;
-            len = (uint32_t)d;
-            if (len != d)
-                goto fail;
+    int a_sign, b_sign, res, i;
+    a_sign = js_bigint_sign(a);
+    b_sign = js_bigint_sign(b);
+    if (a_sign != b_sign) {
+        res = 1 - 2 * a_sign;
+    } else {
+        /* we assume the numbers are normalized */
+        if (a->len != b->len) {
+            if (a->len < b->len)
+                res = 2 * a_sign - 1;
+            else
+                res = 1 - 2 * a_sign;
         } else {
-            uint32_t len1;
-
-            if (is_array_ctor) {
-                val = JS_ToNumberFree(ctx, val);
-                if (JS_IsException(val))
-                    return -1;
-                /* cannot recurse because val is a number */
-                if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
-                    return -1;
-            } else {
-                /* legacy behavior: must do the conversion twice and compare */
-                if (JS_ToUint32(ctx, &len, val)) {
-                    JS_FreeValue(ctx, val);
-                    return -1;
-                }
-                val = JS_ToNumberFree(ctx, val);
-                if (JS_IsException(val))
-                    return -1;
-                /* cannot recurse because val is a number */
-                if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
-                    return -1;
-                if (len1 != len) {
-                fail:
-                    JS_ThrowRangeError(ctx, "invalid array length");
-                    return -1;
+            res = 0;
+            for(i = a->len -1; i >= 0; i--) {
+                if (a->tab[i] != b->tab[i]) {
+                    if (a->tab[i] < b->tab[i])
+                        res = -1;
+                    else
+                        res = 1;
+                    break;
                 }
             }
         }
-        break;
-    }
-    *plen = len;
-    return 0;
-}
-
-#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
-
-static BOOL is_safe_integer(double d)
-{
-    return isfinite(d) && floor(d) == d &&
-        fabs(d) <= (double)MAX_SAFE_INTEGER;
-}
-
-int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
-{
-    int64_t v;
-    if (JS_ToInt64Sat(ctx, &v, val))
-        return -1;
-    if (v < 0 || v > MAX_SAFE_INTEGER) {
-        JS_ThrowRangeError(ctx, "invalid array index");
-        *plen = 0;
-        return -1;
     }
-    *plen = v;
-    return 0;
-}
-
-/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
-   return -1 for exception */
-static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
-                                       JSValue val)
-{
-    int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
-    JS_FreeValue(ctx, val);
     return res;
 }
 
-/* Note: can return an exception */
-static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
-{
-    double d;
-    if (!JS_IsNumber(val))
-        return FALSE;
-    if (unlikely(JS_ToFloat64(ctx, &d, val)))
-        return -1;
-    return isfinite(d) && floor(d) == d;
-}
-
-static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
-{
-    uint32_t tag;
-
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-        {
-            int v;
-            v = JS_VALUE_GET_INT(val);
-            return (v < 0);
-        }
-    case JS_TAG_FLOAT64:
-        {
-            JSFloat64Union u;
-            u.d = JS_VALUE_GET_FLOAT64(val);
-            return (u.u64 >> 63);
-        }
-    case JS_TAG_BIG_INT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            /* Note: integer zeros are not necessarily positive */
-            return p->num.sign && !bf_is_zero(&p->num);
-        }
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            return p->num.sign;
-        }
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        {
-            JSBigDecimal *p = JS_VALUE_GET_PTR(val);
-            return p->num.sign;
-        }
-        break;
+/* contains 10^i */
+static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = {
+    1U,
+    10U,
+    100U,
+    1000U,
+    10000U,
+    100000U,
+    1000000U,
+    10000000U,
+    100000000U,
+    1000000000U,
+#if JS_LIMB_BITS == 64
+    10000000000U,
+    100000000000U,
+    1000000000000U,
+    10000000000000U,
+    100000000000000U,
+    1000000000000000U,
+    10000000000000000U,
+    100000000000000000U,
+    1000000000000000000U,
+    10000000000000000000U,
 #endif
-    default:
-        return FALSE;
-    }
-}
-
-static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
-{
-    JSValue ret;
-    bf_t a_s, *a;
-    char *str;
-    int saved_sign;
-
-    a = JS_ToBigInt(ctx, &a_s, val);
-    if (!a)
-        return JS_EXCEPTION;
-    saved_sign = a->sign;
-    if (a->expn == BF_EXP_ZERO)
-        a->sign = 0;
-    str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
-                  BF_FTOA_JS_QUIRKS);
-    a->sign = saved_sign;
-    JS_FreeBigInt(ctx, a, &a_s);
-    if (!str)
-        return JS_ThrowOutOfMemory(ctx);
-    ret = JS_NewString(ctx, str);
-    bf_free(ctx->bf_ctx, str);
-    return ret;
-}
-
-static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
-{
-    return js_bigint_to_string1(ctx, val, 10);
-}
-
-#ifdef CONFIG_BIGNUM
+};
 
-static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
-                       limb_t prec, bf_flags_t flags)
+/* syntax: [-]digits in base radix. Return NULL if memory error. radix
+   = 10, 2, 8 or 16. */
+static JSBigInt *js_bigint_from_string(JSContext *ctx,
+                                       const char *str, int radix)
 {
-    JSValue val, ret;
-    bf_t a_s, *a;
-    char *str;
-    int saved_sign;
-
-    val = JS_ToNumeric(ctx, val1);
-    if (JS_IsException(val))
-        return val;
-    a = JS_ToBigFloat(ctx, &a_s, val);
-    if (!a) {
-        JS_FreeValue(ctx, val);
-        return JS_EXCEPTION;
+    const char *p = str;
+    int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i;
+    JSBigInt *r;
+    js_limb_t v, c, h;
+    
+    is_neg = 0;
+    if (*p == '-') {
+        is_neg = 1;
+        p++;
     }
-    saved_sign = a->sign;
-    if (a->expn == BF_EXP_ZERO)
-        a->sign = 0;
-    flags |= BF_FTOA_JS_QUIRKS;
-    if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
-        /* Note: for floating point numbers with a radix which is not
-           a power of two, the current precision is used to compute
-           the number of digits. */
-        if ((radix & (radix - 1)) != 0) {
-            bf_t r_s, *r = &r_s;
-            int prec, flags1;
-            /* must round first */
-            if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
-                prec = ctx->fp_env.prec;
-                flags1 = ctx->fp_env.flags &
-                    (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
+    while (*p == '0')
+        p++;
+    n_digits = strlen(p);
+    log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */
+    /* compute the maximum number of limbs */
+    /* XXX: overflow */
+    if (radix == 10) {
+        n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */
+    } else {
+        n_bits = n_digits * log2_radix;
+    }
+    /* we add one extra bit for the sign */
+    n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1);
+    r = js_bigint_new(ctx, n_limbs);
+    if (!r)
+        return NULL;
+    if (radix == 10) {
+        int digits_per_limb = JS_LIMB_DIGITS;
+        len = 1;
+        r->tab[0] = 0;
+        for(;;) {
+            /* XXX: slow */
+            v = 0;
+            for(i = 0; i < digits_per_limb; i++) {
+                c = to_digit(*p);
+                if (c >= radix)
+                    break;
+                p++;
+                v = v * 10 + c;
+            }
+            if (i == 0)
+                break;
+            if (len == 1 && r->tab[0] == 0) {
+                r->tab[0] = v;
             } else {
-                prec = 53;
-                flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
-            }
-            bf_init(ctx->bf_ctx, r);
-            bf_set(r, a);
-            bf_round(r, prec, flags1 | BF_RNDN);
-            str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
-            bf_delete(r);
-        } else {
-            str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
-        }
-    } else {
-        str = bf_ftoa(NULL, a, radix, prec, flags);
+                h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v);
+                if (h != 0) {
+                    r->tab[len++] = h;
+                }
+            }
+        }
+        /* add one extra limb to have the correct sign*/
+        if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0)
+            r->tab[len++] = 0;
+        r->len = len;
+    } else {
+        unsigned int bit_pos, shift, pos;
+        
+        /* power of two base: no multiplication is needed */
+        r->len = n_limbs;
+        memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs);
+        for(i = 0; i < n_digits; i++) {
+            c = to_digit(p[n_digits - 1 - i]);
+            assert(c < radix);
+            bit_pos = i * log2_radix;
+            shift = bit_pos & (JS_LIMB_BITS - 1);
+            pos = bit_pos / JS_LIMB_BITS;
+            r->tab[pos] |= c << shift;
+            /* if log2_radix does not divide JS_LIMB_BITS, needed an
+               additional op */
+            if (shift + log2_radix > JS_LIMB_BITS) {
+                r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift);
+            }
+        }
+    }
+    r = js_bigint_normalize(ctx, r);
+    /* XXX: could do it in place */
+    if (is_neg) {
+        JSBigInt *r1;
+        r1 = js_bigint_neg(ctx, r);
+        js_free(ctx, r);
+        r = r1;
     }
-    a->sign = saved_sign;
-    if (a == &a_s)
-        bf_delete(a);
-    JS_FreeValue(ctx, val);
-    if (!str)
-        return JS_ThrowOutOfMemory(ctx);
-    ret = JS_NewString(ctx, str);
-    bf_free(ctx->bf_ctx, str);
-    return ret;
-}
-
-static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
-{
-    return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
-}
-
-static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
-                                        limb_t prec, int flags)
-{
-    JSValue ret;
-    bfdec_t *a;
-    char *str;
-    int saved_sign;
-
-    a = JS_ToBigDecimal(ctx, val);
-    if (!a)
-        return JS_EXCEPTION;
-    saved_sign = a->sign;
-    if (a->expn == BF_EXP_ZERO)
-        a->sign = 0;
-    str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
-    a->sign = saved_sign;
-    if (!str)
-        return JS_ThrowOutOfMemory(ctx);
-    ret = JS_NewString(ctx, str);
-    bf_free(ctx->bf_ctx, str);
-    return ret;
-}
-
-static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
-{
-    return js_bigdecimal_to_string1(ctx, val, 0,
-                                    BF_RNDZ | BF_FTOA_FORMAT_FREE);
+    return r;
 }
 
-#endif /* CONFIG_BIGNUM */
-
 /* 2 <= base <= 36 */
 static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
 
-static char *i64toa(char *buf_end, int64_t n, unsigned int base)
+static char *u64toa(char *q, int64_t n, unsigned int base)
 {
-    char *q = buf_end;
-    int digit, is_neg;
-
-    is_neg = 0;
-    if (n < 0) {
-        is_neg = 1;
-        n = -n;
-    }
-    *--q = '\0';
+    int digit;
     if (base == 10) {
         /* division by known base uses multiplication */
         do {
@@ -11367,2118 +11158,2136 @@ static char *i64toa(char *buf_end, int64_t n, unsigned int base)
             *--q = digits[digit];
         } while (n != 0);
     }
-    if (is_neg)
-        *--q = '-';
     return q;
 }
 
-/* buf1 contains the printf result */
-static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
-                     int rounding_mode, char *buf1, int buf1_size)
+static char *i64toa(char *buf_end, int64_t n, unsigned int base)
 {
-    if (rounding_mode != FE_TONEAREST)
-        fesetround(rounding_mode);
-    snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
-    if (rounding_mode != FE_TONEAREST)
-        fesetround(FE_TONEAREST);
-    *sign = (buf1[0] == '-');
-    /* mantissa */
-    buf[0] = buf1[1];
-    if (n_digits > 1)
-        memcpy(buf + 1, buf1 + 3, n_digits - 1);
-    buf[n_digits] = '\0';
-    /* exponent */
-    *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
-}
+    char *q = buf_end;
+    int is_neg;
 
-/* maximum buffer size for js_dtoa */
-#define JS_DTOA_BUF_SIZE 128
+    is_neg = 0;
+    if (n < 0) {
+        is_neg = 1;
+        n = -n;
+    }
+    *--q = '\0';
+    q = u64toa(q, n, base);
+    if (is_neg)
+        *--q = '-';
+    return q;
+}
 
-/* needed because ecvt usually limits the number of digits to
-   17. Return the number of digits. */
-static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
-                   BOOL is_fixed)
+/* len >= 1. 2 <= radix <= 36 */
+static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len)
 {
-    int rounding_mode;
-    char buf_tmp[JS_DTOA_BUF_SIZE];
+    int digit, i;
 
-    if (!is_fixed) {
-        unsigned int n_digits_min, n_digits_max;
-        /* find the minimum amount of digits (XXX: inefficient but simple) */
-        n_digits_min = 1;
-        n_digits_max = 17;
-        while (n_digits_min < n_digits_max) {
-            n_digits = (n_digits_min + n_digits_max) / 2;
-            js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
-                     buf_tmp, sizeof(buf_tmp));
-            if (strtod(buf_tmp, NULL) == d) {
-                /* no need to keep the trailing zeros */
-                while (n_digits >= 2 && buf[n_digits - 1] == '0')
-                    n_digits--;
-                n_digits_max = n_digits;
-            } else {
-                n_digits_min = n_digits + 1;
-            }
+    if (radix == 10) {
+        /* specific case with constant divisor */
+        /* XXX: optimize */
+        for(i = 0; i < len; i++) {
+            digit = (js_limb_t)n % 10;
+            n = (js_limb_t)n / 10;
+            *--q = digit + '0';
         }
-        n_digits = n_digits_max;
-        rounding_mode = FE_TONEAREST;
     } else {
-        rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
-        {
-            char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
-            int decpt1, sign1, decpt2, sign2;
-            /* The JS rounding is specified as round to nearest ties away
-               from zero (RNDNA), but in printf the "ties" case is not
-               specified (for example it is RNDN for glibc, RNDNA for
-               Windows), so we must round manually. */
-            js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
-                     buf_tmp, sizeof(buf_tmp));
-            /* XXX: could use 2 digits to reduce the average running time */
-            if (buf1[n_digits] == '5') {
-                js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
-                         buf_tmp, sizeof(buf_tmp));
-                js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
-                         buf_tmp, sizeof(buf_tmp));
-                if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
-                    /* exact result: round away from zero */
-                    if (sign1)
-                        rounding_mode = FE_DOWNWARD;
-                    else
-                        rounding_mode = FE_UPWARD;
-                }
-            }
+        for(i = 0; i < len; i++) {
+            digit = (js_limb_t)n % radix;
+            n = (js_limb_t)n / radix;
+            *--q = digits[digit];
         }
-#endif /* CONFIG_PRINTF_RNDN */
     }
-    js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
-             buf_tmp, sizeof(buf_tmp));
-    return n_digits;
-}
-
-static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
-                    int rounding_mode)
-{
-    int n;
-    if (rounding_mode != FE_TONEAREST)
-        fesetround(rounding_mode);
-    n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
-    if (rounding_mode != FE_TONEAREST)
-        fesetround(FE_TONEAREST);
-    assert(n < sizeof(*buf));
-    return n;
+    return q;
 }
 
-static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
-{
-    int rounding_mode;
-    rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
-    {
-        int n1, n2;
-        char buf1[JS_DTOA_BUF_SIZE];
-        char buf2[JS_DTOA_BUF_SIZE];
+#define JS_RADIX_MAX 36
 
-        /* The JS rounding is specified as round to nearest ties away from
-           zero (RNDNA), but in printf the "ties" case is not specified
-           (for example it is RNDN for glibc, RNDNA for Windows), so we
-           must round manually. */
-        n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
-        rounding_mode = FE_TONEAREST;
-        /* XXX: could use 2 digits to reduce the average running time */
-        if (buf1[n1 - 1] == '5') {
-            n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
-            n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
-            if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
-                /* exact result: round away from zero */
-                if (buf1[0] == '-')
-                    rounding_mode = FE_DOWNWARD;
-                else
-                    rounding_mode = FE_UPWARD;
-            }
-        }
-    }
-#endif /* CONFIG_PRINTF_RNDN */
-    js_fcvt1(buf, d, n_digits, rounding_mode);
-}
+static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
+#if JS_LIMB_BITS == 32
+32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+#else
+64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
+#endif
+};
 
-/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
-/* use as many digits as necessary */
-#define JS_DTOA_VAR_FORMAT   (0 << 0)
-/* use n_digits significant digits (1 <= n_digits <= 101) */
-#define JS_DTOA_FIXED_FORMAT (1 << 0)
-/* force fractional format: [-]dd.dd with n_digits fractional digits */
-#define JS_DTOA_FRAC_FORMAT  (2 << 0)
-/* force exponential notation either in fixed or variable format */
-#define JS_DTOA_FORCE_EXP    (1 << 2)
+static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = {
+#if JS_LIMB_BITS == 32
+ 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
+ 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
+ 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
+ 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
+ 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
+ 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
+ 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
+ 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
+ 0x5c13d840, 0x6d91b519, 0x81bf1000,
+#else
+ 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d,
+ 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821,
+ 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d,
+ 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1,
+ 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49,
+ 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9,
+ 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd,
+ 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981,
+ 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000,
+#endif
+};
 
-/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
-   XXX: radix != 10 is only supported for small integers
-*/
-static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
-                     int radix, int n_digits, int flags)
+static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
 {
-    char *q;
-
-    if (!isfinite(d)) {
-        if (isnan(d)) {
-            pstrcpy(*buf, sizeof(*buf), "NaN");
-        } else if (d < 0) {
-            pstrcpy(*buf, sizeof(*buf), "-Infinity");
-        } else {
-            pstrcpy(*buf, sizeof(*buf), "Infinity");
-        }
-    } else if (flags == JS_DTOA_VAR_FORMAT) {
-        int64_t i64;
-        char buf1[70], *ptr;
-        if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
-            goto generic_conv;
-        i64 = (int64_t)d;
-        if (d != i64)
-            goto generic_conv;
-        /* fast path for integers */
-        ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
-        pstrcpy(*buf, sizeof(*buf), ptr);
+    if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+        char buf[66], *q;
+        
+        q = i64toa(buf + sizeof(buf), JS_VALUE_GET_SHORT_BIG_INT(val), radix);
+        return JS_NewString(ctx, q);
     } else {
-        if (d == 0.0)
-            d = 0.0; /* convert -0 to 0 */
-        if (flags == JS_DTOA_FRAC_FORMAT) {
-            js_fcvt(buf, d, n_digits);
-        } else {
-            char buf1[JS_DTOA_BUF_SIZE];
-            int sign, decpt, k, n, i, p, n_max;
-            BOOL is_fixed;
-        generic_conv:
-            is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
-            if (is_fixed) {
-                n_max = n_digits;
-            } else {
-                n_max = 21;
-            }
-            /* the number has k digits (k >= 1) */
-            k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
-            n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
-            q = *buf;
-            if (sign)
-                *q++ = '-';
-            if (flags & JS_DTOA_FORCE_EXP)
-                goto force_exp;
-            if (n >= 1 && n <= n_max) {
-                if (k <= n) {
-                    memcpy(q, buf1, k);
-                    q += k;
-                    for(i = 0; i < (n - k); i++)
-                        *q++ = '0';
-                    *q = '\0';
+        JSBigInt *r, *tmp = NULL;
+        char *buf, *q;
+        int is_neg, n_bits, log2_radix, n_digits;
+        BOOL is_binary_radix;
+        JSValue res;
+        
+        assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT);
+        r = JS_VALUE_GET_PTR(val);
+        if (r->len == 1 && r->tab[0] == 0) {
+            /* '0' case */
+            return JS_NewString(ctx, "0");
+        }
+        is_binary_radix = ((radix & (radix - 1)) == 0);
+        is_neg = js_bigint_sign(r);
+        if (is_neg) {
+            tmp = js_bigint_neg(ctx, r);
+            if (!tmp)
+                return JS_EXCEPTION;
+            r = tmp;
+        } else if (!is_binary_radix) {
+            /* need to modify 'r' */
+            tmp = js_bigint_new(ctx, r->len);
+            if (!tmp)
+                return JS_EXCEPTION;
+            memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0]));
+            r = tmp;
+        }
+        log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */
+        n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]);
+        /* n_digits is exact only if radix is a power of
+           two. Otherwise it is >= the exact number of digits */
+        n_digits = (n_bits + log2_radix - 1) / log2_radix;
+        /* XXX: could directly build the JSString */
+        buf = js_malloc(ctx, n_digits + is_neg + 1);
+        if (!buf) {
+            js_free(ctx, tmp);
+            return JS_EXCEPTION;
+        }
+        q = buf + n_digits + is_neg + 1;
+        *--q = '\0';
+        if (!is_binary_radix) {
+            int len;
+            js_limb_t radix_base, v;
+            radix_base = radix_base_table[radix - 2];
+            len = r->len;
+            for(;;) {
+                /* remove leading zero limbs */
+                while (len > 1 && r->tab[len - 1] == 0)
+                    len--;
+                if (len == 1 && r->tab[0] < radix_base) {
+                    v = r->tab[0];
+                    if (v != 0) {
+                        q = u64toa(q, v, radix);
+                    }
+                    break;
                 } else {
-                    /* k > n */
-                    memcpy(q, buf1, n);
-                    q += n;
-                    *q++ = '.';
-                    for(i = 0; i < (k - n); i++)
-                        *q++ = buf1[n + i];
-                    *q = '\0';
+                    v = mp_div1(r->tab, r->tab, len, radix_base, 0);
+                    q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]);
                 }
-            } else if (n >= -5 && n <= 0) {
-                *q++ = '0';
-                *q++ = '.';
-                for(i = 0; i < -n; i++)
-                    *q++ = '0';
-                memcpy(q, buf1, k);
-                q += k;
-                *q = '\0';
-            } else {
-            force_exp:
-                /* exponential notation */
-                *q++ = buf1[0];
-                if (k > 1) {
-                    *q++ = '.';
-                    for(i = 1; i < k; i++)
-                        *q++ = buf1[i];
+            }
+        } else {
+            int i, shift;
+            unsigned int bit_pos, pos, c;
+
+            /* radix is a power of two */
+            for(i = 0; i < n_digits; i++) {
+                bit_pos = i * log2_radix;
+                pos = bit_pos / JS_LIMB_BITS;
+                shift = bit_pos % JS_LIMB_BITS;
+                if (likely((shift + log2_radix) <= JS_LIMB_BITS)) {
+                    c = r->tab[pos] >> shift;
+                } else {
+                    c = (r->tab[pos] >> shift) |
+                        (r->tab[pos + 1] << (JS_LIMB_BITS - shift));
                 }
-                *q++ = 'e';
-                p = n - 1;
-                if (p >= 0)
-                    *q++ = '+';
-                snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
+                c &= (radix - 1);
+                *--q = digits[c];
             }
         }
+        if (is_neg)
+            *--q = '-';
+        js_free(ctx, tmp);
+        res = JS_NewString(ctx, q);
+        js_free(ctx, buf);
+        return res;
     }
 }
 
-static JSValue js_dtoa(JSContext *ctx,
-                       double d, int radix, int n_digits, int flags)
+/* if possible transform a BigInt to short big and free it, otherwise
+   return a normal bigint */
+static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p)
 {
-    char buf[JS_DTOA_BUF_SIZE];
-    js_dtoa1(&buf, d, radix, n_digits, flags);
-    return JS_NewString(ctx, buf);
+    JSValue res;
+    if (p->len == 1) {
+        res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]);
+        js_free(ctx, p);
+        return res;
+    } else {
+        return JS_MKPTR(JS_TAG_BIG_INT, p);
+    }
 }
 
-static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
+/* XXX: remove */
+static double js_strtod(const char *str, int radix, BOOL is_float)
 {
-    char buf[2200], *ptr, *ptr2;
-    /* d is finite */
-    int sign = d < 0;
-    int digit;
-    double frac, d0;
-    int64_t n0 = 0;
-    d = fabs(d);
-    d0 = trunc(d);
-    frac = d - d0;
-    ptr = buf + 1100;
-    *ptr = '\0';
-    if (d0 <= MAX_SAFE_INTEGER) {
-        int64_t n = n0 = (int64_t)d0;
-        while (n >= radix) {
-            digit = n % radix;
-            n = n / radix;
-            *--ptr = digits[digit];
-        }
-        *--ptr = digits[(int)n];
-    } else {
-        /* no decimals */
-        while (d0 >= radix) {
-            digit = fmod(d0, radix);
-            d0 = trunc(d0 / radix);
-            if (d0 >= MAX_SAFE_INTEGER)
-                digit = 0;
-            *--ptr = digits[digit];
+    double d;
+    int c;
+
+    if (!is_float || radix != 10) {
+        const char *p = str;
+        uint64_t n_max, n;
+        int int_exp, is_neg;
+
+        is_neg = 0;
+        if (*p == '-') {
+            is_neg = 1;
+            p++;
         }
-        *--ptr = digits[(int)d0];
-        goto done;
-    }
-    if (frac != 0) {
-        double log2_radix = log2(radix);
-        double prec = 1023 + 51;  // handle subnormals
-        ptr2 = buf + 1100;
-        *ptr2++ = '.';
-        while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
-            frac *= radix;
-            digit = trunc(frac);
-            frac -= digit;
-            *ptr2++ = digits[digit];
-            n0 = n0 * radix + digit;
-            prec -= log2_radix;
+
+        /* skip leading zeros */
+        while (*p == '0')
+            p++;
+        n = 0;
+        if (radix == 10)
+            n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
+        else
+            n_max = ((uint64_t)-1 - (radix - 1)) / radix;
+        /* XXX: could be more precise */
+        int_exp = 0;
+        while (*p != '\0') {
+            c = to_digit((uint8_t)*p);
+            if (c >= radix)
+                break;
+            if (n <= n_max) {
+                n = n * radix + c;
+            } else {
+                if (radix == 10)
+                    goto strtod_case;
+                int_exp++;
+            }
+            p++;
         }
-        *ptr2 = '\0';
-        if (frac * radix >= radix / 2) {
-            char nine = digits[radix - 1];
-            // round to closest
-            while (ptr2[-1] == nine)
-                *--ptr2 = '\0';
-            if (ptr2[-1] == '.') {
-                *--ptr2 = '\0';
-                while (ptr2[-1] == nine)
-                    *--ptr2 = '0';
+        d = n;
+        if (int_exp != 0) {
+            d *= pow(radix, int_exp);
+        }
+        if (is_neg)
+            d = -d;
+    } else {
+    strtod_case:
+        d = strtod(str, NULL);
+    }
+    return d;
+}
+
+#define ATOD_INT_ONLY        (1 << 0)
+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
+#define ATOD_ACCEPT_BIN_OCT  (1 << 2)
+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
+#define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)
+/* accept _ between digits as a digit separator */
+#define ATOD_ACCEPT_UNDERSCORES  (1 << 5)
+/* allow a suffix to override the type */
+#define ATOD_ACCEPT_SUFFIX    (1 << 6)
+/* default type */
+#define ATOD_TYPE_MASK        (3 << 7)
+#define ATOD_TYPE_FLOAT64     (0 << 7)
+#define ATOD_TYPE_BIG_INT     (1 << 7)
+/* accept -0x1 */
+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
+
+/* return an exception in case of memory error. Return JS_NAN if
+   invalid syntax */
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+                       int radix, int flags)
+{
+    const char *p, *p_start;
+    int sep, is_neg;
+    BOOL is_float, has_legacy_octal;
+    int atod_type = flags & ATOD_TYPE_MASK;
+    char buf1[64], *buf;
+    int i, j, len;
+    BOOL buf_allocated = FALSE;
+    JSValue val;
+
+    /* optional separator between digits */
+    sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+    has_legacy_octal = FALSE;
+
+    p = str;
+    p_start = p;
+    is_neg = 0;
+    if (p[0] == '+') {
+        p++;
+        p_start++;
+        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+            goto no_radix_prefix;
+    } else if (p[0] == '-') {
+        p++;
+        p_start++;
+        is_neg = 1;
+        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+            goto no_radix_prefix;
+    }
+    if (p[0] == '0') {
+        if ((p[1] == 'x' || p[1] == 'X') &&
+            (radix == 0 || radix == 16)) {
+            p += 2;
+            radix = 16;
+        } else if ((p[1] == 'o' || p[1] == 'O') &&
+                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+            p += 2;
+            radix = 8;
+        } else if ((p[1] == 'b' || p[1] == 'B') &&
+                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+            p += 2;
+            radix = 2;
+        } else if ((p[1] >= '0' && p[1] <= '9') &&
+                   radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
+            int i;
+            has_legacy_octal = TRUE;
+            sep = 256;
+            for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+                continue;
+            if (p[i] == '8' || p[i] == '9')
+                goto no_prefix;
+            p += 1;
+            radix = 8;
+        } else {
+            goto no_prefix;
+        }
+        /* there must be a digit after the prefix */
+        if (to_digit((uint8_t)*p) >= radix)
+            goto fail;
+    no_prefix: ;
+    } else {
+ no_radix_prefix:
+        if (!(flags & ATOD_INT_ONLY) &&
+            (atod_type == ATOD_TYPE_FLOAT64) &&
+            strstart(p, "Infinity", &p)) {
+            double d = 1.0 / 0.0;
+            if (is_neg)
+                d = -d;
+            val = JS_NewFloat64(ctx, d);
+            goto done;
+        }
+    }
+    if (radix == 0)
+        radix = 10;
+    is_float = FALSE;
+    p_start = p;
+    while (to_digit((uint8_t)*p) < radix
+           ||  (*p == sep && (radix != 10 ||
+                              p != p_start + 1 || p[-1] != '0') &&
+                to_digit((uint8_t)p[1]) < radix)) {
+        p++;
+    }
+    if (!(flags & ATOD_INT_ONLY)) {
+        if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
+            is_float = TRUE;
+            p++;
+            if (*p == sep)
+                goto fail;
+            while (to_digit((uint8_t)*p) < radix ||
+                   (*p == sep && to_digit((uint8_t)p[1]) < radix))
+                p++;
+        }
+        if (p > p_start &&
+            (((*p == 'e' || *p == 'E') && radix == 10) ||
+             ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+            const char *p1 = p + 1;
+            is_float = TRUE;
+            if (*p1 == '+') {
+                p1++;
+            } else if (*p1 == '-') {
+                p1++;
             }
-            if (ptr2 - 1 == ptr)
-                *--ptr = '1';
-            else
-                ptr2[-1] += 1;
+            if (is_digit((uint8_t)*p1)) {
+                p = p1 + 1;
+                while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+                    p++;
+            }
+        }
+    }
+    if (p == p_start)
+        goto fail;
+
+    buf = buf1;
+    buf_allocated = FALSE;
+    len = p - p_start;
+    if (unlikely((len + 2) > sizeof(buf1))) {
+        buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
+        if (!buf)
+            goto mem_error;
+        buf_allocated = TRUE;
+    }
+    /* remove the separators and the radix prefixes */
+    j = 0;
+    if (is_neg)
+        buf[j++] = '-';
+    for (i = 0; i < len; i++) {
+        if (p_start[i] != '_')
+            buf[j++] = p_start[i];
+    }
+    buf[j] = '\0';
+
+    if (flags & ATOD_ACCEPT_SUFFIX) {
+        if (*p == 'n') {
+            p++;
+            atod_type = ATOD_TYPE_BIG_INT;
         } else {
-            while (ptr2[-1] == '0')
-                *--ptr2 = '\0';
-            if (ptr2[-1] == '.')
-                *--ptr2 = '\0';
+            if (is_float && radix != 10)
+                goto fail;
+        }
+    } else {
+        if (atod_type == ATOD_TYPE_FLOAT64) {
+            if (is_float && radix != 10)
+                goto fail;
+        }
+    }
+
+    switch(atod_type) {
+    case ATOD_TYPE_FLOAT64:
+        {
+            double d;
+            d = js_strtod(buf, radix, is_float);
+            /* return int or float64 */
+            val = JS_NewFloat64(ctx, d);
+        }
+        break;
+    case ATOD_TYPE_BIG_INT:
+        {
+            JSBigInt *r;
+            if (has_legacy_octal || is_float)
+                goto fail;
+            r = js_bigint_from_string(ctx, buf, radix);
+            if (!r)
+                goto mem_error;
+            val = JS_CompactBigInt(ctx, r);
         }
+        break;
+    default:
+        abort();
     }
+
 done:
-    ptr[-1] = '-';
-    ptr -= sign;
-    return JS_NewString(ctx, ptr);
+    if (buf_allocated)
+        js_free_rt(ctx->rt, buf);
+    if (pp)
+        *pp = p;
+    return val;
+ fail:
+    val = JS_NAN;
+    goto done;
+ mem_error:
+    val = JS_ThrowOutOfMemory(ctx);
+    goto done;
 }
 
-JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
+typedef enum JSToNumberHintEnum {
+    TON_FLAG_NUMBER,
+    TON_FLAG_NUMERIC,
+} JSToNumberHintEnum;
+
+static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
+                                   JSToNumberHintEnum flag)
 {
     uint32_t tag;
-    const char *str;
-    char buf[32];
+    JSValue ret;
 
+ redo:
     tag = JS_VALUE_GET_NORM_TAG(val);
     switch(tag) {
-    case JS_TAG_STRING:
-        return JS_DupValue(ctx, val);
+    case JS_TAG_BIG_INT:
+    case JS_TAG_SHORT_BIG_INT:
+        if (flag != TON_FLAG_NUMERIC) {
+            JS_FreeValue(ctx, val);
+            return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+        }
+        ret = val;
+        break;
+    case JS_TAG_FLOAT64:
     case JS_TAG_INT:
-        snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
-        str = buf;
-        goto new_string;
+    case JS_TAG_EXCEPTION:
+        ret = val;
+        break;
     case JS_TAG_BOOL:
-        return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
-                          JS_ATOM_true : JS_ATOM_false);
     case JS_TAG_NULL:
-        return JS_AtomToString(ctx, JS_ATOM_null);
+        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+        break;
     case JS_TAG_UNDEFINED:
-        return JS_AtomToString(ctx, JS_ATOM_undefined);
-    case JS_TAG_EXCEPTION:
-        return JS_EXCEPTION;
+        ret = JS_NAN;
+        break;
     case JS_TAG_OBJECT:
+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+        if (JS_IsException(val))
+            return JS_EXCEPTION;
+        goto redo;
+    case JS_TAG_STRING:
         {
-            JSValue val1, ret;
-            val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
-            if (JS_IsException(val1))
-                return val1;
-            ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
-            JS_FreeValue(ctx, val1);
-            return ret;
+            const char *str;
+            const char *p;
+            size_t len;
+
+            str = JS_ToCStringLen(ctx, &len, val);
+            JS_FreeValue(ctx, val);
+            if (!str)
+                return JS_EXCEPTION;
+            p = str;
+            p += skip_spaces(p);
+            if ((p - str) == len) {
+                ret = JS_NewInt32(ctx, 0);
+            } else {
+                int flags = ATOD_ACCEPT_BIN_OCT;
+                ret = js_atof(ctx, p, &p, 0, flags);
+                if (!JS_IsException(ret)) {
+                    p += skip_spaces(p);
+                    if ((p - str) != len) {
+                        JS_FreeValue(ctx, ret);
+                        ret = JS_NAN;
+                    }
+                }
+            }
+            JS_FreeCString(ctx, str);
         }
         break;
-    case JS_TAG_FUNCTION_BYTECODE:
-        str = "[function bytecode]";
-        goto new_string;
     case JS_TAG_SYMBOL:
-        if (is_ToPropertyKey) {
-            return JS_DupValue(ctx, val);
-        } else {
-            return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
-        }
-    case JS_TAG_FLOAT64:
-        return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
-                       JS_DTOA_VAR_FORMAT);
-    case JS_TAG_BIG_INT:
-        return ctx->rt->bigint_ops.to_string(ctx, val);
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        return ctx->rt->bigfloat_ops.to_string(ctx, val);
-    case JS_TAG_BIG_DECIMAL:
-        return ctx->rt->bigdecimal_ops.to_string(ctx, val);
-#endif
+        JS_FreeValue(ctx, val);
+        return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
     default:
-        str = "[unsupported type]";
-    new_string:
-        return JS_NewString(ctx, str);
+        JS_FreeValue(ctx, val);
+        ret = JS_NAN;
+        break;
     }
+    return ret;
 }
 
-JSValue JS_ToString(JSContext *ctx, JSValueConst val)
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
 {
-    return JS_ToStringInternal(ctx, val, FALSE);
+    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
 }
 
-static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
 {
-    JSValue ret;
-    ret = JS_ToString(ctx, val);
-    JS_FreeValue(ctx, val);
-    return ret;
+    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
 }
 
-static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
+static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
 {
-    if (JS_IsUndefined(val) || JS_IsNull(val))
-        return JS_ToStringFree(ctx, val);
-    return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+    return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
 }
 
-JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
+                                          JSValue val)
 {
-    return JS_ToStringInternal(ctx, val, TRUE);
+    double d;
+    uint32_t tag;
+    
+    val = JS_ToNumberFree(ctx, val);
+    if (JS_IsException(val))
+        goto fail;
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+        d = JS_VALUE_GET_INT(val);
+        break;
+    case JS_TAG_FLOAT64:
+        d = JS_VALUE_GET_FLOAT64(val);
+        break;
+    default:
+        abort();
+    }
+    *pres = d;
+    return 0;
+ fail:
+    *pres = JS_FLOAT64_NAN;
+    return -1;
 }
 
-static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
 {
-    uint32_t tag = JS_VALUE_GET_TAG(val);
-    if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
-        return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
-    return JS_ToString(ctx, val);
+    uint32_t tag;
+
+    tag = JS_VALUE_GET_TAG(val);
+    if (tag <= JS_TAG_NULL) {
+        *pres = JS_VALUE_GET_INT(val);
+        return 0;
+    } else if (JS_TAG_IS_FLOAT64(tag)) {
+        *pres = JS_VALUE_GET_FLOAT64(val);
+        return 0;
+    } else {
+        return __JS_ToFloat64Free(ctx, pres, val);
+    }
 }
 
-static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
 {
-    JSValue val;
-    JSString *p;
-    int i;
-    uint32_t c;
-    StringBuffer b_s, *b = &b_s;
-    char buf[16];
+    return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+}
 
-    val = JS_ToStringCheckObject(ctx, val1);
-    if (JS_IsException(val))
-        return val;
-    p = JS_VALUE_GET_STRING(val);
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+{
+    return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+}
 
-    if (string_buffer_init(ctx, b, p->len + 2))
-        goto fail;
+/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+{
+    uint32_t tag;
+    JSValue ret;
 
-    if (string_buffer_putc8(b, '\"'))
-        goto fail;
-    for(i = 0; i < p->len; ) {
-        c = string_getc(p, &i);
-        switch(c) {
-        case '\t':
-            c = 't';
-            goto quote;
-        case '\r':
-            c = 'r';
-            goto quote;
-        case '\n':
-            c = 'n';
-            goto quote;
-        case '\b':
-            c = 'b';
-            goto quote;
-        case '\f':
-            c = 'f';
-            goto quote;
-        case '\"':
-        case '\\':
-        quote:
-            if (string_buffer_putc8(b, '\\'))
-                goto fail;
-            if (string_buffer_putc8(b, c))
-                goto fail;
-            break;
-        default:
-            if (c < 32 || is_surrogate(c)) {
-                snprintf(buf, sizeof(buf), "\\u%04x", c);
-                if (string_buffer_puts8(b, buf))
-                    goto fail;
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+    case JS_TAG_BOOL:
+    case JS_TAG_NULL:
+    case JS_TAG_UNDEFINED:
+        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+        break;
+    case JS_TAG_FLOAT64:
+        {
+            double d = JS_VALUE_GET_FLOAT64(val);
+            if (isnan(d)) {
+                ret = JS_NewInt32(ctx, 0);
             } else {
-                if (string_buffer_putc(b, c))
-                    goto fail;
+                /* convert -0 to +0 */
+                d = trunc(d) + 0.0;
+                ret = JS_NewFloat64(ctx, d);
             }
-            break;
         }
+        break;
+    default:
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val))
+            return val;
+        goto redo;
     }
-    if (string_buffer_putc8(b, '\"'))
-        goto fail;
-    JS_FreeValue(ctx, val);
-    return string_buffer_end(b);
- fail:
-    JS_FreeValue(ctx, val);
-    string_buffer_free(b);
-    return JS_EXCEPTION;
+    return ret;
 }
 
-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+/* Note: the integer value is satured to 32 bits */
+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
 {
-    printf("%14s %4s %4s %14s %10s %s\n",
-           "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
-}
+    uint32_t tag;
+    int ret;
 
-/* for debug only: dump an object without side effect */
-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
-{
-    uint32_t i;
-    char atom_buf[ATOM_GET_STR_BUF_SIZE];
-    JSShape *sh;
-    JSShapeProperty *prs;
-    JSProperty *pr;
-    BOOL is_first = TRUE;
-
-    /* XXX: should encode atoms with special characters */
-    sh = p->shape; /* the shape can be NULL while freeing an object */
-    printf("%14p %4d ",
-           (void *)p,
-           p->header.ref_count);
-    if (sh) {
-        printf("%3d%c %14p ",
-               sh->header.ref_count,
-               " *"[sh->is_hashed],
-               (void *)sh->proto);
-    } else {
-        printf("%3s  %14s ", "-", "-");
-    }
-    printf("%10s ",
-           JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
-    if (p->is_exotic && p->fast_array) {
-        printf("[ ");
-        for(i = 0; i < p->u.array.count; i++) {
-            if (i != 0)
-                printf(", ");
-            switch (p->class_id) {
-            case JS_CLASS_ARRAY:
-            case JS_CLASS_ARGUMENTS:
-                JS_DumpValueShort(rt, p->u.array.u.values[i]);
-                break;
-            case JS_CLASS_UINT8C_ARRAY:
-            case JS_CLASS_INT8_ARRAY:
-            case JS_CLASS_UINT8_ARRAY:
-            case JS_CLASS_INT16_ARRAY:
-            case JS_CLASS_UINT16_ARRAY:
-            case JS_CLASS_INT32_ARRAY:
-            case JS_CLASS_UINT32_ARRAY:
-            case JS_CLASS_BIG_INT64_ARRAY:
-            case JS_CLASS_BIG_UINT64_ARRAY:
-            case JS_CLASS_FLOAT32_ARRAY:
-            case JS_CLASS_FLOAT64_ARRAY:
-                {
-                    int size = 1 << typed_array_size_log2(p->class_id);
-                    const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
-                    while (size-- > 0)
-                        printf("%02X", *b++);
-                }
-                break;
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+    case JS_TAG_BOOL:
+    case JS_TAG_NULL:
+    case JS_TAG_UNDEFINED:
+        ret = JS_VALUE_GET_INT(val);
+        break;
+    case JS_TAG_EXCEPTION:
+        *pres = 0;
+        return -1;
+    case JS_TAG_FLOAT64:
+        {
+            double d = JS_VALUE_GET_FLOAT64(val);
+            if (isnan(d)) {
+                ret = 0;
+            } else {
+                if (d < INT32_MIN)
+                    ret = INT32_MIN;
+                else if (d > INT32_MAX)
+                    ret = INT32_MAX;
+                else
+                    ret = (int)d;
             }
         }
-        printf(" ] ");
-    }
-
-    if (sh) {
-        printf("{ ");
-        for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
-            if (prs->atom != JS_ATOM_NULL) {
-                pr = &p->prop[i];
-                if (!is_first)
-                    printf(", ");
-                printf("%s: ",
-                       JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
-                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-                    printf("[getset %p %p]", (void *)pr->u.getset.getter,
-                           (void *)pr->u.getset.setter);
-                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
-                    printf("[varref %p]", (void *)pr->u.var_ref);
-                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
-                    printf("[autoinit %p %d %p]",
-                           (void *)js_autoinit_get_realm(pr),
-                           js_autoinit_get_id(pr),
-                           (void *)pr->u.init.opaque);
-                } else {
-                    JS_DumpValueShort(rt, pr->u.value);
-                }
-                is_first = FALSE;
-            }
+        break;
+    default:
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val)) {
+            *pres = 0;
+            return -1;
         }
-        printf(" }");
+        goto redo;
     }
+    *pres = ret;
+    return 0;
+}
 
-    if (js_class_has_bytecode(p->class_id)) {
-        JSFunctionBytecode *b = p->u.func.function_bytecode;
-        JSVarRef **var_refs;
-        if (b->closure_var_count) {
-            var_refs = p->u.func.var_refs;
-            printf(" Closure:");
-            for(i = 0; i < b->closure_var_count; i++) {
-                printf(" ");
-                JS_DumpValueShort(rt, var_refs[i]->value);
-            }
-            if (p->u.func.home_object) {
-                printf(" HomeObject: ");
-                JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
-            }
-        }
-    }
-    printf("\n");
+int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+{
+    return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
 }
 
-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
+                    int min, int max, int min_offset)
 {
-    if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
-        JS_DumpObject(rt, (JSObject *)p);
-    } else {
-        printf("%14p %4d ",
-               (void *)p,
-               p->ref_count);
-        switch(p->gc_obj_type) {
-        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
-            printf("[function bytecode]");
-            break;
-        case JS_GC_OBJ_TYPE_SHAPE:
-            printf("[shape]");
-            break;
-        case JS_GC_OBJ_TYPE_VAR_REF:
-            printf("[var_ref]");
-            break;
-        case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
-            printf("[async_function]");
-            break;
-        case JS_GC_OBJ_TYPE_JS_CONTEXT:
-            printf("[js_context]");
-            break;
-        default:
-            printf("[unknown %d]", p->gc_obj_type);
-            break;
+    int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+    if (res == 0) {
+        if (*pres < min) {
+            *pres += min_offset;
+            if (*pres < min)
+                *pres = min;
+        } else {
+            if (*pres > max)
+                *pres = max;
         }
-        printf("\n");
     }
+    return res;
 }
 
-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
-                                                      JSValueConst val)
+static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
 {
-    uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
-    const char *str;
+    uint32_t tag;
 
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
     switch(tag) {
     case JS_TAG_INT:
-        printf("%d", JS_VALUE_GET_INT(val));
-        break;
     case JS_TAG_BOOL:
-        if (JS_VALUE_GET_BOOL(val))
-            str = "true";
-        else
-            str = "false";
-        goto print_str;
     case JS_TAG_NULL:
-        str = "null";
-        goto print_str;
-    case JS_TAG_EXCEPTION:
-        str = "exception";
-        goto print_str;
-    case JS_TAG_UNINITIALIZED:
-        str = "uninitialized";
-        goto print_str;
     case JS_TAG_UNDEFINED:
-        str = "undefined";
-    print_str:
-        printf("%s", str);
-        break;
+        *pres = JS_VALUE_GET_INT(val);
+        return 0;
+    case JS_TAG_EXCEPTION:
+        *pres = 0;
+        return -1;
     case JS_TAG_FLOAT64:
-        printf("%.14g", JS_VALUE_GET_FLOAT64(val));
-        break;
-    case JS_TAG_BIG_INT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            char *str;
-            str = bf_ftoa(NULL, &p->num, 10, 0,
-                          BF_RNDZ | BF_FTOA_FORMAT_FRAC);
-            printf("%sn", str);
-            bf_realloc(&rt->bf_ctx, str, 0);
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            char *str;
-            str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
-                          BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
-            printf("%sl", str);
-            bf_free(&rt->bf_ctx, str);
-        }
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        {
-            JSBigDecimal *p = JS_VALUE_GET_PTR(val);
-            char *str;
-            str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
-                             BF_RNDZ | BF_FTOA_FORMAT_FREE);
-            printf("%sm", str);
-            bf_free(&rt->bf_ctx, str);
-        }
-        break;
-#endif
-    case JS_TAG_STRING:
-        {
-            JSString *p;
-            p = JS_VALUE_GET_STRING(val);
-            JS_DumpString(rt, p);
-        }
-        break;
-    case JS_TAG_FUNCTION_BYTECODE:
-        {
-            JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
-            char buf[ATOM_GET_STR_BUF_SIZE];
-            printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
-        }
-        break;
-    case JS_TAG_OBJECT:
-        {
-            JSObject *p = JS_VALUE_GET_OBJ(val);
-            JSAtom atom = rt->class_array[p->class_id].class_name;
-            char atom_buf[ATOM_GET_STR_BUF_SIZE];
-            printf("[%s %p]",
-                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
-        }
-        break;
-    case JS_TAG_SYMBOL:
         {
-            JSAtomStruct *p = JS_VALUE_GET_PTR(val);
-            char atom_buf[ATOM_GET_STR_BUF_SIZE];
-            printf("Symbol(%s)",
-                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+            double d = JS_VALUE_GET_FLOAT64(val);
+            if (isnan(d)) {
+                *pres = 0;
+            } else {
+                if (d < INT64_MIN)
+                    *pres = INT64_MIN;
+                else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
+                    *pres = INT64_MAX;
+                else
+                    *pres = (int64_t)d;
+            }
         }
-        break;
-    case JS_TAG_MODULE:
-        printf("[module]");
-        break;
+        return 0;
     default:
-        printf("[unknown tag %d]", tag);
-        break;
-    }
-}
-
-static __maybe_unused void JS_DumpValue(JSContext *ctx,
-                                                 JSValueConst val)
-{
-    JS_DumpValueShort(ctx->rt, val);
-}
-
-static __maybe_unused void JS_PrintValue(JSContext *ctx,
-                                                  const char *str,
-                                                  JSValueConst val)
-{
-    printf("%s=", str);
-    JS_DumpValueShort(ctx->rt, val);
-    printf("\n");
-}
-
-/* return -1 if exception (proxy case) or TRUE/FALSE */
-// TODO: should take flags to make proxy resolution and exceptions optional
-int JS_IsArray(JSContext *ctx, JSValueConst val)
-{
-    if (js_resolve_proxy(ctx, &val, TRUE))
-        return -1;
-    if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
-        JSObject *p = JS_VALUE_GET_OBJ(val);
-        return p->class_id == JS_CLASS_ARRAY;
-    } else {
-        return FALSE;
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val)) {
+            *pres = 0;
+            return -1;
+        }
+        goto redo;
     }
 }
 
-static double js_pow(double a, double b)
+int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
 {
-    if (unlikely(!isfinite(b)) && fabs(a) == 1) {
-        /* not compatible with IEEE 754 */
-        return JS_FLOAT64_NAN;
-    } else {
-        return pow(a, b);
-    }
+    return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
 }
 
-JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
+int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
+                    int64_t min, int64_t max, int64_t neg_offset)
 {
-    JSValue val;
-    bf_t *a;
-    val = JS_NewBigInt(ctx);
-    if (JS_IsException(val))
-        return val;
-    a = JS_GetBigInt(val);
-    if (bf_set_si(a, v)) {
-        JS_FreeValue(ctx, val);
-        return JS_ThrowOutOfMemory(ctx);
+    int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+    if (res == 0) {
+        if (*pres < 0)
+            *pres += neg_offset;
+        if (*pres < min)
+            *pres = min;
+        else if (*pres > max)
+            *pres = max;
     }
-    return val;
+    return res;
 }
 
-JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
+   in case of exception */
+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
 {
-    if (is_math_mode(ctx) &&
-        v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
-        return JS_NewInt64(ctx, v);
-    } else {
-        return JS_NewBigInt64_1(ctx, v);
-    }
-}
+    uint32_t tag;
+    int64_t ret;
 
-JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
-{
-    JSValue val;
-    if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
-        val = JS_NewInt64(ctx, v);
-    } else {
-        bf_t *a;
-        val = JS_NewBigInt(ctx);
-        if (JS_IsException(val))
-            return val;
-        a = JS_GetBigInt(val);
-        if (bf_set_ui(a, v)) {
-            JS_FreeValue(ctx, val);
-            return JS_ThrowOutOfMemory(ctx);
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+    case JS_TAG_BOOL:
+    case JS_TAG_NULL:
+    case JS_TAG_UNDEFINED:
+        ret = JS_VALUE_GET_INT(val);
+        break;
+    case JS_TAG_FLOAT64:
+        {
+            JSFloat64Union u;
+            double d;
+            int e;
+            d = JS_VALUE_GET_FLOAT64(val);
+            u.d = d;
+            /* we avoid doing fmod(x, 2^64) */
+            e = (u.u64 >> 52) & 0x7ff;
+            if (likely(e <= (1023 + 62))) {
+                /* fast case */
+                ret = (int64_t)d;
+            } else if (e <= (1023 + 62 + 53)) {
+                uint64_t v;
+                /* remainder modulo 2^64 */
+                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+                ret = v << ((e - 1023) - 52);
+                /* take the sign into account */
+                if (u.u64 >> 63)
+                    ret = -ret;
+            } else {
+                ret = 0; /* also handles NaN and +inf */
+            }
+        }
+        break;
+    default:
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val)) {
+            *pres = 0;
+            return -1;
         }
+        goto redo;
     }
-    return val;
+    *pres = ret;
+    return 0;
 }
 
-/* return NaN if bad bigint literal */
-static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
 {
-    const char *str, *p;
-    size_t len;
-    int flags;
-
-    str = JS_ToCStringLen(ctx, &len, val);
-    JS_FreeValue(ctx, val);
-    if (!str)
-        return JS_EXCEPTION;
-    p = str;
-    p += skip_spaces(p);
-    if ((p - str) == len) {
-        val = JS_NewBigInt64(ctx, 0);
-    } else {
-        flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
-#ifdef CONFIG_BIGNUM
-        if (is_math_mode(ctx))
-            flags |= ATOD_MODE_BIGINT;
-#endif
-        val = js_atof(ctx, p, &p, 0, flags);
-        p += skip_spaces(p);
-        if (!JS_IsException(val)) {
-            if ((p - str) != len) {
-                JS_FreeValue(ctx, val);
-                val = JS_NAN;
-            }
-        }
-    }
-    JS_FreeCString(ctx, str);
-    return val;
+    return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
 }
 
-static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
 {
-    val = JS_StringToBigInt(ctx, val);
-    if (JS_VALUE_IS_NAN(val))
-        return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
-    return val;
+    if (JS_IsBigInt(ctx, val))
+        return JS_ToBigInt64(ctx, pres, val);
+    else
+        return JS_ToInt64(ctx, pres, val);
 }
 
-/* if the returned bigfloat is allocated it is equal to
-   'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
-static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
+/* return (<0, 0) in case of exception */
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
 {
     uint32_t tag;
-    bf_t *r;
-    JSBigFloat *p;
+    int32_t ret;
 
  redo:
     tag = JS_VALUE_GET_NORM_TAG(val);
     switch(tag) {
     case JS_TAG_INT:
+    case JS_TAG_BOOL:
     case JS_TAG_NULL:
     case JS_TAG_UNDEFINED:
-        if (!is_math_mode(ctx))
-            goto fail;
-        /* fall tru */
-    case JS_TAG_BOOL:
-        r = buf;
-        bf_init(ctx->bf_ctx, r);
-        bf_set_si(r, JS_VALUE_GET_INT(val));
+        ret = JS_VALUE_GET_INT(val);
         break;
     case JS_TAG_FLOAT64:
         {
-            double d = JS_VALUE_GET_FLOAT64(val);
-            if (!is_math_mode(ctx))
-                goto fail;
-            if (!isfinite(d))
-                goto fail;
-            r = buf;
-            bf_init(ctx->bf_ctx, r);
-            d = trunc(d);
-            bf_set_float64(r, d);
+            JSFloat64Union u;
+            double d;
+            int e;
+            d = JS_VALUE_GET_FLOAT64(val);
+            u.d = d;
+            /* we avoid doing fmod(x, 2^32) */
+            e = (u.u64 >> 52) & 0x7ff;
+            if (likely(e <= (1023 + 30))) {
+                /* fast case */
+                ret = (int32_t)d;
+            } else if (e <= (1023 + 30 + 53)) {
+                uint64_t v;
+                /* remainder modulo 2^32 */
+                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+                v = v << ((e - 1023) - 52 + 32);
+                ret = v >> 32;
+                /* take the sign into account */
+                if (u.u64 >> 63)
+                    ret = -ret;
+            } else {
+                ret = 0; /* also handles NaN and +inf */
+            }
         }
         break;
-    case JS_TAG_BIG_INT:
-        p = JS_VALUE_GET_PTR(val);
-        r = &p->num;
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        if (!is_math_mode(ctx))
-            goto fail;
-        p = JS_VALUE_GET_PTR(val);
-        if (!bf_is_finite(&p->num))
-            goto fail;
-        r = buf;
-        bf_init(ctx->bf_ctx, r);
-        bf_set(r, &p->num);
-        bf_rint(r, BF_RNDZ);
-        JS_FreeValue(ctx, val);
-        break;
-#endif
-    case JS_TAG_STRING:
-        val = JS_StringToBigIntErr(ctx, val);
-        if (JS_IsException(val))
-            return NULL;
-        goto redo;
-    case JS_TAG_OBJECT:
-        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
-        if (JS_IsException(val))
-            return NULL;
-        goto redo;
     default:
-    fail:
-        JS_FreeValue(ctx, val);
-        JS_ThrowTypeError(ctx, "cannot convert to bigint");
-        return NULL;
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val)) {
+            *pres = 0;
+            return -1;
+        }
+        goto redo;
     }
-    return r;
+    *pres = ret;
+    return 0;
 }
 
-static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
 {
-    return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
+    return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
 }
 
-static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
+static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
 {
-    if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
-        return val;
-    } else {
-        bf_t a_s, *a, *r;
-        int ret;
-        JSValue res;
-
-        res = JS_NewBigInt(ctx);
-        if (JS_IsException(res))
-            return JS_EXCEPTION;
-        a = JS_ToBigIntFree(ctx, &a_s, val);
-        if (!a) {
-            JS_FreeValue(ctx, res);
-            return JS_EXCEPTION;
-        }
-        r = JS_GetBigInt(res);
-        ret = bf_set(r, a);
-        JS_FreeBigInt(ctx, a, &a_s);
-        if (ret) {
-            JS_FreeValue(ctx, res);
-            return JS_ThrowOutOfMemory(ctx);
-        }
-        return JS_CompactBigInt(ctx, res);
-    }
+    return JS_ToInt32Free(ctx, (int32_t *)pres, val);
 }
 
-/* free the bf_t allocated by JS_ToBigInt */
-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
 {
-    if (a == buf) {
-        bf_delete(a);
-    } else {
-        JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
-                                       offsetof(JSBigFloat, num));
-        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p));
-    }
-}
-
-/* XXX: merge with JS_ToInt64Free with a specific flag */
-static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
-{
-    bf_t a_s, *a;
+    uint32_t tag;
+    int res;
 
-    a = JS_ToBigIntFree(ctx, &a_s, val);
-    if (!a) {
-        *pres = 0;
-        return -1;
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+    case JS_TAG_BOOL:
+    case JS_TAG_NULL:
+    case JS_TAG_UNDEFINED:
+        res = JS_VALUE_GET_INT(val);
+        res = max_int(0, min_int(255, res));
+        break;
+    case JS_TAG_FLOAT64:
+        {
+            double d = JS_VALUE_GET_FLOAT64(val);
+            if (isnan(d)) {
+                res = 0;
+            } else {
+                if (d < 0)
+                    res = 0;
+                else if (d > 255)
+                    res = 255;
+                else
+                    res = lrint(d);
+            }
+        }
+        break;
+    default:
+        val = JS_ToNumberFree(ctx, val);
+        if (JS_IsException(val)) {
+            *pres = 0;
+            return -1;
+        }
+        goto redo;
     }
-    bf_get_int64(pres, a, BF_GET_INT_MOD);
-    JS_FreeBigInt(ctx, a, &a_s);
+    *pres = res;
     return 0;
 }
 
-int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+                                            JSValue val, BOOL is_array_ctor)
 {
-    return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
-}
+    uint32_t tag, len;
 
-static JSBigFloat *js_new_bf(JSContext *ctx)
-{
-    JSBigFloat *p;
-    p = js_malloc(ctx, sizeof(*p));
-    if (!p)
-        return NULL;
-    p->header.ref_count = 1;
-    bf_init(ctx->bf_ctx, &p->num);
-    return p;
+    tag = JS_VALUE_GET_TAG(val);
+    switch(tag) {
+    case JS_TAG_INT:
+    case JS_TAG_BOOL:
+    case JS_TAG_NULL:
+        {
+            int v;
+            v = JS_VALUE_GET_INT(val);
+            if (v < 0)
+                goto fail;
+            len = v;
+        }
+        break;
+    default:
+        if (JS_TAG_IS_FLOAT64(tag)) {
+            double d;
+            d = JS_VALUE_GET_FLOAT64(val);
+            if (!(d >= 0 && d <= UINT32_MAX))
+                goto fail;
+            len = (uint32_t)d;
+            if (len != d)
+                goto fail;
+        } else {
+            uint32_t len1;
+
+            if (is_array_ctor) {
+                val = JS_ToNumberFree(ctx, val);
+                if (JS_IsException(val))
+                    return -1;
+                /* cannot recurse because val is a number */
+                if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
+                    return -1;
+            } else {
+                /* legacy behavior: must do the conversion twice and compare */
+                if (JS_ToUint32(ctx, &len, val)) {
+                    JS_FreeValue(ctx, val);
+                    return -1;
+                }
+                val = JS_ToNumberFree(ctx, val);
+                if (JS_IsException(val))
+                    return -1;
+                /* cannot recurse because val is a number */
+                if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
+                    return -1;
+                if (len1 != len) {
+                fail:
+                    JS_ThrowRangeError(ctx, "invalid array length");
+                    return -1;
+                }
+            }
+        }
+        break;
+    }
+    *plen = len;
+    return 0;
 }
 
-static JSValue JS_NewBigInt(JSContext *ctx)
+#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
+
+static BOOL is_safe_integer(double d)
 {
-    JSBigFloat *p;
-    p = js_malloc(ctx, sizeof(*p));
-    if (!p)
-        return JS_EXCEPTION;
-    p->header.ref_count = 1;
-    bf_init(ctx->bf_ctx, &p->num);
-    return JS_MKPTR(JS_TAG_BIG_INT, p);
+    return isfinite(d) && floor(d) == d &&
+        fabs(d) <= (double)MAX_SAFE_INTEGER;
 }
 
-static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
-                                 BOOL convert_to_safe_integer)
+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
 {
     int64_t v;
-    bf_t *a;
-
-    if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
-        return val; /* fail safe */
-    a = JS_GetBigInt(val);
-    if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
-        v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
-        JS_FreeValue(ctx, val);
-        return JS_NewInt64(ctx, v);
-    } else if (a->expn == BF_EXP_ZERO && a->sign) {
-        JSBigFloat *p = JS_VALUE_GET_PTR(val);
-        assert(p->header.ref_count == 1);
-        a->sign = 0;
+    if (JS_ToInt64Sat(ctx, &v, val))
+        return -1;
+    if (v < 0 || v > MAX_SAFE_INTEGER) {
+        JS_ThrowRangeError(ctx, "invalid array index");
+        *plen = 0;
+        return -1;
     }
-    return val;
+    *plen = v;
+    return 0;
 }
 
-/* Convert the big int to a safe integer if in math mode. normalize
-   the zero representation. Could also be used to convert the bigint
-   to a short bigint value. The reference count of the value must be
-   1. Cannot fail */
-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
+/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+   return -1 for exception */
+static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+                                       JSValue val)
 {
-    return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
+    int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+    JS_FreeValue(ctx, val);
+    return res;
 }
 
-static JSValue throw_bf_exception(JSContext *ctx, int status)
+/* Note: can return an exception */
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
 {
-    const char *str;
-    if (status & BF_ST_MEM_ERROR)
-        return JS_ThrowOutOfMemory(ctx);
-    if (status & BF_ST_DIVIDE_ZERO) {
-        str = "division by zero";
-    } else if (status & BF_ST_INVALID_OP) {
-        str = "invalid operation";
-    } else {
-        str = "integer overflow";
-    }
-    return JS_ThrowRangeError(ctx, "%s", str);
+    double d;
+    if (!JS_IsNumber(val))
+        return FALSE;
+    if (unlikely(JS_ToFloat64(ctx, &d, val)))
+        return -1;
+    return isfinite(d) && floor(d) == d;
 }
 
-/* if the returned bigfloat is allocated it is equal to
-   'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
-   NULL in case of error. */
-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
 {
     uint32_t tag;
-    bf_t *r;
-    JSBigFloat *p;
 
     tag = JS_VALUE_GET_NORM_TAG(val);
     switch(tag) {
     case JS_TAG_INT:
-    case JS_TAG_BOOL:
-    case JS_TAG_NULL:
-        r = buf;
-        bf_init(ctx->bf_ctx, r);
-        if (bf_set_si(r, JS_VALUE_GET_INT(val)))
-            goto fail;
-        break;
+        {
+            int v;
+            v = JS_VALUE_GET_INT(val);
+            return (v < 0);
+        }
     case JS_TAG_FLOAT64:
-        r = buf;
-        bf_init(ctx->bf_ctx, r);
-        if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
-        fail:
-            bf_delete(r);
-            return NULL;
+        {
+            JSFloat64Union u;
+            u.d = JS_VALUE_GET_FLOAT64(val);
+            return (u.u64 >> 63);
         }
-        break;
+    case JS_TAG_SHORT_BIG_INT:
+        return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0);
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-#endif
-        p = JS_VALUE_GET_PTR(val);
-        r = &p->num;
-        break;
-    case JS_TAG_UNDEFINED:
+        {
+            JSBigInt *p = JS_VALUE_GET_PTR(val);
+            return js_bigint_sign(p);
+        }
     default:
-        r = buf;
-        bf_init(ctx->bf_ctx, r);
-        bf_set_nan(r);
-        break;
+        return FALSE;
     }
-    return r;
 }
 
-#ifdef CONFIG_BIGNUM
-/* return NULL if invalid type */
-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
+static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
 {
-    uint32_t tag;
-    JSBigDecimal *p;
-    bfdec_t *r;
-
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_BIG_DECIMAL:
-        p = JS_VALUE_GET_PTR(val);
-        r = &p->num;
-        break;
-    default:
-        JS_ThrowTypeError(ctx, "bigdecimal expected");
-        r = NULL;
-        break;
-    }
-    return r;
+    return js_bigint_to_string1(ctx, val, 10);
 }
 
-static JSValue JS_NewBigFloat(JSContext *ctx)
+/* buf1 contains the printf result */
+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
+                     int rounding_mode, char *buf1, int buf1_size)
 {
-    JSBigFloat *p;
-    p = js_malloc(ctx, sizeof(*p));
-    if (!p)
-        return JS_EXCEPTION;
-    p->header.ref_count = 1;
-    bf_init(ctx->bf_ctx, &p->num);
-    return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+    if (rounding_mode != FE_TONEAREST)
+        fesetround(rounding_mode);
+    snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
+    if (rounding_mode != FE_TONEAREST)
+        fesetround(FE_TONEAREST);
+    *sign = (buf1[0] == '-');
+    /* mantissa */
+    buf[0] = buf1[1];
+    if (n_digits > 1)
+        memcpy(buf + 1, buf1 + 3, n_digits - 1);
+    buf[n_digits] = '\0';
+    /* exponent */
+    *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
 }
 
-static JSValue JS_NewBigDecimal(JSContext *ctx)
-{
-    JSBigDecimal *p;
-    p = js_malloc(ctx, sizeof(*p));
-    if (!p)
-        return JS_EXCEPTION;
-    p->header.ref_count = 1;
-    bfdec_init(ctx->bf_ctx, &p->num);
-    return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
-}
-
-/* must be kept in sync with JSOverloadableOperatorEnum */
-/* XXX: use atoms ? */
-static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
-    "+",
-    "-",
-    "*",
-    "/",
-    "%",
-    "**",
-    "|",
-    "&",
-    "^",
-    "<<",
-    ">>",
-    ">>>",
-    "==",
-    "<",
-    "pos",
-    "neg",
-    "++",
-    "--",
-    "~",
-};
+/* maximum buffer size for js_dtoa */
+#define JS_DTOA_BUF_SIZE 128
 
-static int get_ovop_from_opcode(OPCodeEnum op)
+/* needed because ecvt usually limits the number of digits to
+   17. Return the number of digits. */
+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+                   BOOL is_fixed)
 {
-    switch(op) {
-    case OP_add:
-        return JS_OVOP_ADD;
-    case OP_sub:
-        return JS_OVOP_SUB;
-    case OP_mul:
-        return JS_OVOP_MUL;
-    case OP_div:
-        return JS_OVOP_DIV;
-    case OP_mod:
-    case OP_math_mod:
-        return JS_OVOP_MOD;
-    case OP_pow:
-        return JS_OVOP_POW;
-    case OP_or:
-        return JS_OVOP_OR;
-    case OP_and:
-        return JS_OVOP_AND;
-    case OP_xor:
-        return JS_OVOP_XOR;
-    case OP_shl:
-        return JS_OVOP_SHL;
-    case OP_sar:
-        return JS_OVOP_SAR;
-    case OP_shr:
-        return JS_OVOP_SHR;
-    case OP_eq:
-    case OP_neq:
-        return JS_OVOP_EQ;
-    case OP_lt:
-    case OP_lte:
-    case OP_gt:
-    case OP_gte:
-        return JS_OVOP_LESS;
-    case OP_plus:
-        return JS_OVOP_POS;
-    case OP_neg:
-        return JS_OVOP_NEG;
-    case OP_inc:
-        return JS_OVOP_INC;
-    case OP_dec:
-        return JS_OVOP_DEC;
-    default:
-        abort();
+    int rounding_mode;
+    char buf_tmp[JS_DTOA_BUF_SIZE];
+
+    if (!is_fixed) {
+        unsigned int n_digits_min, n_digits_max;
+        /* find the minimum amount of digits (XXX: inefficient but simple) */
+        n_digits_min = 1;
+        n_digits_max = 17;
+        while (n_digits_min < n_digits_max) {
+            n_digits = (n_digits_min + n_digits_max) / 2;
+            js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+                     buf_tmp, sizeof(buf_tmp));
+            if (strtod(buf_tmp, NULL) == d) {
+                /* no need to keep the trailing zeros */
+                while (n_digits >= 2 && buf[n_digits - 1] == '0')
+                    n_digits--;
+                n_digits_max = n_digits;
+            } else {
+                n_digits_min = n_digits + 1;
+            }
+        }
+        n_digits = n_digits_max;
+        rounding_mode = FE_TONEAREST;
+    } else {
+        rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+        {
+            char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
+            int decpt1, sign1, decpt2, sign2;
+            /* The JS rounding is specified as round to nearest ties away
+               from zero (RNDNA), but in printf the "ties" case is not
+               specified (for example it is RNDN for glibc, RNDNA for
+               Windows), so we must round manually. */
+            js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
+                     buf_tmp, sizeof(buf_tmp));
+            /* XXX: could use 2 digits to reduce the average running time */
+            if (buf1[n_digits] == '5') {
+                js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
+                         buf_tmp, sizeof(buf_tmp));
+                js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
+                         buf_tmp, sizeof(buf_tmp));
+                if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
+                    /* exact result: round away from zero */
+                    if (sign1)
+                        rounding_mode = FE_DOWNWARD;
+                    else
+                        rounding_mode = FE_UPWARD;
+                }
+            }
+        }
+#endif /* CONFIG_PRINTF_RNDN */
     }
+    js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
+             buf_tmp, sizeof(buf_tmp));
+    return n_digits;
 }
 
-/* return NULL if not present */
-static JSObject *find_binary_op(JSBinaryOperatorDef *def,
-                                uint32_t operator_index,
-                                JSOverloadableOperatorEnum op)
+static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
+                    int rounding_mode)
 {
-    JSBinaryOperatorDefEntry *ent;
-    int i;
-    for(i = 0; i < def->count; i++) {
-        ent = &def->tab[i];
-        if (ent->operator_index == operator_index)
-            return ent->ops[op];
-    }
-    return NULL;
+    int n;
+    if (rounding_mode != FE_TONEAREST)
+        fesetround(rounding_mode);
+    n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
+    if (rounding_mode != FE_TONEAREST)
+        fesetround(FE_TONEAREST);
+    assert(n < sizeof(*buf));
+    return n;
 }
 
-/* return -1 if exception, 0 if no operator overloading, 1 if
-   overloaded operator called */
-static __exception int js_call_binary_op_fallback(JSContext *ctx,
-                                                  JSValue *pret,
-                                                  JSValueConst op1,
-                                                  JSValueConst op2,
-                                                  OPCodeEnum op,
-                                                  BOOL is_numeric,
-                                                  int hint)
+static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
 {
-    JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
-    JSOperatorSetData *opset1, *opset2;
-    JSOverloadableOperatorEnum ovop;
-    JSObject *p;
-    JSValueConst args[2];
-
-    if (!ctx->allow_operator_overloading)
-        return 0;
-
-    opset2_obj = JS_UNDEFINED;
-    opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
-    if (JS_IsException(opset1_obj))
-        goto exception;
-    if (JS_IsUndefined(opset1_obj))
-        return 0;
-    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
-    if (!opset1)
-        goto exception;
-
-    opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
-    if (JS_IsException(opset2_obj))
-        goto exception;
-    if (JS_IsUndefined(opset2_obj)) {
-        JS_FreeValue(ctx, opset1_obj);
-        return 0;
-    }
-    opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
-    if (!opset2)
-        goto exception;
+    int rounding_mode;
+    rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+    {
+        int n1, n2;
+        char buf1[JS_DTOA_BUF_SIZE];
+        char buf2[JS_DTOA_BUF_SIZE];
 
-    if (opset1->is_primitive && opset2->is_primitive) {
-        JS_FreeValue(ctx, opset1_obj);
-        JS_FreeValue(ctx, opset2_obj);
-        return 0;
+        /* The JS rounding is specified as round to nearest ties away from
+           zero (RNDNA), but in printf the "ties" case is not specified
+           (for example it is RNDN for glibc, RNDNA for Windows), so we
+           must round manually. */
+        n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
+        rounding_mode = FE_TONEAREST;
+        /* XXX: could use 2 digits to reduce the average running time */
+        if (buf1[n1 - 1] == '5') {
+            n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
+            n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
+            if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
+                /* exact result: round away from zero */
+                if (buf1[0] == '-')
+                    rounding_mode = FE_DOWNWARD;
+                else
+                    rounding_mode = FE_UPWARD;
+            }
+        }
     }
+#endif /* CONFIG_PRINTF_RNDN */
+    js_fcvt1(buf, d, n_digits, rounding_mode);
+}
 
-    ovop = get_ovop_from_opcode(op);
+/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
+/* use as many digits as necessary */
+#define JS_DTOA_VAR_FORMAT   (0 << 0)
+/* use n_digits significant digits (1 <= n_digits <= 101) */
+#define JS_DTOA_FIXED_FORMAT (1 << 0)
+/* force fractional format: [-]dd.dd with n_digits fractional digits */
+#define JS_DTOA_FRAC_FORMAT  (2 << 0)
+/* force exponential notation either in fixed or variable format */
+#define JS_DTOA_FORCE_EXP    (1 << 2)
 
-    if (opset1->operator_counter == opset2->operator_counter) {
-        p = opset1->self_ops[ovop];
-    } else if (opset1->operator_counter > opset2->operator_counter) {
-        p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
-    } else {
-        p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
-    }
-    if (!p) {
-        JS_ThrowTypeError(ctx, "operator %s: no function defined",
-                          js_overloadable_operator_names[ovop]);
-        goto exception;
-    }
+/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
+   XXX: radix != 10 is only supported for small integers
+*/
+static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
+                     int radix, int n_digits, int flags)
+{
+    char *q;
 
-    if (opset1->is_primitive) {
-        if (is_numeric) {
-            new_op1 = JS_ToNumeric(ctx, op1);
+    if (!isfinite(d)) {
+        if (isnan(d)) {
+            pstrcpy(*buf, sizeof(*buf), "NaN");
+        } else if (d < 0) {
+            pstrcpy(*buf, sizeof(*buf), "-Infinity");
         } else {
-            new_op1 = JS_ToPrimitive(ctx, op1, hint);
+            pstrcpy(*buf, sizeof(*buf), "Infinity");
         }
-        if (JS_IsException(new_op1))
-            goto exception;
+    } else if (flags == JS_DTOA_VAR_FORMAT) {
+        int64_t i64;
+        char buf1[70], *ptr;
+        if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
+            goto generic_conv;
+        i64 = (int64_t)d;
+        if (d != i64)
+            goto generic_conv;
+        /* fast path for integers */
+        ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
+        pstrcpy(*buf, sizeof(*buf), ptr);
     } else {
-        new_op1 = JS_DupValue(ctx, op1);
-    }
-
-    if (opset2->is_primitive) {
-        if (is_numeric) {
-            new_op2 = JS_ToNumeric(ctx, op2);
+        if (d == 0.0)
+            d = 0.0; /* convert -0 to 0 */
+        if (flags == JS_DTOA_FRAC_FORMAT) {
+            js_fcvt(buf, d, n_digits);
         } else {
-            new_op2 = JS_ToPrimitive(ctx, op2, hint);
-        }
-        if (JS_IsException(new_op2)) {
-            JS_FreeValue(ctx, new_op1);
-            goto exception;
+            char buf1[JS_DTOA_BUF_SIZE];
+            int sign, decpt, k, n, i, p, n_max;
+            BOOL is_fixed;
+        generic_conv:
+            is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
+            if (is_fixed) {
+                n_max = n_digits;
+            } else {
+                n_max = 21;
+            }
+            /* the number has k digits (k >= 1) */
+            k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
+            n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
+            q = *buf;
+            if (sign)
+                *q++ = '-';
+            if (flags & JS_DTOA_FORCE_EXP)
+                goto force_exp;
+            if (n >= 1 && n <= n_max) {
+                if (k <= n) {
+                    memcpy(q, buf1, k);
+                    q += k;
+                    for(i = 0; i < (n - k); i++)
+                        *q++ = '0';
+                    *q = '\0';
+                } else {
+                    /* k > n */
+                    memcpy(q, buf1, n);
+                    q += n;
+                    *q++ = '.';
+                    for(i = 0; i < (k - n); i++)
+                        *q++ = buf1[n + i];
+                    *q = '\0';
+                }
+            } else if (n >= -5 && n <= 0) {
+                *q++ = '0';
+                *q++ = '.';
+                for(i = 0; i < -n; i++)
+                    *q++ = '0';
+                memcpy(q, buf1, k);
+                q += k;
+                *q = '\0';
+            } else {
+            force_exp:
+                /* exponential notation */
+                *q++ = buf1[0];
+                if (k > 1) {
+                    *q++ = '.';
+                    for(i = 1; i < k; i++)
+                        *q++ = buf1[i];
+                }
+                *q++ = 'e';
+                p = n - 1;
+                if (p >= 0)
+                    *q++ = '+';
+                snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
+            }
         }
-    } else {
-        new_op2 = JS_DupValue(ctx, op2);
     }
+}
 
-    /* XXX: could apply JS_ToPrimitive() if primitive type so that the
-       operator function does not get a value object */
+static JSValue js_dtoa(JSContext *ctx,
+                       double d, int radix, int n_digits, int flags)
+{
+    char buf[JS_DTOA_BUF_SIZE];
+    js_dtoa1(&buf, d, radix, n_digits, flags);
+    return JS_NewString(ctx, buf);
+}
 
-    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
-    if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
-        args[0] = new_op2;
-        args[1] = new_op1;
+static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
+{
+    char buf[2200], *ptr, *ptr2;
+    /* d is finite */
+    int sign = d < 0;
+    int digit;
+    double frac, d0;
+    int64_t n0 = 0;
+    d = fabs(d);
+    d0 = trunc(d);
+    frac = d - d0;
+    ptr = buf + 1100;
+    *ptr = '\0';
+    if (d0 <= MAX_SAFE_INTEGER) {
+        int64_t n = n0 = (int64_t)d0;
+        while (n >= radix) {
+            digit = n % radix;
+            n = n / radix;
+            *--ptr = digits[digit];
+        }
+        *--ptr = digits[(int)n];
     } else {
-        args[0] = new_op1;
-        args[1] = new_op2;
+        /* no decimals */
+        while (d0 >= radix) {
+            digit = fmod(d0, radix);
+            d0 = trunc(d0 / radix);
+            if (d0 >= MAX_SAFE_INTEGER)
+                digit = 0;
+            *--ptr = digits[digit];
+        }
+        *--ptr = digits[(int)d0];
+        goto done;
     }
-    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
-    JS_FreeValue(ctx, new_op1);
-    JS_FreeValue(ctx, new_op2);
-    if (JS_IsException(ret))
-        goto exception;
-    if (ovop == JS_OVOP_EQ) {
-        BOOL res = JS_ToBoolFree(ctx, ret);
-        if (op == OP_neq)
-            res ^= 1;
-        ret = JS_NewBool(ctx, res);
-    } else if (ovop == JS_OVOP_LESS) {
-        if (JS_IsUndefined(ret)) {
-            ret = JS_FALSE;
+    if (frac != 0) {
+        double log2_radix = log2(radix);
+        double prec = 1023 + 51;  // handle subnormals
+        ptr2 = buf + 1100;
+        *ptr2++ = '.';
+        while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
+            frac *= radix;
+            digit = trunc(frac);
+            frac -= digit;
+            *ptr2++ = digits[digit];
+            n0 = n0 * radix + digit;
+            prec -= log2_radix;
+        }
+        *ptr2 = '\0';
+        if (frac * radix >= radix / 2) {
+            char nine = digits[radix - 1];
+            // round to closest
+            while (ptr2[-1] == nine)
+                *--ptr2 = '\0';
+            if (ptr2[-1] == '.') {
+                *--ptr2 = '\0';
+                while (ptr2[-1] == nine)
+                    *--ptr2 = '0';
+            }
+            if (ptr2 - 1 == ptr)
+                *--ptr = '1';
+            else
+                ptr2[-1] += 1;
         } else {
-            BOOL res = JS_ToBoolFree(ctx, ret);
-            if (op == OP_lte || op == OP_gte)
-                res ^= 1;
-            ret = JS_NewBool(ctx, res);
+            while (ptr2[-1] == '0')
+                *--ptr2 = '\0';
+            if (ptr2[-1] == '.')
+                *--ptr2 = '\0';
         }
     }
-    JS_FreeValue(ctx, opset1_obj);
-    JS_FreeValue(ctx, opset2_obj);
-    *pret = ret;
-    return 1;
- exception:
-    JS_FreeValue(ctx, opset1_obj);
-    JS_FreeValue(ctx, opset2_obj);
-    *pret = JS_UNDEFINED;
-    return -1;
+done:
+    ptr[-1] = '-';
+    ptr -= sign;
+    return JS_NewString(ctx, ptr);
 }
 
-/* try to call the operation on the operatorSet field of 'obj'. Only
-   used for "/" and "**" on the BigInt prototype in math mode */
-static __exception int js_call_binary_op_simple(JSContext *ctx,
-                                                JSValue *pret,
-                                                JSValueConst obj,
-                                                JSValueConst op1,
-                                                JSValueConst op2,
-                                                OPCodeEnum op)
+JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
 {
-    JSValue opset1_obj, method, ret, new_op1, new_op2;
-    JSOperatorSetData *opset1;
-    JSOverloadableOperatorEnum ovop;
-    JSObject *p;
-    JSValueConst args[2];
-
-    opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
-    if (JS_IsException(opset1_obj))
-        goto exception;
-    if (JS_IsUndefined(opset1_obj))
-        return 0;
-    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
-    if (!opset1)
-        goto exception;
-    ovop = get_ovop_from_opcode(op);
-
-    p = opset1->self_ops[ovop];
-    if (!p) {
-        JS_FreeValue(ctx, opset1_obj);
-        return 0;
-    }
+    uint32_t tag;
+    const char *str;
+    char buf[32];
 
-    new_op1 = JS_ToNumeric(ctx, op1);
-    if (JS_IsException(new_op1))
-        goto exception;
-    new_op2 = JS_ToNumeric(ctx, op2);
-    if (JS_IsException(new_op2)) {
-        JS_FreeValue(ctx, new_op1);
-        goto exception;
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_STRING:
+        return JS_DupValue(ctx, val);
+    case JS_TAG_INT:
+        snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
+        str = buf;
+        goto new_string;
+    case JS_TAG_BOOL:
+        return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+                          JS_ATOM_true : JS_ATOM_false);
+    case JS_TAG_NULL:
+        return JS_AtomToString(ctx, JS_ATOM_null);
+    case JS_TAG_UNDEFINED:
+        return JS_AtomToString(ctx, JS_ATOM_undefined);
+    case JS_TAG_EXCEPTION:
+        return JS_EXCEPTION;
+    case JS_TAG_OBJECT:
+        {
+            JSValue val1, ret;
+            val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
+            if (JS_IsException(val1))
+                return val1;
+            ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
+            JS_FreeValue(ctx, val1);
+            return ret;
+        }
+        break;
+    case JS_TAG_FUNCTION_BYTECODE:
+        str = "[function bytecode]";
+        goto new_string;
+    case JS_TAG_SYMBOL:
+        if (is_ToPropertyKey) {
+            return JS_DupValue(ctx, val);
+        } else {
+            return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
+        }
+    case JS_TAG_FLOAT64:
+        return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
+                       JS_DTOA_VAR_FORMAT);
+    case JS_TAG_SHORT_BIG_INT:
+    case JS_TAG_BIG_INT:
+        return js_bigint_to_string(ctx, val);
+    default:
+        str = "[unsupported type]";
+    new_string:
+        return JS_NewString(ctx, str);
     }
-
-    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
-    args[0] = new_op1;
-    args[1] = new_op2;
-    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
-    JS_FreeValue(ctx, new_op1);
-    JS_FreeValue(ctx, new_op2);
-    if (JS_IsException(ret))
-        goto exception;
-    JS_FreeValue(ctx, opset1_obj);
-    *pret = ret;
-    return 1;
- exception:
-    JS_FreeValue(ctx, opset1_obj);
-    *pret = JS_UNDEFINED;
-    return -1;
 }
 
-/* return -1 if exception, 0 if no operator overloading, 1 if
-   overloaded operator called */
-static __exception int js_call_unary_op_fallback(JSContext *ctx,
-                                                 JSValue *pret,
-                                                 JSValueConst op1,
-                                                 OPCodeEnum op)
+JSValue JS_ToString(JSContext *ctx, JSValueConst val)
 {
-    JSValue opset1_obj, method, ret;
-    JSOperatorSetData *opset1;
-    JSOverloadableOperatorEnum ovop;
-    JSObject *p;
-
-    if (!ctx->allow_operator_overloading)
-        return 0;
-
-    opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
-    if (JS_IsException(opset1_obj))
-        goto exception;
-    if (JS_IsUndefined(opset1_obj))
-        return 0;
-    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
-    if (!opset1)
-        goto exception;
-    if (opset1->is_primitive) {
-        JS_FreeValue(ctx, opset1_obj);
-        return 0;
-    }
-
-    ovop = get_ovop_from_opcode(op);
+    return JS_ToStringInternal(ctx, val, FALSE);
+}
 
-    p = opset1->self_ops[ovop];
-    if (!p) {
-        JS_ThrowTypeError(ctx, "no overloaded operator %s",
-                          js_overloadable_operator_names[ovop]);
-        goto exception;
-    }
-    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
-    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
-    if (JS_IsException(ret))
-        goto exception;
-    JS_FreeValue(ctx, opset1_obj);
-    *pret = ret;
-    return 1;
- exception:
-    JS_FreeValue(ctx, opset1_obj);
-    *pret = JS_UNDEFINED;
-    return -1;
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+{
+    JSValue ret;
+    ret = JS_ToString(ctx, val);
+    JS_FreeValue(ctx, val);
+    return ret;
 }
 
-static int js_unary_arith_bigfloat(JSContext *ctx,
-                                   JSValue *pres, OPCodeEnum op, JSValue op1)
+static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
 {
-    bf_t a_s, *r, *a;
-    int ret, v;
-    JSValue res;
+    if (JS_IsUndefined(val) || JS_IsNull(val))
+        return JS_ToStringFree(ctx, val);
+    return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+}
 
-    if (op == OP_plus && !is_math_mode(ctx)) {
-        JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+{
+    return JS_ToStringInternal(ctx, val, TRUE);
+}
 
-    res = JS_NewBigFloat(ctx);
-    if (JS_IsException(res)) {
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    r = JS_GetBigFloat(res);
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    ret = 0;
-    switch(op) {
-    case OP_inc:
-    case OP_dec:
-        v = 2 * (op - OP_dec) - 1;
-        ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
-        break;
-    case OP_plus:
-        ret = bf_set(r, a);
-        break;
-    case OP_neg:
-        ret = bf_set(r, a);
-        bf_neg(r);
-        break;
-    default:
-        abort();
-    }
-    if (a == &a_s)
-        bf_delete(a);
-    JS_FreeValue(ctx, op1);
-    if (unlikely(ret & BF_ST_MEM_ERROR)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
-        return -1;
-    }
-    *pres = res;
-    return 0;
+static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+{
+    uint32_t tag = JS_VALUE_GET_TAG(val);
+    if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+        return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
+    return JS_ToString(ctx, val);
 }
 
-static int js_unary_arith_bigdecimal(JSContext *ctx,
-                                     JSValue *pres, OPCodeEnum op, JSValue op1)
+static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
 {
-    bfdec_t *r, *a;
-    int ret, v;
-    JSValue res;
+    JSValue val;
+    JSString *p;
+    int i;
+    uint32_t c;
+    StringBuffer b_s, *b = &b_s;
+    char buf[16];
 
-    if (op == OP_plus && !is_math_mode(ctx)) {
-        JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
+    val = JS_ToStringCheckObject(ctx, val1);
+    if (JS_IsException(val))
+        return val;
+    p = JS_VALUE_GET_STRING(val);
 
-    res = JS_NewBigDecimal(ctx);
-    if (JS_IsException(res)) {
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    r = JS_GetBigDecimal(res);
-    a = JS_ToBigDecimal(ctx, op1);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    ret = 0;
-    switch(op) {
-    case OP_inc:
-    case OP_dec:
-        v = 2 * (op - OP_dec) - 1;
-        ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_plus:
-        ret = bfdec_set(r, a);
-        break;
-    case OP_neg:
-        ret = bfdec_set(r, a);
-        bfdec_neg(r);
-        break;
-    default:
-        abort();
-    }
-    JS_FreeValue(ctx, op1);
-    if (unlikely(ret)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
-        return -1;
+    if (string_buffer_init(ctx, b, p->len + 2))
+        goto fail;
+
+    if (string_buffer_putc8(b, '\"'))
+        goto fail;
+    for(i = 0; i < p->len; ) {
+        c = string_getc(p, &i);
+        switch(c) {
+        case '\t':
+            c = 't';
+            goto quote;
+        case '\r':
+            c = 'r';
+            goto quote;
+        case '\n':
+            c = 'n';
+            goto quote;
+        case '\b':
+            c = 'b';
+            goto quote;
+        case '\f':
+            c = 'f';
+            goto quote;
+        case '\"':
+        case '\\':
+        quote:
+            if (string_buffer_putc8(b, '\\'))
+                goto fail;
+            if (string_buffer_putc8(b, c))
+                goto fail;
+            break;
+        default:
+            if (c < 32 || is_surrogate(c)) {
+                snprintf(buf, sizeof(buf), "\\u%04x", c);
+                if (string_buffer_puts8(b, buf))
+                    goto fail;
+            } else {
+                if (string_buffer_putc(b, c))
+                    goto fail;
+            }
+            break;
+        }
     }
-    *pres = res;
-    return 0;
+    if (string_buffer_putc8(b, '\"'))
+        goto fail;
+    JS_FreeValue(ctx, val);
+    return string_buffer_end(b);
+ fail:
+    JS_FreeValue(ctx, val);
+    string_buffer_free(b);
+    return JS_EXCEPTION;
 }
 
-#endif /* CONFIG_BIGNUM */
-
-static int js_unary_arith_bigint(JSContext *ctx,
-                                 JSValue *pres, OPCodeEnum op, JSValue op1)
+static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
 {
-    bf_t a_s, *r, *a;
-    int ret, v;
-    JSValue res;
-
-    if (op == OP_plus && !is_math_mode(ctx)) {
-        JS_ThrowTypeError(ctx, "bigint argument with unary +");
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    res = JS_NewBigInt(ctx);
-    if (JS_IsException(res)) {
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    r = JS_GetBigInt(res);
-    a = JS_ToBigInt(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    ret = 0;
-    switch(op) {
-    case OP_inc:
-    case OP_dec:
-        v = 2 * (op - OP_dec) - 1;
-        ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_plus:
-        ret = bf_set(r, a);
-        break;
-    case OP_neg:
-        ret = bf_set(r, a);
-        bf_neg(r);
-        break;
-    case OP_not:
-        ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
-        bf_neg(r);
-        break;
-    default:
-        abort();
-    }
-    JS_FreeBigInt(ctx, a, &a_s);
-    JS_FreeValue(ctx, op1);
-    if (unlikely(ret)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
-        return -1;
-    }
-    res = JS_CompactBigInt(ctx, res);
-    *pres = res;
-    return 0;
+    printf("%14s %4s %4s %14s %10s %s\n",
+           "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
 }
 
-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
-                                                     JSValue *sp,
-                                                     OPCodeEnum op)
+/* for debug only: dump an object without side effect */
+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
 {
-    JSValue op1;
-    int v;
-    uint32_t tag;
+    uint32_t i;
+    char atom_buf[ATOM_GET_STR_BUF_SIZE];
+    JSShape *sh;
+    JSShapeProperty *prs;
+    JSProperty *pr;
+    BOOL is_first = TRUE;
 
-    op1 = sp[-1];
-    /* fast path for float64 */
-    if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
-        goto handle_float64;
-#ifdef CONFIG_BIGNUM
-    if (JS_IsObject(op1)) {
-        JSValue val;
-        int ret = js_call_unary_op_fallback(ctx, &val, op1, op);
-        if (ret < 0)
-            return -1;
-        if (ret) {
-            JS_FreeValue(ctx, op1);
-            sp[-1] = val;
-            return 0;
-        }
+    /* XXX: should encode atoms with special characters */
+    sh = p->shape; /* the shape can be NULL while freeing an object */
+    printf("%14p %4d ",
+           (void *)p,
+           p->header.ref_count);
+    if (sh) {
+        printf("%3d%c %14p ",
+               sh->header.ref_count,
+               " *"[sh->is_hashed],
+               (void *)sh->proto);
+    } else {
+        printf("%3s  %14s ", "-", "-");
     }
-#endif
-    op1 = JS_ToNumericFree(ctx, op1);
-    if (JS_IsException(op1))
-        goto exception;
-    tag = JS_VALUE_GET_TAG(op1);
-    switch(tag) {
-    case JS_TAG_INT:
-        {
-            int64_t v64;
-            v64 = JS_VALUE_GET_INT(op1);
-            switch(op) {
-            case OP_inc:
-            case OP_dec:
-                v = 2 * (op - OP_dec) - 1;
-                v64 += v;
-                break;
-            case OP_plus:
+    printf("%10s ",
+           JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
+    if (p->is_exotic && p->fast_array) {
+        printf("[ ");
+        for(i = 0; i < p->u.array.count; i++) {
+            if (i != 0)
+                printf(", ");
+            switch (p->class_id) {
+            case JS_CLASS_ARRAY:
+            case JS_CLASS_ARGUMENTS:
+                JS_DumpValueShort(rt, p->u.array.u.values[i]);
                 break;
-            case OP_neg:
-                if (v64 == 0) {
-                    sp[-1] = __JS_NewFloat64(ctx, -0.0);
-                    return 0;
-                } else {
-                    v64 = -v64;
+            case JS_CLASS_UINT8C_ARRAY:
+            case JS_CLASS_INT8_ARRAY:
+            case JS_CLASS_UINT8_ARRAY:
+            case JS_CLASS_INT16_ARRAY:
+            case JS_CLASS_UINT16_ARRAY:
+            case JS_CLASS_INT32_ARRAY:
+            case JS_CLASS_UINT32_ARRAY:
+            case JS_CLASS_BIG_INT64_ARRAY:
+            case JS_CLASS_BIG_UINT64_ARRAY:
+            case JS_CLASS_FLOAT32_ARRAY:
+            case JS_CLASS_FLOAT64_ARRAY:
+                {
+                    int size = 1 << typed_array_size_log2(p->class_id);
+                    const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
+                    while (size-- > 0)
+                        printf("%02X", *b++);
                 }
                 break;
-            default:
-                abort();
             }
-            sp[-1] = JS_NewInt64(ctx, v64);
         }
-        break;
-    case JS_TAG_BIG_INT:
-    handle_bigint:
-        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
-            goto exception;
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
-            goto exception;
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
-            goto exception;
-        break;
-#endif
-    default:
-    handle_float64:
-        {
-            double d;
-            if (is_math_mode(ctx))
-                goto handle_bigint;
-            d = JS_VALUE_GET_FLOAT64(op1);
-            switch(op) {
-            case OP_inc:
-            case OP_dec:
-                v = 2 * (op - OP_dec) - 1;
-                d += v;
-                break;
-            case OP_plus:
-                break;
-            case OP_neg:
-                d = -d;
-                break;
-            default:
-                abort();
+        printf(" ] ");
+    }
+
+    if (sh) {
+        printf("{ ");
+        for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+            if (prs->atom != JS_ATOM_NULL) {
+                pr = &p->prop[i];
+                if (!is_first)
+                    printf(", ");
+                printf("%s: ",
+                       JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
+                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+                    printf("[getset %p %p]", (void *)pr->u.getset.getter,
+                           (void *)pr->u.getset.setter);
+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+                    printf("[varref %p]", (void *)pr->u.var_ref);
+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+                    printf("[autoinit %p %d %p]",
+                           (void *)js_autoinit_get_realm(pr),
+                           js_autoinit_get_id(pr),
+                           (void *)pr->u.init.opaque);
+                } else {
+                    JS_DumpValueShort(rt, pr->u.value);
+                }
+                is_first = FALSE;
             }
-            sp[-1] = __JS_NewFloat64(ctx, d);
         }
-        break;
+        printf(" }");
     }
-    return 0;
- exception:
-    sp[-1] = JS_UNDEFINED;
-    return -1;
-}
-
-static __exception int js_post_inc_slow(JSContext *ctx,
-                                        JSValue *sp, OPCodeEnum op)
-{
-    JSValue op1;
 
-    /* XXX: allow custom operators */
-    op1 = sp[-1];
-    op1 = JS_ToNumericFree(ctx, op1);
-    if (JS_IsException(op1)) {
-        sp[-1] = JS_UNDEFINED;
-        return -1;
+    if (js_class_has_bytecode(p->class_id)) {
+        JSFunctionBytecode *b = p->u.func.function_bytecode;
+        JSVarRef **var_refs;
+        if (b->closure_var_count) {
+            var_refs = p->u.func.var_refs;
+            printf(" Closure:");
+            for(i = 0; i < b->closure_var_count; i++) {
+                printf(" ");
+                JS_DumpValueShort(rt, var_refs[i]->value);
+            }
+            if (p->u.func.home_object) {
+                printf(" HomeObject: ");
+                JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
+            }
+        }
     }
-    sp[-1] = op1;
-    sp[0] = JS_DupValue(ctx, op1);
-    return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+    printf("\n");
 }
 
-static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
 {
-    JSValue op1;
-
-    op1 = sp[-1];
-#ifdef CONFIG_BIGNUM
-    if (JS_IsObject(op1)) {
-        JSValue val;
-        int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
-        if (ret < 0)
-            return -1;
-        if (ret) {
-            JS_FreeValue(ctx, op1);
-            sp[-1] = val;
-            return 0;
-        }
-    }
-#endif
-    op1 = JS_ToNumericFree(ctx, op1);
-    if (JS_IsException(op1))
-        goto exception;
-    if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
-        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
-            goto exception;
+    if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+        JS_DumpObject(rt, (JSObject *)p);
     } else {
-        int32_t v1;
-        if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
-            goto exception;
-        sp[-1] = JS_NewInt32(ctx, ~v1);
+        printf("%14p %4d ",
+               (void *)p,
+               p->ref_count);
+        switch(p->gc_obj_type) {
+        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+            printf("[function bytecode]");
+            break;
+        case JS_GC_OBJ_TYPE_SHAPE:
+            printf("[shape]");
+            break;
+        case JS_GC_OBJ_TYPE_VAR_REF:
+            printf("[var_ref]");
+            break;
+        case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+            printf("[async_function]");
+            break;
+        case JS_GC_OBJ_TYPE_JS_CONTEXT:
+            printf("[js_context]");
+            break;
+        default:
+            printf("[unknown %d]", p->gc_obj_type);
+            break;
+        }
+        printf("\n");
     }
-    return 0;
- exception:
-    sp[-1] = JS_UNDEFINED;
-    return -1;
 }
 
-static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
-                                  JSValue *pres, JSValue op1, JSValue op2)
+static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+                                                      JSValueConst val)
 {
-    bf_t a_s, b_s, *r, *a, *b;
-    int ret;
-    JSValue res;
+    uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+    const char *str;
 
-    res = JS_NewBigInt(ctx);
-    if (JS_IsException(res))
-        goto fail;
-    a = JS_ToBigInt(ctx, &a_s, op1);
-    if (!a)
-        goto fail;
-    b = JS_ToBigInt(ctx, &b_s, op2);
-    if (!b) {
-        JS_FreeBigInt(ctx, a, &a_s);
-        goto fail;
-    }
-    r = JS_GetBigInt(res);
-    ret = 0;
-    switch(op) {
-    case OP_add:
-        ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_sub:
-        ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_mul:
-        ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_div:
-        if (!is_math_mode(ctx)) {
-            bf_t rem_s, *rem = &rem_s;
-            bf_init(ctx->bf_ctx, rem);
-            ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
-                            BF_RNDZ);
-            bf_delete(rem);
-        } else {
-            goto math_mode_div_pow;
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case OP_math_mod:
-        /* Euclidian remainder */
-        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
-                     BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
+    switch(tag) {
+    case JS_TAG_INT:
+        printf("%d", JS_VALUE_GET_INT(val));
+        break;
+    case JS_TAG_BOOL:
+        if (JS_VALUE_GET_BOOL(val))
+            str = "true";
+        else
+            str = "false";
+        goto print_str;
+    case JS_TAG_NULL:
+        str = "null";
+        goto print_str;
+    case JS_TAG_EXCEPTION:
+        str = "exception";
+        goto print_str;
+    case JS_TAG_UNINITIALIZED:
+        str = "uninitialized";
+        goto print_str;
+    case JS_TAG_UNDEFINED:
+        str = "undefined";
+    print_str:
+        printf("%s", str);
+        break;
+    case JS_TAG_FLOAT64:
+        printf("%.14g", JS_VALUE_GET_FLOAT64(val));
+        break;
+#if 0
+        /* XXX: TODO */
+    case JS_TAG_BIG_INT:
+        {
+            JSBigFloat *p = JS_VALUE_GET_PTR(val);
+            char *str;
+            str = bf_ftoa(NULL, &p->num, 10, 0,
+                          BF_RNDZ | BF_FTOA_FORMAT_FRAC);
+            printf("%sn", str);
+            bf_realloc(&rt->bf_ctx, str, 0);
+        }
         break;
 #endif
-    case OP_mod:
-        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
-                     BF_RNDZ) & BF_ST_INVALID_OP;
-        break;
-    case OP_pow:
-        if (b->sign) {
-            if (!is_math_mode(ctx)) {
-                ret = BF_ST_INVALID_OP;
-            } else {
-            math_mode_div_pow:
-#ifdef CONFIG_BIGNUM
-                JS_FreeValue(ctx, res);
-                ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
-                if (ret != 0) {
-                    JS_FreeBigInt(ctx, a, &a_s);
-                    JS_FreeBigInt(ctx, b, &b_s);
-                    JS_FreeValue(ctx, op1);
-                    JS_FreeValue(ctx, op2);
-                    if (ret < 0) {
-                        return -1;
-                    } else {
-                        *pres = res;
-                        return 0;
-                    }
-                }
-                /* if no BigInt power operator defined, return a
-                   bigfloat */
-                res = JS_NewBigFloat(ctx);
-                if (JS_IsException(res)) {
-                    JS_FreeBigInt(ctx, a, &a_s);
-                    JS_FreeBigInt(ctx, b, &b_s);
-                    goto fail;
-                }
-                r = JS_GetBigFloat(res);
-                if (op == OP_div) {
-                    ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
-                } else {
-                    ret = bf_pow(r, a, b, ctx->fp_env.prec,
-                                 ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
-                }
-                JS_FreeBigInt(ctx, a, &a_s);
-                JS_FreeBigInt(ctx, b, &b_s);
-                JS_FreeValue(ctx, op1);
-                JS_FreeValue(ctx, op2);
-                if (unlikely(ret)) {
-                    JS_FreeValue(ctx, res);
-                    throw_bf_exception(ctx, ret);
-                    return -1;
-                }
-                *pres = res;
-                return 0;
-#else
-                abort();
-#endif
-            }
-        } else {
-            ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
+    case JS_TAG_STRING:
+        {
+            JSString *p;
+            p = JS_VALUE_GET_STRING(val);
+            JS_DumpString(rt, p);
         }
         break;
-
-        /* logical operations */
-    case OP_shl:
-    case OP_sar:
+    case JS_TAG_FUNCTION_BYTECODE:
         {
-            slimb_t v2;
-#if LIMB_BITS == 32
-            bf_get_int32(&v2, b, 0);
-            if (v2 == INT32_MIN)
-                v2 = INT32_MIN + 1;
-#else
-            bf_get_int64(&v2, b, 0);
-            if (v2 == INT64_MIN)
-                v2 = INT64_MIN + 1;
-#endif
-            if (op == OP_sar)
-                v2 = -v2;
-            ret = bf_set(r, a);
-            ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
-            if (v2 < 0) {
-                ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
-            }
+            JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
+            char buf[ATOM_GET_STR_BUF_SIZE];
+            printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
         }
         break;
-    case OP_and:
-        ret = bf_logic_and(r, a, b);
+    case JS_TAG_OBJECT:
+        {
+            JSObject *p = JS_VALUE_GET_OBJ(val);
+            JSAtom atom = rt->class_array[p->class_id].class_name;
+            char atom_buf[ATOM_GET_STR_BUF_SIZE];
+            printf("[%s %p]",
+                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
+        }
         break;
-    case OP_or:
-        ret = bf_logic_or(r, a, b);
+    case JS_TAG_SYMBOL:
+        {
+            JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+            char atom_buf[ATOM_GET_STR_BUF_SIZE];
+            printf("Symbol(%s)",
+                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+        }
         break;
-    case OP_xor:
-        ret = bf_logic_xor(r, a, b);
+    case JS_TAG_MODULE:
+        printf("[module]");
         break;
     default:
-        abort();
+        printf("[unknown tag %d]", tag);
+        break;
     }
-    JS_FreeBigInt(ctx, a, &a_s);
-    JS_FreeBigInt(ctx, b, &b_s);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    if (unlikely(ret)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
+}
+
+static __maybe_unused void JS_DumpValue(JSContext *ctx,
+                                                 JSValueConst val)
+{
+    JS_DumpValueShort(ctx->rt, val);
+}
+
+static __maybe_unused void JS_PrintValue(JSContext *ctx,
+                                                  const char *str,
+                                                  JSValueConst val)
+{
+    printf("%s=", str);
+    JS_DumpValueShort(ctx->rt, val);
+    printf("\n");
+}
+
+/* return -1 if exception (proxy case) or TRUE/FALSE */
+// TODO: should take flags to make proxy resolution and exceptions optional
+int JS_IsArray(JSContext *ctx, JSValueConst val)
+{
+    if (js_resolve_proxy(ctx, &val, TRUE))
         return -1;
+    if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
+        JSObject *p = JS_VALUE_GET_OBJ(val);
+        return p->class_id == JS_CLASS_ARRAY;
+    } else {
+        return FALSE;
     }
-    *pres = JS_CompactBigInt(ctx, res);
-    return 0;
- fail:
-    JS_FreeValue(ctx, res);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    return -1;
 }
 
-#ifdef CONFIG_BIGNUM
-static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
-                                    JSValue *pres, JSValue op1, JSValue op2)
+static double js_pow(double a, double b)
 {
-    bf_t a_s, b_s, *r, *a, *b;
-    int ret;
-    JSValue res;
+    if (unlikely(!isfinite(b)) && fabs(a) == 1) {
+        /* not compatible with IEEE 754 */
+        return JS_FLOAT64_NAN;
+    } else {
+        return pow(a, b);
+    }
+}
 
-    res = JS_NewBigFloat(ctx);
-    if (JS_IsException(res))
-        goto fail;
-    r = JS_GetBigFloat(res);
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        goto fail;
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+#if JS_SHORT_BIG_INT_BITS == 64
+    return __JS_NewShortBigInt(ctx, v);
+#else
+    if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) {
+        return __JS_NewShortBigInt(ctx, v);
+    } else {
+        JSBigInt *p;
+        p = js_bigint_new_si64(ctx, v);
+        if (!p)
+            return JS_EXCEPTION;
+        return JS_MKPTR(JS_TAG_BIG_INT, p);
     }
-    b = JS_ToBigFloat(ctx, &b_s, op2);
-    if (!b) {
-        if (a == &a_s)
-            bf_delete(a);
-        JS_FreeValue(ctx, res);
-        goto fail;
+#endif
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+    if (v <= JS_SHORT_BIG_INT_MAX) {
+        return __JS_NewShortBigInt(ctx, v);
+    } else {
+        JSBigInt *p;
+        p = js_bigint_new_ui64(ctx, v);
+        if (!p)
+            return JS_EXCEPTION;
+        return JS_MKPTR(JS_TAG_BIG_INT, p);
     }
-    bf_init(ctx->bf_ctx, r);
-    switch(op) {
-    case OP_add:
-        ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
-        break;
-    case OP_sub:
-        ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
-        break;
-    case OP_mul:
-        ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
-        break;
-    case OP_div:
-        ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
-        break;
-    case OP_math_mod:
-        /* Euclidian remainder */
-        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
-                     BF_DIVREM_EUCLIDIAN);
-        break;
-    case OP_mod:
-        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
-                     BF_RNDZ);
+}
+
+/* return NaN if bad bigint literal */
+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+{
+    const char *str, *p;
+    size_t len;
+    int flags;
+
+    str = JS_ToCStringLen(ctx, &len, val);
+    JS_FreeValue(ctx, val);
+    if (!str)
+        return JS_EXCEPTION;
+    p = str;
+    p += skip_spaces(p);
+    if ((p - str) == len) {
+        val = JS_NewBigInt64(ctx, 0);
+    } else {
+        flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
+        val = js_atof(ctx, p, &p, 0, flags);
+        p += skip_spaces(p);
+        if (!JS_IsException(val)) {
+            if ((p - str) != len) {
+                JS_FreeValue(ctx, val);
+                val = JS_NAN;
+            }
+        }
+    }
+    JS_FreeCString(ctx, str);
+    return val;
+}
+
+static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+{
+    val = JS_StringToBigInt(ctx, val);
+    if (JS_VALUE_IS_NAN(val))
+        return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
+    return val;
+}
+
+/* JS Numbers are not allowed */
+static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val)
+{
+    uint32_t tag;
+
+ redo:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
+    case JS_TAG_SHORT_BIG_INT:
+    case JS_TAG_BIG_INT:
         break;
-    case OP_pow:
-        ret = bf_pow(r, a, b, ctx->fp_env.prec,
-                     ctx->fp_env.flags | BF_POW_JS_QUIRKS);
+    case JS_TAG_INT:
+    case JS_TAG_NULL:
+    case JS_TAG_UNDEFINED:
+    case JS_TAG_FLOAT64:
+        goto fail;
+    case JS_TAG_BOOL:
+        val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val));
         break;
+    case JS_TAG_STRING:
+        val = JS_StringToBigIntErr(ctx, val);
+        if (JS_IsException(val))
+            return val;
+        goto redo;
+    case JS_TAG_OBJECT:
+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+        if (JS_IsException(val))
+            return val;
+        goto redo;
     default:
-        abort();
-    }
-    if (a == &a_s)
-        bf_delete(a);
-    if (b == &b_s)
-        bf_delete(b);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    if (unlikely(ret & BF_ST_MEM_ERROR)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
-        return -1;
+    fail:
+        JS_FreeValue(ctx, val);
+        return JS_ThrowTypeError(ctx, "cannot convert to bigint");
     }
-    *pres = res;
-    return 0;
- fail:
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    return -1;
+    return val;
 }
 
-/* b must be a positive integer */
-static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
+static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val)
 {
-    bfdec_t b1;
-    int32_t b2;
-    int ret;
+    return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val));
+}
 
-    bfdec_init(b->ctx, &b1);
-    ret = bfdec_set(&b1, b);
-    if (ret) {
-        bfdec_delete(&b1);
-        return ret;
+/* XXX: merge with JS_ToInt64Free with a specific flag ? */
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+    uint64_t res;
+
+    val = JS_ToBigIntFree(ctx, val);
+    if (JS_IsException(val)) {
+        *pres = 0;
+        return -1;
     }
-    ret = bfdec_rint(&b1, BF_RNDZ);
-    if (ret) {
-        bfdec_delete(&b1);
-        return BF_ST_INVALID_OP; /* must be an integer */
+    if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+        res = JS_VALUE_GET_SHORT_BIG_INT(val);
+    } else {
+        JSBigInt *p = JS_VALUE_GET_PTR(val);
+        /* return the value mod 2^64 */
+        res = p->tab[0];
+#if JS_LIMB_BITS == 32
+        if (p->len >= 2)
+            res |= (uint64_t)p->tab[1] << 32;
+#endif
+        JS_FreeValue(ctx, val);
     }
-    ret = bfdec_get_int32(&b2, &b1);
-    bfdec_delete(&b1);
-    if (ret)
-        return ret; /* overflow */
-    if (b2 < 0)
-        return BF_ST_INVALID_OP; /* must be positive */
-    return bfdec_pow_ui(r, a, b2);
+    *pres = res;
+    return 0;
 }
 
-static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
-                                      JSValue *pres, JSValue op1, JSValue op2)
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
 {
-    bfdec_t *r, *a, *b;
-    int ret;
-    JSValue res;
+    return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
 
-    res = JS_NewBigDecimal(ctx);
-    if (JS_IsException(res))
-        goto fail;
-    r = JS_GetBigDecimal(res);
+static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+                                                     JSValue *sp,
+                                                     OPCodeEnum op)
+{
+    JSValue op1;
+    int v;
+    uint32_t tag;
+    JSBigIntBuf buf1;
+    JSBigInt *p1;
 
-    a = JS_ToBigDecimal(ctx, op1);
-    if (!a)
-        goto fail;
-    b = JS_ToBigDecimal(ctx, op2);
-    if (!b)
-        goto fail;
-    switch(op) {
-    case OP_add:
-        ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_sub:
-        ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_mul:
-        ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_div:
-        ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
-        break;
-    case OP_math_mod:
-        /* Euclidian remainder */
-        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
+    op1 = sp[-1];
+    /* fast path for float64 */
+    if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
+        goto handle_float64;
+    op1 = JS_ToNumericFree(ctx, op1);
+    if (JS_IsException(op1))
+        goto exception;
+    tag = JS_VALUE_GET_TAG(op1);
+    switch(tag) {
+    case JS_TAG_INT:
+        {
+            int64_t v64;
+            v64 = JS_VALUE_GET_INT(op1);
+            switch(op) {
+            case OP_inc:
+            case OP_dec:
+                v = 2 * (op - OP_dec) - 1;
+                v64 += v;
+                break;
+            case OP_plus:
+                break;
+            case OP_neg:
+                if (v64 == 0) {
+                    sp[-1] = __JS_NewFloat64(ctx, -0.0);
+                    return 0;
+                } else {
+                    v64 = -v64;
+                }
+                break;
+            default:
+                abort();
+            }
+            sp[-1] = JS_NewInt64(ctx, v64);
+        }
         break;
-    case OP_mod:
-        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
+    case JS_TAG_SHORT_BIG_INT:
+        {
+            int64_t v;
+            v = JS_VALUE_GET_SHORT_BIG_INT(op1);
+            switch(op) {
+            case OP_plus:
+                JS_ThrowTypeError(ctx, "bigint argument with unary +");
+                goto exception;
+            case OP_inc:
+                if (v == JS_SHORT_BIG_INT_MAX)
+                    goto bigint_slow_case;
+                sp[-1] = __JS_NewShortBigInt(ctx, v + 1);
+                break;
+            case OP_dec:
+                if (v == JS_SHORT_BIG_INT_MIN)
+                    goto bigint_slow_case;
+                sp[-1] = __JS_NewShortBigInt(ctx, v - 1);
+                break;
+            case OP_neg:
+                v = JS_VALUE_GET_SHORT_BIG_INT(op1);
+                if (v == JS_SHORT_BIG_INT_MIN) {
+                bigint_slow_case:
+                    p1 = js_bigint_set_short(&buf1, op1);
+                    goto bigint_slow_case1;
+                }
+                sp[-1] = __JS_NewShortBigInt(ctx, -v);
+                break;
+            default:
+                abort();
+            }
+        }
         break;
-    case OP_pow:
-        ret = js_bfdec_pow(r, a, b);
+    case JS_TAG_BIG_INT:
+        {
+            JSBigInt *r;
+            p1 = JS_VALUE_GET_PTR(op1);
+        bigint_slow_case1:
+            switch(op) {
+            case OP_plus:
+                JS_ThrowTypeError(ctx, "bigint argument with unary +");
+                goto exception;
+            case OP_inc:
+            case OP_dec:
+                {
+                    JSBigIntBuf buf2;
+                    JSBigInt *p2;
+                    p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1);
+                    r = js_bigint_add(ctx, p1, p2, 0);
+                }
+                break;
+            case OP_neg:
+                r = js_bigint_neg(ctx, p1);
+                break;
+            case OP_not:
+                r = js_bigint_not(ctx, p1);
+                break;
+            default:
+                abort();
+            }
+            JS_FreeValue(ctx, op1);
+            if (!r)
+                goto exception;
+            sp[-1] = JS_CompactBigInt(ctx, r);
+        }
         break;
     default:
-        abort();
+    handle_float64:
+        {
+            double d;
+            d = JS_VALUE_GET_FLOAT64(op1);
+            switch(op) {
+            case OP_inc:
+            case OP_dec:
+                v = 2 * (op - OP_dec) - 1;
+                d += v;
+                break;
+            case OP_plus:
+                break;
+            case OP_neg:
+                d = -d;
+                break;
+            default:
+                abort();
+            }
+            sp[-1] = __JS_NewFloat64(ctx, d);
+        }
+        break;
     }
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    if (unlikely(ret)) {
-        JS_FreeValue(ctx, res);
-        throw_bf_exception(ctx, ret);
+    return 0;
+ exception:
+    sp[-1] = JS_UNDEFINED;
+    return -1;
+}
+
+static __exception int js_post_inc_slow(JSContext *ctx,
+                                        JSValue *sp, OPCodeEnum op)
+{
+    JSValue op1;
+
+    /* XXX: allow custom operators */
+    op1 = sp[-1];
+    op1 = JS_ToNumericFree(ctx, op1);
+    if (JS_IsException(op1)) {
+        sp[-1] = JS_UNDEFINED;
         return -1;
     }
-    *pres = res;
+    sp[-1] = op1;
+    sp[0] = JS_DupValue(ctx, op1);
+    return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+    JSValue op1;
+
+    op1 = sp[-1];
+    op1 = JS_ToNumericFree(ctx, op1);
+    if (JS_IsException(op1))
+        goto exception;
+    if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) {
+        sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1));
+    } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
+        JSBigInt *r;
+        r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1));
+        JS_FreeValue(ctx, op1);
+        if (!r)
+            goto exception;
+        sp[-1] = JS_CompactBigInt(ctx, r);
+    } else {
+        int32_t v1;
+        if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
+            goto exception;
+        sp[-1] = JS_NewInt32(ctx, ~v1);
+    }
     return 0;
- fail:
-    JS_FreeValue(ctx, res);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
+ exception:
+    sp[-1] = JS_UNDEFINED;
     return -1;
 }
-#endif /* CONFIG_BIGNUM */
 
 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
                                                       OPCodeEnum op)
@@ -13497,28 +13306,50 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         d2 = JS_VALUE_GET_FLOAT64(op2);
         goto handle_float64;
     }
-
-#ifdef CONFIG_BIGNUM
-    /* try to call an overloaded operator */
-    if ((tag1 == JS_TAG_OBJECT &&
-         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
-        (tag2 == JS_TAG_OBJECT &&
-         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
-        JSValue res;
-        int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
-        if (ret != 0) {
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            if (ret < 0) {
-                goto exception;
-            } else {
-                sp[-2] = res;
-                return 0;
+    /* fast path for short big int operations */
+    if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+        js_slimb_t v1, v2;
+        js_sdlimb_t v;
+        v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+        v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+        switch(op) {
+        case OP_sub:
+            v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2;
+            break;
+        case OP_mul:
+            v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2;
+            break;
+        case OP_div:
+            if (v2 == 0 ||
+                ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
+                 v2 == -1)) {
+                goto slow_big_int;
+            }
+            sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2);
+            return 0;
+        case OP_mod:
+            if (v2 == 0 ||
+                ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
+                 v2 == -1)) {
+                goto slow_big_int;
             }
+            sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2);
+            return 0;
+        case OP_pow:
+            goto slow_big_int;
+        default:
+            abort();
+        }
+        if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
+            sp[-2] = __JS_NewShortBigInt(ctx, v);
+        } else {
+            JSBigInt *r = js_bigint_new_di(ctx, v);
+            if (!r)
+                goto exception;
+            sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
         }
+        return 0;
     }
-#endif
-
     op1 = JS_ToNumericFree(ctx, op1);
     if (JS_IsException(op1)) {
         JS_FreeValue(ctx, op2);
@@ -13543,34 +13374,14 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
             break;
         case OP_mul:
             v = (int64_t)v1 * (int64_t)v2;
-            if (is_math_mode(ctx) &&
-                (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
-                goto handle_bigint;
             if (v == 0 && (v1 | v2) < 0) {
                 sp[-2] = __JS_NewFloat64(ctx, -0.0);
                 return 0;
             }
             break;
         case OP_div:
-            if (is_math_mode(ctx))
-                goto handle_bigint;
-            sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
+            sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
             return 0;
-#ifdef CONFIG_BIGNUM
-        case OP_math_mod:
-            if (unlikely(v2 == 0)) {
-                throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
-                goto exception;
-            }
-            v = (int64_t)v1 % (int64_t)v2;
-            if (v < 0) {
-                if (v2 < 0)
-                    v -= v2;
-                else
-                    v += v2;
-            }
-            break;
-#endif
         case OP_mod:
             if (v1 < 0 || v2 <= 0) {
                 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
@@ -13580,31 +13391,53 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
             }
             break;
         case OP_pow:
-            if (!is_math_mode(ctx)) {
-                sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
-                return 0;
-            } else {
-                goto handle_bigint;
-            }
-            break;
+            sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
+            return 0;
         default:
             abort();
         }
         sp[-2] = JS_NewInt64(ctx, v);
-    } else
-#ifdef CONFIG_BIGNUM
-    if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
-        if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-            goto exception;
-    } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
-        if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-            goto exception;
-    } else
-#endif
-    if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
-    handle_bigint:
-        if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+    } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) &&
+               (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) {
+        JSBigInt *p1, *p2, *r;
+        JSBigIntBuf buf1, buf2;
+    slow_big_int:
+        /* bigint result */
+        if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+            p1 = js_bigint_set_short(&buf1, op1);
+        else
+            p1 = JS_VALUE_GET_PTR(op1);
+        if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+            p2 = js_bigint_set_short(&buf2, op2);
+        else
+            p2 = JS_VALUE_GET_PTR(op2);
+        switch(op) {
+        case OP_add:
+            r = js_bigint_add(ctx, p1, p2, 0);
+            break;
+        case OP_sub:
+            r = js_bigint_add(ctx, p1, p2, 1);
+            break;
+        case OP_mul:
+            r = js_bigint_mul(ctx, p1, p2);
+            break;
+        case OP_div:
+            r = js_bigint_divrem(ctx, p1, p2, FALSE);
+            break;
+        case OP_mod:
+            r = js_bigint_divrem(ctx, p1, p2, TRUE);
+            break;
+        case OP_pow:
+            r = js_bigint_pow(ctx, p1, p2);
+            break;
+        default:
+            abort();
+        }
+        JS_FreeValue(ctx, op1);
+        JS_FreeValue(ctx, op2);
+        if (!r)
             goto exception;
+        sp[-2] = JS_CompactBigInt(ctx, r);
     } else {
         double dr;
         /* float64 result */
@@ -13615,8 +13448,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         if (JS_ToFloat64Free(ctx, &d2, op2))
             goto exception;
     handle_float64:
-        if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
-            goto handle_bigint;
         switch(op) {
         case OP_sub:
             dr = d1 - d2;
@@ -13630,15 +13461,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         case OP_mod:
             dr = fmod(d1, d2);
             break;
-#ifdef CONFIG_BIGNUM
-        case OP_math_mod:
-            d2 = fabs(d2);
-            dr = fmod(d1, d2);
-            /* XXX: loss of accuracy if dr < 0 */
-            if (dr < 0)
-                dr += d2;
-            break;
-#endif
         case OP_pow:
             dr = js_pow(d1, d2);
             break;
@@ -13672,31 +13494,25 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
         return 0;
     }
-
-    if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
-#ifdef CONFIG_BIGNUM
-        /* try to call an overloaded operator */
-        if ((tag1 == JS_TAG_OBJECT &&
-             (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
-              tag2 != JS_TAG_STRING)) ||
-            (tag2 == JS_TAG_OBJECT &&
-             (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
-              tag1 != JS_TAG_STRING))) {
-            JSValue res;
-            int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
-                                                 FALSE, HINT_NONE);
-            if (ret != 0) {
-                JS_FreeValue(ctx, op1);
-                JS_FreeValue(ctx, op2);
-                if (ret < 0) {
-                    goto exception;
-                } else {
-                    sp[-2] = res;
-                    return 0;
-                }
-            }
+    /* fast path for short bigint */
+    if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+        js_slimb_t v1, v2;
+        js_sdlimb_t v;
+        v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+        v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+        v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2;
+        if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
+            sp[-2] = __JS_NewShortBigInt(ctx, v);
+        } else {
+            JSBigInt *r = js_bigint_new_di(ctx, v);
+            if (!r)
+                goto exception;
+            sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
         }
-#endif
+        return 0;
+    }
+    
+    if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
         if (JS_IsException(op1)) {
             JS_FreeValue(ctx, op2);
@@ -13739,20 +13555,25 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
         v2 = JS_VALUE_GET_INT(op2);
         v = (int64_t)v1 + (int64_t)v2;
         sp[-2] = JS_NewInt64(ctx, v);
-    } else
-#ifdef CONFIG_BIGNUM
-    if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
-        if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
-            goto exception;
-    } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
-        if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
-            goto exception;
-    } else
-#endif
-    if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
-    handle_bigint:
-        if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+    } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+               (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
+        JSBigInt *p1, *p2, *r;
+        JSBigIntBuf buf1, buf2;
+        /* bigint result */
+        if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+            p1 = js_bigint_set_short(&buf1, op1);
+        else
+            p1 = JS_VALUE_GET_PTR(op1);
+        if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+            p2 = js_bigint_set_short(&buf2, op2);
+        else
+            p2 = JS_VALUE_GET_PTR(op2);
+        r = js_bigint_add(ctx, p1, p2, 0);
+        JS_FreeValue(ctx, op1);
+        JS_FreeValue(ctx, op2);
+        if (!r)
             goto exception;
+        sp[-2] = JS_CompactBigInt(ctx, r);
     } else {
         double d1, d2;
         /* float64 result */
@@ -13762,8 +13583,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
         }
         if (JS_ToFloat64Free(ctx, &d2, op2))
             goto exception;
-        if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
-            goto handle_bigint;
         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
     }
     return 0;
@@ -13786,27 +13605,62 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
     tag1 = JS_VALUE_GET_NORM_TAG(op1);
     tag2 = JS_VALUE_GET_NORM_TAG(op2);
 
-#ifdef CONFIG_BIGNUM
-    /* try to call an overloaded operator */
-    if ((tag1 == JS_TAG_OBJECT &&
-         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
-        (tag2 == JS_TAG_OBJECT &&
-         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
-        JSValue res;
-        int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
-        if (ret != 0) {
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            if (ret < 0) {
-                goto exception;
-            } else {
-                sp[-2] = res;
-                return 0;
-            }
+    if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+        js_slimb_t v1, v2, v;
+        js_sdlimb_t vd;
+        v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+        v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+        /* bigint fast path */
+        switch(op) {
+        case OP_and:
+            v = v1 & v2;
+            break;
+        case OP_or:
+            v = v1 | v2;
+            break;
+        case OP_xor:
+            v = v1 ^ v2;
+            break;
+        case OP_sar:
+            if (v2 > (JS_LIMB_BITS - 1)) {
+                goto slow_big_int;
+            } else if (v2 < 0) {
+                if (v2 < -(JS_LIMB_BITS - 1))
+                    goto slow_big_int;
+                v2 = -v2;
+                goto bigint_shl;
+            }
+        bigint_sar:
+            v = v1 >> v2;
+            break;
+        case OP_shl:
+            if (v2 > (JS_LIMB_BITS - 1)) {
+                goto slow_big_int;
+            } else if (v2 < 0) {
+                if (v2 < -(JS_LIMB_BITS - 1))
+                    goto slow_big_int;
+                v2 = -v2;
+                goto bigint_sar;
+            }
+        bigint_shl:
+            vd = (js_sdlimb_t)v1 << v2;
+            if (likely(vd >= JS_SHORT_BIG_INT_MIN &&
+                       vd <= JS_SHORT_BIG_INT_MAX)) {
+                v = vd;
+            } else {
+                JSBigInt *r = js_bigint_new_di(ctx, vd);
+                if (!r)
+                    goto exception;
+                sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
+                return 0;
+            }
+            break;
+        default:
+            abort();
         }
+        sp[-2] = __JS_NewShortBigInt(ctx, v);
+        return 0;
     }
-#endif
-
     op1 = JS_ToNumericFree(ctx, op1);
     if (JS_IsException(op1)) {
         JS_FreeValue(ctx, op2);
@@ -13818,22 +13672,52 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
         goto exception;
     }
 
-    if (is_math_mode(ctx))
-        goto bigint_op;
-
     tag1 = JS_VALUE_GET_TAG(op1);
     tag2 = JS_VALUE_GET_TAG(op2);
-    if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
-        if (tag1 != tag2) {
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            JS_ThrowTypeError(ctx, "both operands must be bigint");
-            goto exception;
-        } else {
-        bigint_op:
-            if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-                goto exception;
+    if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+        (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
+        JSBigInt *p1, *p2, *r;
+        JSBigIntBuf buf1, buf2;
+    slow_big_int:
+        if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+            p1 = js_bigint_set_short(&buf1, op1);
+        else
+            p1 = JS_VALUE_GET_PTR(op1);
+        if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+            p2 = js_bigint_set_short(&buf2, op2);
+        else
+            p2 = JS_VALUE_GET_PTR(op2);
+        switch(op) {
+        case OP_and:
+        case OP_or:
+        case OP_xor:
+            r = js_bigint_logic(ctx, p1, p2, op);
+            break;
+        case OP_shl:
+        case OP_sar:
+            {
+                js_slimb_t shift;
+                shift = js_bigint_get_si_sat(p2);
+                if (shift > INT32_MAX)
+                    shift = INT32_MAX;
+                else if (shift < -INT32_MAX)
+                    shift = -INT32_MAX;
+                if (op == OP_sar)
+                    shift = -shift;
+                if (shift >= 0)
+                    r = js_bigint_shl(ctx, p1, shift);
+                else
+                    r = js_bigint_shr(ctx, p1, -shift);
+            }
+            break;
+        default:
+            abort();
         }
+        JS_FreeValue(ctx, op1);
+        JS_FreeValue(ctx, op2);
+        if (!r)
+            goto exception;
+        sp[-2] = JS_CompactBigInt(ctx, r);
     } else {
         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
             JS_FreeValue(ctx, op2);
@@ -13869,100 +13753,98 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
     return -1;
 }
 
-/* Note: also used for bigint */
-static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
-                               JSValue op1, JSValue op2)
+/* op1 must be a bigint or int. */
+static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1,
+                                JSValue op1)
 {
-    bf_t a_s, b_s, *a, *b;
-    int res;
-
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, op2);
-        return -1;
-    }
-    b = JS_ToBigFloat(ctx, &b_s, op2);
-    if (!b) {
-        if (a == &a_s)
-            bf_delete(a);
-        JS_FreeValue(ctx, op1);
-        return -1;
-    }
-    switch(op) {
-    case OP_lt:
-        res = bf_cmp_lt(a, b); /* if NaN return false */
-        break;
-    case OP_lte:
-        res = bf_cmp_le(a, b); /* if NaN return false */
-        break;
-    case OP_gt:
-        res = bf_cmp_lt(b, a); /* if NaN return false */
+    JSBigInt *p1;
+    
+    switch(JS_VALUE_GET_TAG(op1)) {
+    case JS_TAG_INT:
+        p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1));
         break;
-    case OP_gte:
-        res = bf_cmp_le(b, a); /* if NaN return false */
+    case JS_TAG_SHORT_BIG_INT:
+        p1 = js_bigint_set_short(buf1, op1);
         break;
-    case OP_eq:
-        res = bf_cmp_eq(a, b); /* if NaN return false */
+    case JS_TAG_BIG_INT:
+        p1 = JS_VALUE_GET_PTR(op1);
         break;
     default:
         abort();
     }
-    if (a == &a_s)
-        bf_delete(a);
-    if (b == &b_s)
-        bf_delete(b);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    return res;
+    return p1;
 }
 
-#ifdef CONFIG_BIGNUM
-static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
-                                 JSValue op1, JSValue op2)
+/* op1 and op2 must be numeric types and at least one must be a
+   bigint. No exception is generated. */
+static int js_compare_bigint(JSContext *ctx, OPCodeEnum op,
+                             JSValue op1, JSValue op2)
 {
-    bfdec_t *a, *b;
-    int res;
-
-    /* Note: binary floats are converted to bigdecimal with
-       toString(). It is not mathematically correct but is consistent
-       with the BigDecimal() constructor behavior */
-    op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
-    if (JS_IsException(op1)) {
-        JS_FreeValue(ctx, op2);
-        return -1;
-    }
-    op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
-    if (JS_IsException(op2)) {
+    int res, val, tag1, tag2;
+    JSBigIntBuf buf1, buf2;
+    JSBigInt *p1, *p2;
+    
+    tag1 = JS_VALUE_GET_NORM_TAG(op1);
+    tag2 = JS_VALUE_GET_NORM_TAG(op2);
+    if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) &&
+        (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) {
+        /* fast path */
+        js_slimb_t v1, v2;
+        if (tag1 == JS_TAG_INT)
+            v1 = JS_VALUE_GET_INT(op1);
+        else
+            v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+        if (tag2 == JS_TAG_INT)
+            v2 = JS_VALUE_GET_INT(op2);
+        else
+            v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+        val = (v1 > v2) - (v1 < v2);
+    } else {
+        if (tag1 == JS_TAG_FLOAT64) {
+            p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
+            val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1));
+            if (val == 2)
+                goto unordered;
+            val = -val;
+        } else if (tag2 == JS_TAG_FLOAT64) {
+            p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
+            val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2));
+            if (val == 2) {
+            unordered:
+                JS_FreeValue(ctx, op1);
+                JS_FreeValue(ctx, op2);
+                return FALSE;
+            }
+        } else {
+            p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
+            p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
+            val = js_bigint_cmp(ctx, p1, p2);
+        }
         JS_FreeValue(ctx, op1);
-        return -1;
+        JS_FreeValue(ctx, op2);
     }
-    a = JS_ToBigDecimal(ctx, op1); /* cannot fail */
-    b = JS_ToBigDecimal(ctx, op2); /* cannot fail */
 
     switch(op) {
     case OP_lt:
-        res = bfdec_cmp_lt(a, b); /* if NaN return false */
+        res = val < 0;
         break;
     case OP_lte:
-        res = bfdec_cmp_le(a, b); /* if NaN return false */
+        res = val <= 0;
         break;
     case OP_gt:
-        res = bfdec_cmp_lt(b, a); /* if NaN return false */
+        res = val > 0;
         break;
     case OP_gte:
-        res = bfdec_cmp_le(b, a); /* if NaN return false */
+        res = val >= 0;
         break;
     case OP_eq:
-        res = bfdec_cmp_eq(a, b); /* if NaN return false */
+        res = val == 0;
         break;
     default:
         abort();
     }
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
     return res;
 }
-#endif /* !CONFIG_BIGNUM */
 
 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
                                         OPCodeEnum op)
@@ -13975,27 +13857,6 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
     op2 = sp[-1];
     tag1 = JS_VALUE_GET_NORM_TAG(op1);
     tag2 = JS_VALUE_GET_NORM_TAG(op2);
-#ifdef CONFIG_BIGNUM
-    /* try to call an overloaded operator */
-    if ((tag1 == JS_TAG_OBJECT &&
-         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
-        (tag2 == JS_TAG_OBJECT &&
-         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
-        JSValue ret;
-        res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
-                                         FALSE, HINT_NUMBER);
-        if (res != 0) {
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            if (res < 0) {
-                goto exception;
-            } else {
-                sp[-2] = ret;
-                return 0;
-            }
-        }
-    }
-#endif
     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
     if (JS_IsException(op1)) {
         JS_FreeValue(ctx, op2);
@@ -14036,17 +13897,20 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
         /* fast path for float64/int */
         goto float64_compare;
     } else {
-        if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
-             (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
-            !is_math_mode(ctx)) {
+        if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+              tag2 == JS_TAG_STRING) ||
+             ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) &&
+              tag1 == JS_TAG_STRING))) {
             if (tag1 == JS_TAG_STRING) {
                 op1 = JS_StringToBigInt(ctx, op1);
-                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
+                    JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
                     goto invalid_bigint_string;
             }
             if (tag2 == JS_TAG_STRING) {
                 op2 = JS_StringToBigInt(ctx, op2);
-                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
+                    JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) {
                 invalid_bigint_string:
                     JS_FreeValue(ctx, op1);
                     JS_FreeValue(ctx, op2);
@@ -14070,21 +13934,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
         tag1 = JS_VALUE_GET_NORM_TAG(op1);
         tag2 = JS_VALUE_GET_NORM_TAG(op2);
 
-#ifdef CONFIG_BIGNUM
-        if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
-            res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
-            if (res < 0)
-                goto exception;
-        } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
-            res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
-            if (res < 0)
-                goto exception;
-        } else
-#endif
-        if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
-            res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
-            if (res < 0)
-                goto exception;
+        if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
+            tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
+            res = js_compare_bigint(ctx, op, op1, op2);
         } else {
             double d1, d2;
 
@@ -14128,21 +13980,15 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
 
 static BOOL tag_is_number(uint32_t tag)
 {
-    return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
-            tag == JS_TAG_FLOAT64
-#ifdef CONFIG_BIGNUM
-            || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL
-#endif
-            );
+    return (tag == JS_TAG_INT || 
+            tag == JS_TAG_FLOAT64 ||
+            tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT);
 }
 
 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
                                             BOOL is_neq)
 {
     JSValue op1, op2;
-#ifdef CONFIG_BIGNUM
-    JSValue ret;
-#endif
     int res;
     uint32_t tag1, tag2;
 
@@ -14170,42 +14016,10 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
                 d2 = JS_VALUE_GET_INT(op2);
             }
             res = (d1 == d2);
-        } else
-#ifdef CONFIG_BIGNUM
-        if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
-            res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
-            if (res < 0)
-                goto exception;
-        } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
-            res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
-            if (res < 0)
-                goto exception;
-        } else
-#endif
-        {
-            res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
-            if (res < 0)
-                goto exception;
+        } else {
+            res = js_compare_bigint(ctx, OP_eq, op1, op2);
         }
     } else if (tag1 == tag2) {
-#ifdef CONFIG_BIGNUM
-        if (tag1 == JS_TAG_OBJECT) {
-            /* try the fallback operator */
-            res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
-                                             is_neq ? OP_neq : OP_eq,
-                                             FALSE, HINT_NONE);
-            if (res != 0) {
-                JS_FreeValue(ctx, op1);
-                JS_FreeValue(ctx, op2);
-                if (res < 0) {
-                    goto exception;
-                } else {
-                    sp[-2] = ret;
-                    return 0;
-                }
-            }
-        }
-#endif
         res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
@@ -14213,16 +14027,18 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
     } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
                (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
 
-        if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
-            !is_math_mode(ctx)) {
+        if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
+            tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
             if (tag1 == JS_TAG_STRING) {
                 op1 = JS_StringToBigInt(ctx, op1);
-                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
+                    JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
                     goto invalid_bigint_string;
             }
             if (tag2 == JS_TAG_STRING) {
                 op2 = JS_StringToBigInt(ctx, op2);
-                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
+                    JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) {
                 invalid_bigint_string:
                     JS_FreeValue(ctx, op1);
                     JS_FreeValue(ctx, op2);
@@ -14253,22 +14069,6 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
                 (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
                (tag2 == JS_TAG_OBJECT &&
                 (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
-#ifdef CONFIG_BIGNUM
-        /* try the fallback operator */
-        res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
-                                         is_neq ? OP_neq : OP_eq,
-                                         FALSE, HINT_NONE);
-        if (res != 0) {
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            if (res < 0) {
-                goto exception;
-            } else {
-                sp[-2] = ret;
-                return 0;
-            }
-        }
-#endif
         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
         if (JS_IsException(op1)) {
             JS_FreeValue(ctx, op2);
@@ -14319,10 +14119,10 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
         JS_FreeValue(ctx, op1);
         goto exception;
     }
-    /* XXX: could forbid >>> in bignum mode */
-    if (!is_math_mode(ctx) &&
-        (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
-         JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
+    if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
+        JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT ||
+        JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT ||
+        JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) {
         JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
         JS_FreeValue(ctx, op1);
         JS_FreeValue(ctx, op2);
@@ -14340,67 +14140,6 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
     return -1;
 }
 
-#ifdef CONFIG_BIGNUM
-static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
-                                       int64_t exponent)
-{
-    bf_t r_s, *r = &r_s;
-    double d;
-    int ret;
-
-    /* always convert to Float64 */
-    bf_init(ctx->bf_ctx, r);
-    ret = bf_mul_pow_radix(r, a, 10, exponent,
-                           53, bf_set_exp_bits(11) | BF_RNDN |
-                           BF_FLAG_SUBNORMAL);
-    bf_get_float64(r, &d, BF_RNDN);
-    bf_delete(r);
-    if (ret & BF_ST_MEM_ERROR)
-        return JS_ThrowOutOfMemory(ctx);
-    else
-        return __JS_NewFloat64(ctx, d);
-}
-
-static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
-{
-    bf_t a_s, *a, *r;
-    JSValue op1, op2, res;
-    int64_t e;
-    int ret;
-
-    res = JS_NewBigFloat(ctx);
-    if (JS_IsException(res))
-        return -1;
-    r = JS_GetBigFloat(res);
-    op1 = sp[-2];
-    op2 = sp[-1];
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        return -1;
-    }
-    if (JS_IsBigInt(ctx, op2)) {
-        ret = JS_ToBigInt64(ctx, &e, op2);
-    } else {
-        ret = JS_ToInt64(ctx, &e, op2);
-    }
-    if (ret) {
-        if (a == &a_s)
-            bf_delete(a);
-        JS_FreeValue(ctx, res);
-        return -1;
-    }
-
-    bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
-    if (a == &a_s)
-        bf_delete(a);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    sp[-2] = res;
-    return 0;
-}
-#endif
-
 /* XXX: Should take JSValueConst arguments */
 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
                           JSStrictEqModeEnum eq_mode)
@@ -14493,63 +14232,29 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
             res = (d1 == d2); /* if NaN return false and +0 == -0 */
         }
         goto done_no_free;
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
         {
-            bf_t a_s, *a, b_s, *b;
-            if (tag1 != tag2) {
-                res = FALSE;
-                break;
-            }
-            a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */
-            b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */
-            res = bf_cmp_eq(a, b);
-            if (a == &a_s)
-                bf_delete(a);
-            if (b == &b_s)
-                bf_delete(b);
-        }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p1, *p2;
-            const bf_t *a, *b;
-            if (tag1 != tag2) {
-                res = FALSE;
-                break;
-            }
-            p1 = JS_VALUE_GET_PTR(op1);
-            p2 = JS_VALUE_GET_PTR(op2);
-            a = &p1->num;
-            b = &p2->num;
-            if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
-                if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
-                           a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
-                    res = TRUE;
-                } else {
-                    res = (bf_cmp_full(a, b) == 0);
-                }
-            } else {
-                res = bf_cmp_eq(a, b);
-            }
-        }
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        {
-            JSBigDecimal *p1, *p2;
-            const bfdec_t *a, *b;
-            if (tag1 != tag2) {
+            JSBigIntBuf buf1, buf2;
+            JSBigInt *p1, *p2;
+
+            if (tag2 != JS_TAG_SHORT_BIG_INT &&
+                tag2 != JS_TAG_BIG_INT) {
                 res = FALSE;
                 break;
             }
-            p1 = JS_VALUE_GET_PTR(op1);
-            p2 = JS_VALUE_GET_PTR(op2);
-            a = &p1->num;
-            b = &p2->num;
-            res = bfdec_cmp_eq(a, b);
+            
+            if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+                p1 = js_bigint_set_short(&buf1, op1);
+            else
+                p1 = JS_VALUE_GET_PTR(op1);
+            if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+                p2 = js_bigint_set_short(&buf2, op2);
+            else
+                p2 = JS_VALUE_GET_PTR(op2);
+            res = (js_bigint_cmp(ctx, p1, p2) == 0);
         }
         break;
-#endif
     default:
         res = FALSE;
         break;
@@ -14709,17 +14414,10 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
 
     tag = JS_VALUE_GET_NORM_TAG(op1);
     switch(tag) {
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
         atom = JS_ATOM_bigint;
         break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        atom = JS_ATOM_bigfloat;
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        atom = JS_ATOM_bigdecimal;
-        break;
-#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         atom = JS_ATOM_number;
@@ -15996,17 +15694,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
     sf->prev_frame = prev_sf;
     rt->current_stack_frame = sf;
     ctx = p->u.cfunc.realm; /* change the current realm */
-
-#ifdef CONFIG_BIGNUM
-    /* we only propagate the bignum mode as some runtime functions
-       test it */
-    if (prev_sf)
-        sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
-    else
-        sf->js_mode = 0;
-#else
     sf->js_mode = 0;
-#endif
     sf->cur_func = (JSValue)func_obj;
     sf->arg_count = argc;
     arg_buf = argv;
@@ -16290,6 +15978,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
             *sp++ = JS_NewInt32(ctx, get_u32(pc));
             pc += 4;
             BREAK;
+        CASE(OP_push_bigint_i32):
+            *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc));
+            pc += 4;
+            BREAK;
         CASE(OP_push_const):
             *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
             pc += 4;
@@ -18004,11 +17696,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     v2 = JS_VALUE_GET_INT(op2);
                     r = (int64_t)v1 * v2;
                     if (unlikely((int)r != r)) {
-#ifdef CONFIG_BIGNUM
-                        if (unlikely(sf->js_mode & JS_MODE_MATH) &&
-                            (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
-                            goto binary_arith_slow;
-#endif
                         d = (double)r;
                         goto mul_fp_res;
                     }
@@ -18020,10 +17707,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     sp[-2] = JS_NewInt32(ctx, r);
                     sp--;
                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
-#ifdef CONFIG_BIGNUM
-                    if (unlikely(sf->js_mode & JS_MODE_MATH))
-                        goto binary_arith_slow;
-#endif
                     d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
                 mul_fp_res:
                     sp[-2] = __JS_NewFloat64(ctx, d);
@@ -18052,9 +17735,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
             }
             BREAK;
         CASE(OP_mod):
-#ifdef CONFIG_BIGNUM
-        CASE(OP_math_mod):
-#endif
             {
                 JSValue op1, op2;
                 op1 = sp[-2];
@@ -18237,28 +17917,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     uint32_t v1, v2;
                     v1 = JS_VALUE_GET_INT(op1);
                     v2 = JS_VALUE_GET_INT(op2);
-#ifdef CONFIG_BIGNUM
-                    {
-                        int64_t r;
-                        if (unlikely(sf->js_mode & JS_MODE_MATH)) {
-                            if (v2 > 0x1f)
-                                goto shl_slow;
-                            r = (int64_t)v1 << v2;
-                            if ((int)r != r)
-                                goto shl_slow;
-                        } else {
-                            v2 &= 0x1f;
-                        }
-                    }
-#else
                     v2 &= 0x1f;
-#endif
                     sp[-2] = JS_NewInt32(ctx, v1 << v2);
                     sp--;
                 } else {
-#ifdef CONFIG_BIGNUM
-                shl_slow:
-#endif
                     if (js_binary_logic_slow(ctx, sp, opcode))
                         goto exception;
                     sp--;
@@ -18273,7 +17935,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
                     uint32_t v2;
                     v2 = JS_VALUE_GET_INT(op2);
-                    /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
                     v2 &= 0x1f;
                     sp[-2] = JS_NewUint32(ctx,
                                           (uint32_t)JS_VALUE_GET_INT(op1) >>
@@ -18294,23 +17955,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
                     uint32_t v2;
                     v2 = JS_VALUE_GET_INT(op2);
-#ifdef CONFIG_BIGNUM
-                    if (unlikely(v2 > 0x1f)) {
-                        if (unlikely(sf->js_mode & JS_MODE_MATH))
-                            goto sar_slow;
-                        else
-                            v2 &= 0x1f;
-                    }
-#else
                     v2 &= 0x1f;
-#endif
                     sp[-2] = JS_NewInt32(ctx,
                                           (int)JS_VALUE_GET_INT(op1) >> v2);
                     sp--;
                 } else {
-#ifdef CONFIG_BIGNUM
-                sar_slow:
-#endif
                     if (js_binary_logic_slow(ctx, sp, opcode))
                         goto exception;
                     sp--;
@@ -18396,13 +18045,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
             OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
             OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
 
-#ifdef CONFIG_BIGNUM
-        CASE(OP_mul_pow10):
-            if (rt->bigfloat_ops.mul_pow10(ctx, sp))
-                goto exception;
-            sp--;
-            BREAK;
-#endif
         CASE(OP_in):
             if (js_operator_in(ctx, sp))
                 goto exception;
@@ -19796,9 +19438,6 @@ enum {
     TOK_AND_ASSIGN,
     TOK_XOR_ASSIGN,
     TOK_OR_ASSIGN,
-#ifdef CONFIG_BIGNUM
-    TOK_MATH_POW_ASSIGN,
-#endif
     TOK_POW_ASSIGN,
     TOK_LAND_ASSIGN,
     TOK_LOR_ASSIGN,
@@ -19818,9 +19457,6 @@ enum {
     TOK_STRICT_NEQ,
     TOK_LAND,
     TOK_LOR,
-#ifdef CONFIG_BIGNUM
-    TOK_MATH_POW,
-#endif
     TOK_POW,
     TOK_ARROW,
     TOK_ELLIPSIS,
@@ -20080,9 +19716,6 @@ typedef struct JSToken {
         } str;
         struct {
             JSValue val;
-#ifdef CONFIG_BIGNUM
-            slimb_t exponent; /* may be != 0 only if val is a float */
-#endif
         } num;
         struct {
             JSAtom atom;
@@ -20920,26 +20553,11 @@ static __exception int next_token(JSParseState *s)
         {
             JSValue ret;
             const uint8_t *p1;
-            int flags, radix;
+            int flags;
             flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
-                ATOD_ACCEPT_UNDERSCORES;
-            flags |= ATOD_ACCEPT_SUFFIX;
-#ifdef CONFIG_BIGNUM
-            if (s->cur_func->js_mode & JS_MODE_MATH) {
-                flags |= ATOD_MODE_BIGINT;
-                if (s->cur_func->js_mode & JS_MODE_MATH)
-                    flags |= ATOD_TYPE_BIG_FLOAT;
-            }
-#endif
-            radix = 0;
-#ifdef CONFIG_BIGNUM
-            s->token.u.num.exponent = 0;
-            ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
-                           flags, &s->token.u.num.exponent);
-#else
-            ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+                ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX;
+            ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0,
                           flags);
-#endif
             if (JS_IsException(ret))
                 goto fail;
             /* reject `10instanceof Number` */
@@ -21095,33 +20713,6 @@ static __exception int next_token(JSParseState *s)
             goto def_token;
         }
         break;
-#ifdef CONFIG_BIGNUM
-        /* in math mode, '^' is the power operator. '^^' is always the
-           xor operator and '**' is always the power operator */
-    case '^':
-        if (p[1] == '=') {
-            p += 2;
-            if (s->cur_func->js_mode & JS_MODE_MATH)
-                s->token.val = TOK_MATH_POW_ASSIGN;
-            else
-                s->token.val = TOK_XOR_ASSIGN;
-        } else if (p[1] == '^') {
-            if (p[2] == '=') {
-                p += 3;
-                s->token.val = TOK_XOR_ASSIGN;
-            } else {
-                p += 2;
-                s->token.val = '^';
-            }
-        } else {
-            p++;
-            if (s->cur_func->js_mode & JS_MODE_MATH)
-                s->token.val = TOK_MATH_POW;
-            else
-                s->token.val = '^';
-        }
-        break;
-#else
     case '^':
         if (p[1] == '=') {
             p += 2;
@@ -21130,7 +20721,6 @@ static __exception int next_token(JSParseState *s)
             goto def_token;
         }
         break;
-#endif
     case '|':
         if (p[1] == '=') {
             p += 2;
@@ -22464,21 +22054,7 @@ static int __exception js_parse_property_name(JSParseState *s,
     } else if (s->token.val == TOK_NUMBER) {
         JSValue val;
         val = s->token.u.num.val;
-#ifdef CONFIG_BIGNUM
-        if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            val = s->ctx->rt->bigfloat_ops.
-                mul_pow10_to_float64(s->ctx, &p->num,
-                                     s->token.u.num.exponent);
-            if (JS_IsException(val))
-                goto fail;
-            name = JS_ValueToAtom(s->ctx, val);
-            JS_FreeValue(s->ctx, val);
-        } else
-#endif
-        {
-            name = JS_ValueToAtom(s->ctx, val);
-        }
+        name = JS_ValueToAtom(s->ctx, val);
         if (name == JS_ATOM_NULL)
             goto fail;
         if (next_token(s))
@@ -24491,34 +24067,17 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
             if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
                 emit_op(s, OP_push_i32);
                 emit_u32(s, JS_VALUE_GET_INT(val));
-            } else
-#ifdef CONFIG_BIGNUM
-            if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
-                slimb_t e;
-                int ret;
-
-                /* need a runtime conversion */
-                /* XXX: could add a cache and/or do it once at
-                   the start of the function */
-                if (emit_push_const(s, val, 0) < 0)
-                    return -1;
-                e = s->token.u.num.exponent;
-                if (e == (int32_t)e) {
-                    emit_op(s, OP_push_i32);
-                    emit_u32(s, e);
+            } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+                int64_t v;
+                v = JS_VALUE_GET_SHORT_BIG_INT(val);
+                if (v >= INT32_MIN && v <= INT32_MAX) {
+                    emit_op(s, OP_push_bigint_i32);
+                    emit_u32(s, v);
                 } else {
-                    val = JS_NewBigInt64_1(s->ctx, e);
-                    if (JS_IsException(val))
-                        return -1;
-                    ret = emit_push_const(s, val, 0);
-                    JS_FreeValue(s->ctx, val);
-                    if (ret < 0)
-                        return -1;
+                    goto large_number;
                 }
-                emit_op(s, OP_mul_pow10);
-            } else
-#endif
-            {
+            } else {
+            large_number:
                 if (emit_push_const(s, val, 0) < 0)
                     return -1;
             }
@@ -25344,24 +24903,6 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
         break;
     }
     if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
-#ifdef CONFIG_BIGNUM
-        if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
-            /* Extended exponentiation syntax rules: we extend the ES7
-               grammar in order to have more intuitive semantics:
-               -2**2 evaluates to -4. */
-            if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
-                if (parse_flags & PF_POW_FORBIDDEN) {
-                    JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
-                    return -1;
-                }
-            }
-            if (next_token(s))
-                return -1;
-            if (js_parse_unary(s, PF_POW_ALLOWED))
-                return -1;
-            emit_op(s, OP_pow);
-        }
-#else
         if (s->token.val == TOK_POW) {
             /* Strict ES7 exponentiation syntax rules: To solve
                conficting semantics between different implementations
@@ -25378,7 +24919,6 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
                 return -1;
             emit_op(s, OP_pow);
         }
-#endif
     }
     return 0;
 }
@@ -25429,12 +24969,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
                 opcode = OP_div;
                 break;
             case '%':
-#ifdef CONFIG_BIGNUM
-                if (s->cur_func->js_mode & JS_MODE_MATH)
-                    opcode = OP_math_mod;
-                else
-#endif
-                    opcode = OP_mod;
+                opcode = OP_mod;
                 break;
             default:
                 return 0;
@@ -25864,18 +25399,9 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
             static const uint8_t assign_opcodes[] = {
                 OP_mul, OP_div, OP_mod, OP_add, OP_sub,
                 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
-#ifdef CONFIG_BIGNUM
-                OP_pow,
-#endif
                 OP_pow,
             };
             op = assign_opcodes[op - TOK_MUL_ASSIGN];
-#ifdef CONFIG_BIGNUM
-            if (s->cur_func->js_mode & JS_MODE_MATH) {
-                if (op == OP_mod)
-                    op = OP_math_mod;
-            }
-#endif
             emit_op(s, op);
         }
         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
@@ -29988,10 +29514,6 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
         printf("  mode:");
         if (b->js_mode & JS_MODE_STRICT)
             printf(" strict");
-#ifdef CONFIG_BIGNUM
-        if (b->js_mode & JS_MODE_MATH)
-            printf(" math");
-#endif
         printf("\n");
     }
     if (b->arg_count && b->vardefs) {
@@ -32562,6 +32084,26 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
             }
             goto no_change;
 
+        case OP_push_bigint_i32:
+            if (OPTIMIZE) {
+                /* transform i32(val) neg -> i32(-val) */
+                val = get_i32(bc_buf + pos + 1);
+                if (val != INT32_MIN
+                &&  code_match(&cc, pos_next, OP_neg, -1)) {
+                    if (cc.line_num >= 0) line_num = cc.line_num;
+                    if (code_match(&cc, cc.pos, OP_drop, -1)) {
+                        if (cc.line_num >= 0) line_num = cc.line_num;
+                    } else {
+                        add_pc2line_info(s, bc_out.size, line_num);
+                        dbuf_putc(&bc_out, OP_push_bigint_i32);
+                        dbuf_put_u32(&bc_out, -val);
+                    }
+                    pos_next = cc.pos;
+                    break;
+                }
+            }
+            goto no_change;
+
 #if SHORT_OPCODES
         case OP_push_const:
         case OP_fclosure:
@@ -33662,11 +33204,6 @@ static __exception int js_parse_directives(JSParseState *s)
         else if (!strcmp(str, "use strip")) {
             s->cur_func->js_mode |= JS_MODE_STRIP;
         }
-#endif
-#ifdef CONFIG_BIGNUM
-        else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
-            s->cur_func->js_mode |= JS_MODE_MATH;
-        }
 #endif
     }
     return js_parse_seek_token(s, &pos);
@@ -34738,17 +34275,9 @@ typedef enum BCTagEnum {
     BC_TAG_DATE,
     BC_TAG_OBJECT_VALUE,
     BC_TAG_OBJECT_REFERENCE,
-#ifdef CONFIG_BIGNUM
-    BC_TAG_BIG_FLOAT,
-    BC_TAG_BIG_DECIMAL,
-#endif
 } BCTagEnum;
 
-#ifdef CONFIG_BIGNUM
-#define BC_VERSION 0x43
-#else
-#define BC_VERSION 3
-#endif
+#define BC_VERSION 4
 
 typedef struct BCWriterState {
     JSContext *ctx;
@@ -34791,10 +34320,6 @@ static const char * const bc_tag_str[] = {
     "Date",
     "ObjectValue",
     "ObjectReference",
-#ifdef CONFIG_BIGNUM
-    "bigfloat",
-    "bigdecimal",
-#endif
 };
 #endif
 
@@ -35022,132 +34547,50 @@ static void JS_WriteString(BCWriterState *s, JSString *p)
     }
 }
 
-static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
+static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj)
 {
-    uint32_t tag, tag1;
-    int64_t e;
-    JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
-    bf_t *a = &bf->num;
-    size_t len, i, n1, j;
-    limb_t v;
-
-    tag = JS_VALUE_GET_TAG(obj);
-    switch(tag) {
-    case JS_TAG_BIG_INT:
-        tag1 = BC_TAG_BIG_INT;
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        tag1 = BC_TAG_BIG_FLOAT;
-        break;
-    case JS_TAG_BIG_DECIMAL:
-        tag1 = BC_TAG_BIG_DECIMAL;
-        break;
-#endif
-    default:
-        abort();
-    }
-    bc_put_u8(s, tag1);
+    JSBigIntBuf buf;
+    JSBigInt *p;
+    uint32_t len, i;
+    js_limb_t v, b;
+    int shift;
+    
+    bc_put_u8(s, BC_TAG_BIG_INT);
 
-    /* sign + exponent */
-    if (a->expn == BF_EXP_ZERO)
-        e = 0;
-    else if (a->expn == BF_EXP_INF)
-        e = 1;
-    else if (a->expn == BF_EXP_NAN)
-        e = 2;
-    else if (a->expn >= 0)
-        e = a->expn + 3;
+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT)
+        p = js_bigint_set_short(&buf, obj);
     else
-        e = a->expn;
-    e = (e * 2) | a->sign;
-    if (e < INT32_MIN || e > INT32_MAX) {
-        JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
-        return -1;
+        p = JS_VALUE_GET_PTR(obj);
+    if (p->len == 1 && p->tab[0] == 0) {
+        /* zero case */
+        len = 0;
+    } else {
+        /* compute the length of the two's complement representation
+           in bytes */
+        len = p->len * (JS_LIMB_BITS / 8);
+        v = p->tab[p->len - 1];
+        shift = JS_LIMB_BITS - 8;
+        while (shift > 0) {
+            b = (v >> shift) & 0xff;
+            if (b != 0x00 && b != 0xff)
+                break;
+            if ((b & 1) != ((v >> (shift - 1)) & 1))
+                break;
+            shift -= 8;
+            len--;
+        }
     }
-    bc_put_sleb128(s, e);
-
-    /* mantissa */
-    if (a->len != 0) {
-        if (tag != JS_TAG_BIG_DECIMAL) {
-            i = 0;
-            while (i < a->len && a->tab[i] == 0)
-                i++;
-            assert(i < a->len);
-            v = a->tab[i];
-            n1 = sizeof(limb_t);
-            while ((v & 0xff) == 0) {
-                n1--;
-                v >>= 8;
-            }
-            i++;
-            len = (a->len - i) * sizeof(limb_t) + n1;
-            if (len > INT32_MAX) {
-                JS_ThrowInternalError(s->ctx, "bignum is too large");
-                return -1;
-            }
-            bc_put_leb128(s, len);
-            /* always saved in byte based little endian representation */
-            for(j = 0; j < n1; j++) {
-                bc_put_u8(s, v >> (j * 8));
-            }
-            for(; i < a->len; i++) {
-                limb_t v = a->tab[i];
-#if LIMB_BITS == 32
-                bc_put_u32(s, v);
+    bc_put_leb128(s, len);
+    if (len > 0) {
+        for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) {
+#if JS_LIMB_BITS == 32
+            bc_put_u32(s, p->tab[i]);
 #else
-                bc_put_u64(s, v);
+            bc_put_u64(s, p->tab[i]);
 #endif
-            }
-        } else {
-            int bpos, d;
-            uint8_t v8;
-            size_t i0;
-
-            /* little endian BCD */
-            i = 0;
-            while (i < a->len && a->tab[i] == 0)
-                i++;
-            assert(i < a->len);
-            len = a->len * LIMB_DIGITS;
-            v = a->tab[i];
-            j = 0;
-            while ((v % 10) == 0) {
-                j++;
-                v /= 10;
-            }
-            len -= j;
-            assert(len > 0);
-            if (len > INT32_MAX) {
-                JS_ThrowInternalError(s->ctx, "bignum is too large");
-                return -1;
-            }
-            bc_put_leb128(s, len);
-
-            bpos = 0;
-            v8 = 0;
-            i0 = i;
-            for(; i < a->len; i++) {
-                if (i != i0) {
-                    v = a->tab[i];
-                    j = 0;
-                }
-                for(; j < LIMB_DIGITS; j++) {
-                    d = v % 10;
-                    v /= 10;
-                    if (bpos == 0) {
-                        v8 = d;
-                        bpos = 1;
-                    } else {
-                        bc_put_u8(s, v8 | (d << 4));
-                        bpos = 0;
-                    }
-                }
-            }
-            /* flush the last digit */
-            if (bpos) {
-                bc_put_u8(s, v8);
-            }
+        }
+        for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) {
+            bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff);
         }
     }
     return 0;
@@ -35512,10 +34955,6 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
             case JS_CLASS_STRING:
             case JS_CLASS_BOOLEAN:
             case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
-            case JS_CLASS_BIG_FLOAT:
-            case JS_CLASS_BIG_DECIMAL:
-#endif
                 bc_put_u8(s, BC_TAG_OBJECT_VALUE);
                 ret = JS_WriteObjectRec(s, p->u.object_data);
                 break;
@@ -35534,12 +34973,9 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
                 goto fail;
         }
         break;
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-    case JS_TAG_BIG_DECIMAL:
-#endif
-        if (JS_WriteBigNum(s, obj))
+        if (JS_WriteBigInt(s, obj))
             goto fail;
         break;
     default:
@@ -35947,138 +35383,54 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
     return 0;
 }
 
-static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
+static JSValue JS_ReadBigInt(BCReaderState *s)
 {
     JSValue obj = JS_UNDEFINED;
+    uint32_t len, i, n;
+    JSBigInt *p;
+    js_limb_t v;
     uint8_t v8;
-    int32_t e;
-    uint32_t len;
-    limb_t l, i, n;
-    JSBigFloat *p;
-    limb_t v;
-    bf_t *a;
-
-    p = js_new_bf(s->ctx);
-    if (!p)
+    
+    if (bc_get_leb128(s, &len))
         goto fail;
-    switch(tag) {
-    case BC_TAG_BIG_INT:
-        obj = JS_MKPTR(JS_TAG_BIG_INT, p);
-        break;
-#ifdef CONFIG_BIGNUM
-    case BC_TAG_BIG_FLOAT:
-        obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
-        break;
-    case BC_TAG_BIG_DECIMAL:
-        obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
-        break;
-#endif
-    default:
-        abort();
+    bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
+    if (len == 0) {
+        /* zero case */
+        bc_read_trace(s, "}\n");
+        return __JS_NewShortBigInt(s->ctx, 0);
     }
-
-    /* sign + exponent */
-    if (bc_get_sleb128(s, &e))
+    p = js_bigint_new(s->ctx,
+                      (len + (JS_LIMB_BITS / 8) - 1) / (JS_LIMB_BITS / 8));
+    if (!p)
         goto fail;
-
-    a = &p->num;
-    a->sign = e & 1;
-    e >>= 1;
-    if (e == 0)
-        a->expn = BF_EXP_ZERO;
-    else if (e == 1)
-        a->expn = BF_EXP_INF;
-    else if (e == 2)
-        a->expn = BF_EXP_NAN;
-    else if (e >= 3)
-        a->expn = e - 3;
-    else
-        a->expn = e;
-
-    /* mantissa */
-    if (a->expn != BF_EXP_ZERO &&
-        a->expn != BF_EXP_INF &&
-        a->expn != BF_EXP_NAN) {
-        if (bc_get_leb128(s, &len))
+    for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) {
+#if JS_LIMB_BITS == 32
+        if (bc_get_u32(s, &v))
             goto fail;
-        bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
-        if (len == 0) {
-            JS_ThrowInternalError(s->ctx, "invalid bignum length");
+#else
+        if (bc_get_u64(s, &v))
             goto fail;
-        }
-#ifdef CONFIG_BIGNUM
-        if (tag == BC_TAG_BIG_DECIMAL) {
-            l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
-        } else
 #endif
-        {
-            l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
-        }
-        if (bf_resize(a, l)) {
-            JS_ThrowOutOfMemory(s->ctx);
-            goto fail;
+        p->tab[i] = v;
+    }
+    n = len % (JS_LIMB_BITS / 8);
+    if (n != 0) {
+        int shift;
+        v = 0;
+        for(i = 0; i < n; i++) {
+            if (bc_get_u8(s, &v8))
+                goto fail;
+            v |= (js_limb_t)v8 << (i * 8);
         }
-#ifdef CONFIG_BIGNUM
-        if (tag == BC_TAG_BIG_DECIMAL) {
-            limb_t j;
-            int bpos, d;
-
-            bpos = 0;
-            for(i = 0; i < l; i++) {
-                if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
-                    j = LIMB_DIGITS - n;
-                } else {
-                    j = 0;
-                }
-                v = 0;
-                for(; j < LIMB_DIGITS; j++) {
-                    if (bpos == 0) {
-                        if (bc_get_u8(s, &v8))
-                            goto fail;
-                        d = v8 & 0xf;
-                        bpos = 1;
-                    } else {
-                        d = v8 >> 4;
-                        bpos = 0;
-                    }
-                    if (d >= 10) {
-                        JS_ThrowInternalError(s->ctx, "invalid digit");
-                        goto fail;
-                    }
-                    v += mp_pow_dec[j] * d;
-                }
-                a->tab[i] = v;
-            }
-        } else
-#endif  /* CONFIG_BIGNUM */
-        {
-            n = len & (sizeof(limb_t) - 1);
-            if (n != 0) {
-                v = 0;
-                for(i = 0; i < n; i++) {
-                    if (bc_get_u8(s, &v8))
-                        goto fail;
-                    v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
-                }
-                a->tab[0] = v;
-                i = 1;
-            } else {
-                i = 0;
-            }
-            for(; i < l; i++) {
-#if LIMB_BITS == 32
-                if (bc_get_u32(s, &v))
-                    goto fail;
-#else
-                if (bc_get_u64(s, &v))
-                    goto fail;
-#endif
-                a->tab[i] = v;
-            }
+        shift = JS_LIMB_BITS - n * 8;
+        /* extend the sign */
+        if (shift != 0) {
+            v = (js_slimb_t)(v << shift) >> shift;
         }
+        p->tab[p->len - 1] = v;
     }
     bc_read_trace(s, "}\n");
-    return obj;
+    return JS_CompactBigInt(s->ctx, p);
  fail:
     JS_FreeValue(s->ctx, obj);
     return JS_EXCEPTION;
@@ -36713,11 +36065,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
         obj = JS_ReadObjectValue(s);
         break;
     case BC_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case BC_TAG_BIG_FLOAT:
-    case BC_TAG_BIG_DECIMAL:
-#endif
-        obj = JS_ReadBigNum(s, tag);
+        obj = JS_ReadBigInt(s);
         break;
     case BC_TAG_OBJECT_REFERENCE:
         {
@@ -37147,17 +36495,10 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
     case JS_TAG_OBJECT:
     case JS_TAG_EXCEPTION:
         return JS_DupValue(ctx, val);
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
         goto set_value;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-        obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
-        goto set_value;
-    case JS_TAG_BIG_DECIMAL:
-        obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
-        goto set_value;
-#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
@@ -40964,28 +40305,20 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
         if (JS_IsException(val))
             return val;
         switch(JS_VALUE_GET_TAG(val)) {
+        case JS_TAG_SHORT_BIG_INT:
+            val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val));
+            if (JS_IsException(val))
+                return val;
+            break;
         case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-        case JS_TAG_BIG_FLOAT:
-#endif
             {
-                JSBigFloat *p = JS_VALUE_GET_PTR(val);
+                JSBigInt *p = JS_VALUE_GET_PTR(val);
                 double d;
-                bf_get_float64(&p->num, &d, BF_RNDN);
+                d = js_bigint_to_float64(ctx, p);
                 JS_FreeValue(ctx, val);
-                val = __JS_NewFloat64(ctx, d);
+                val = JS_NewFloat64(ctx, d);
             }
             break;
-#ifdef CONFIG_BIGNUM
-        case JS_TAG_BIG_DECIMAL:
-            val = JS_ToStringFree(ctx, val);
-            if (JS_IsException(val))
-                return val;
-            val = JS_ToNumberFree(ctx, val);
-            if (JS_IsException(val))
-                return val;
-            break;
-#endif
         default:
             break;
         }
@@ -45334,11 +44667,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
     /* check for object.toJSON method */
     /* ECMA specifies this is done only for Object and BigInt */
     /* we do it for BigFloat and BigDecimal as an extension */
-    if (JS_IsObject(val) || JS_IsBigInt(ctx, val)
-#ifdef CONFIG_BIGNUM
-    ||  JS_IsBigFloat(val) || JS_IsBigDecimal(val)
-#endif
-        ) {
+    if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) {
         JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
         if (JS_IsException(f))
             goto exception;
@@ -45372,11 +44701,8 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
     case JS_TAG_FLOAT64:
     case JS_TAG_BOOL:
     case JS_TAG_NULL:
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-    case JS_TAG_BIG_DECIMAL:
-#endif
     case JS_TAG_EXCEPTION:
         return val;
     default:
@@ -45424,12 +44750,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
             if (JS_IsException(val))
                 goto exception;
             goto concat_primitive;
-        } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT
-#ifdef CONFIG_BIGNUM
-               || cl == JS_CLASS_BIG_FLOAT
-               || cl == JS_CLASS_BIG_DECIMAL
-#endif
-                   )
+        } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT)
         {
             /* This will thow the same error as for the primitive object */
             set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
@@ -45564,11 +44885,8 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
     case JS_TAG_NULL:
     concat_value:
         return string_buffer_concat_value_free(jsc->b, val);
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-    case JS_TAG_BIG_DECIMAL:
-#endif
         /* reject big numbers: use toJSON method to override */
         JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
         goto exception;
@@ -49453,3151 +48771,1307 @@ static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
     for (i = 0, len = p->len; i < len; i++) {
         c = string_get(p, i);
         if (isUnescaped(c)) {
-            string_buffer_putc16(b, c);
-        } else {
-            encodeURI_hex(b, c);
-        }
-    }
-    JS_FreeValue(ctx, str);
-    return string_buffer_end(b);
-}
-
-static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
-                                  int argc, JSValueConst *argv)
-{
-    JSValue str;
-    StringBuffer b_s, *b = &b_s;
-    JSString *p;
-    int i, len, c, n;
-
-    str = JS_ToString(ctx, argv[0]);
-    if (JS_IsException(str))
-        return str;
-
-    string_buffer_init(ctx, b, 0);
-    p = JS_VALUE_GET_STRING(str);
-    for (i = 0, len = p->len; i < len; i++) {
-        c = string_get(p, i);
-        if (c == '%') {
-            if (i + 6 <= len
-            &&  string_get(p, i + 1) == 'u'
-            &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {
-                c = n;
-                i += 6 - 1;
-            } else
-            if (i + 3 <= len
-            &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {
-                c = n;
-                i += 3 - 1;
-            }
-        }
-        string_buffer_putc16(b, c);
-    }
-    JS_FreeValue(ctx, str);
-    return string_buffer_end(b);
-}
-
-/* global object */
-
-static const JSCFunctionListEntry js_global_funcs[] = {
-    JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
-    JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
-    JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
-    JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
-
-    JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
-    JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
-    JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
-    JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
-    JS_CFUNC_DEF("escape", 1, js_global_escape ),
-    JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
-    JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
-    JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
-    JS_PROP_UNDEFINED_DEF("undefined", 0 ),
-    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
-};
-
-/* Date */
-
-static int64_t math_mod(int64_t a, int64_t b) {
-    /* return positive modulo */
-    int64_t m = a % b;
-    return m + (m < 0) * b;
-}
-
-static int64_t floor_div(int64_t a, int64_t b) {
-    /* integer division rounding toward -Infinity */
-    int64_t m = a % b;
-    return (a - (m + (m < 0) * b)) / b;
-}
-
-static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
-                             int argc, JSValueConst *argv);
-
-static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
-{
-    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
-        JSObject *p = JS_VALUE_GET_OBJ(this_val);
-        if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
-            return JS_ToFloat64(ctx, valp, p->u.object_data);
-    }
-    JS_ThrowTypeError(ctx, "not a Date object");
-    return -1;
-}
-
-static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
-{
-    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
-        JSObject *p = JS_VALUE_GET_OBJ(this_val);
-        if (p->class_id == JS_CLASS_DATE) {
-            JS_FreeValue(ctx, p->u.object_data);
-            p->u.object_data = JS_NewFloat64(ctx, v);
-            return JS_DupValue(ctx, p->u.object_data);
-        }
-    }
-    return JS_ThrowTypeError(ctx, "not a Date object");
-}
-
-static int64_t days_from_year(int64_t y) {
-    return 365 * (y - 1970) + floor_div(y - 1969, 4) -
-        floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
-}
-
-static int64_t days_in_year(int64_t y) {
-    return 365 + !(y % 4) - !(y % 100) + !(y % 400);
-}
-
-/* return the year, update days */
-static int64_t year_from_days(int64_t *days) {
-    int64_t y, d1, nd, d = *days;
-    y = floor_div(d * 10000, 3652425) + 1970;
-    /* the initial approximation is very good, so only a few
-       iterations are necessary */
-    for(;;) {
-        d1 = d - days_from_year(y);
-        if (d1 < 0) {
-            y--;
-            d1 += days_in_year(y);
-        } else {
-            nd = days_in_year(y);
-            if (d1 < nd)
-                break;
-            d1 -= nd;
-            y++;
-        }
-    }
-    *days = d1;
-    return y;
-}
-
-static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-static char const day_names[] = "SunMonTueWedThuFriSat";
-
-static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
-                                       double fields[minimum_length(9)], int is_local, int force)
-{
-    double dval;
-    int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
-
-    if (JS_ThisTimeValue(ctx, &dval, obj))
-        return -1;
-
-    if (isnan(dval)) {
-        if (!force)
-            return FALSE; /* NaN */
-        d = 0;        /* initialize all fields to 0 */
-    } else {
-        d = dval;     /* assuming -8.64e15 <= dval <= -8.64e15 */
-        if (is_local) {
-            tz = -getTimezoneOffset(d);
-            d += tz * 60000;
-        }
-    }
-
-    /* result is >= 0, we can use % */
-    h = math_mod(d, 86400000);
-    days = (d - h) / 86400000;
-    ms = h % 1000;
-    h = (h - ms) / 1000;
-    s = h % 60;
-    h = (h - s) / 60;
-    m = h % 60;
-    h = (h - m) / 60;
-    wd = math_mod(days + 4, 7); /* week day */
-    y = year_from_days(&days);
-
-    for(i = 0; i < 11; i++) {
-        md = month_days[i];
-        if (i == 1)
-            md += days_in_year(y) - 365;
-        if (days < md)
-            break;
-        days -= md;
-    }
-    fields[0] = y;
-    fields[1] = i;
-    fields[2] = days + 1;
-    fields[3] = h;
-    fields[4] = m;
-    fields[5] = s;
-    fields[6] = ms;
-    fields[7] = wd;
-    fields[8] = tz;
-    return TRUE;
-}
-
-static double time_clip(double t) {
-    if (t >= -8.64e15 && t <= 8.64e15)
-        return trunc(t) + 0.0;  /* convert -0 to +0 */
-    else
-        return NAN;
-}
-
-/* The spec mandates the use of 'double' and it specifies the order
-   of the operations */
-static double set_date_fields(double fields[minimum_length(7)], int is_local) {
-    double y, m, dt, ym, mn, day, h, s, milli, time, tv;
-    int yi, mi, i;
-    int64_t days;
-    volatile double temp;  /* enforce evaluation order */
-
-    /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
-    y = fields[0];
-    m = fields[1];
-    dt = fields[2];
-    ym = y + floor(m / 12);
-    mn = fmod(m, 12);
-    if (mn < 0)
-        mn += 12;
-    if (ym < -271821 || ym > 275760)
-        return NAN;
-
-    yi = ym;
-    mi = mn;
-    days = days_from_year(yi);
-    for(i = 0; i < mi; i++) {
-        days += month_days[i];
-        if (i == 1)
-            days += days_in_year(yi) - 365;
-    }
-    day = days + dt - 1;
-
-    /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
-    h = fields[3];
-    m = fields[4];
-    s = fields[5];
-    milli = fields[6];
-    /* Use a volatile intermediary variable to ensure order of evaluation
-     * as specified in ECMA. This fixes a test262 error on
-     * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
-     * Without the volatile qualifier, the compile can generate code
-     * that performs the computation in a different order or with instructions
-     * that produce a different result such as FMA (float multiply and add).
-     */
-    time = h * 3600000;
-    time += (temp = m * 60000);
-    time += (temp = s * 1000);
-    time += milli;
-
-    /* emulate 21.4.1.16 MakeDate ( day, time ) */
-    tv = (temp = day * 86400000) + time;   /* prevent generation of FMA */
-    if (!isfinite(tv))
-        return NAN;
-
-    /* adjust for local time and clip */
-    if (is_local) {
-        int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
-        tv += getTimezoneOffset(ti) * 60000;
-    }
-    return time_clip(tv);
-}
-
-static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
-                              int argc, JSValueConst *argv, int magic)
-{
-    // get_date_field(obj, n, is_local)
-    double fields[9];
-    int res, n, is_local;
-
-    is_local = magic & 0x0F;
-    n = (magic >> 4) & 0x0F;
-    res = get_date_fields(ctx, this_val, fields, is_local, 0);
-    if (res < 0)
-        return JS_EXCEPTION;
-    if (!res)
-        return JS_NAN;
-
-    if (magic & 0x100) {    // getYear
-        fields[0] -= 1900;
-    }
-    return JS_NewFloat64(ctx, fields[n]);
-}
-
-static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
-                              int argc, JSValueConst *argv, int magic)
-{
-    // _field(obj, first_field, end_field, args, is_local)
-    double fields[9];
-    int res, first_field, end_field, is_local, i, n;
-    double d, a;
-
-    d = NAN;
-    first_field = (magic >> 8) & 0x0F;
-    end_field = (magic >> 4) & 0x0F;
-    is_local = magic & 0x0F;
-
-    res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
-    if (res < 0)
-        return JS_EXCEPTION;
-
-    // Argument coercion is observable and must be done unconditionally.
-    n = min_int(argc, end_field - first_field);
-    for(i = 0; i < n; i++) {
-        if (JS_ToFloat64(ctx, &a, argv[i]))
-            return JS_EXCEPTION;
-        if (!isfinite(a))
-            res = FALSE;
-        fields[first_field + i] = trunc(a);
-    }
-    if (res && argc > 0)
-        d = set_date_fields(fields, is_local);
-
-    return JS_SetThisTimeValue(ctx, this_val, d);
-}
-
-/* fmt:
-   0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
-   1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
-   2: toISOString: "2018-01-02T23:02:56.927Z"
-   3: toLocaleString: "1/2/2018, 11:40:40 PM"
-   part: 1=date, 2=time 3=all
-   XXX: should use a variant of strftime().
- */
-static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
-                               int argc, JSValueConst *argv, int magic)
-{
-    // _string(obj, fmt, part)
-    char buf[64];
-    double fields[9];
-    int res, fmt, part, pos;
-    int y, mon, d, h, m, s, ms, wd, tz;
-
-    fmt = (magic >> 4) & 0x0F;
-    part = magic & 0x0F;
-
-    res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
-    if (res < 0)
-        return JS_EXCEPTION;
-    if (!res) {
-        if (fmt == 2)
-            return JS_ThrowRangeError(ctx, "Date value is NaN");
-        else
-            return JS_NewString(ctx, "Invalid Date");
-    }
-
-    y = fields[0];
-    mon = fields[1];
-    d = fields[2];
-    h = fields[3];
-    m = fields[4];
-    s = fields[5];
-    ms = fields[6];
-    wd = fields[7];
-    tz = fields[8];
-
-    pos = 0;
-
-    if (part & 1) { /* date part */
-        switch(fmt) {
-        case 0:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%.3s, %02d %.3s %0*d ",
-                            day_names + wd * 3, d,
-                            month_names + mon * 3, 4 + (y < 0), y);
-            break;
-        case 1:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%.3s %.3s %02d %0*d",
-                            day_names + wd * 3,
-                            month_names + mon * 3, d, 4 + (y < 0), y);
-            if (part == 3) {
-                buf[pos++] = ' ';
-            }
-            break;
-        case 2:
-            if (y >= 0 && y <= 9999) {
-                pos += snprintf(buf + pos, sizeof(buf) - pos,
-                                "%04d", y);
-            } else {
-                pos += snprintf(buf + pos, sizeof(buf) - pos,
-                                "%+07d", y);
-            }
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "-%02d-%02dT", mon + 1, d);
-            break;
-        case 3:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
-            if (part == 3) {
-                buf[pos++] = ',';
-                buf[pos++] = ' ';
-            }
-            break;
-        }
-    }
-    if (part & 2) { /* time part */
-        switch(fmt) {
-        case 0:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d:%02d:%02d GMT", h, m, s);
-            break;
-        case 1:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d:%02d:%02d GMT", h, m, s);
-            if (tz < 0) {
-                buf[pos++] = '-';
-                tz = -tz;
-            } else {
-                buf[pos++] = '+';
-            }
-            /* tz is >= 0, can use % */
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d%02d", tz / 60, tz % 60);
-            /* XXX: tack the time zone code? */
-            break;
-        case 2:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d:%02d:%02d.%03dZ", h, m, s, ms);
-            break;
-        case 3:
-            pos += snprintf(buf + pos, sizeof(buf) - pos,
-                            "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
-                            (h < 12) ? 'A' : 'P');
-            break;
-        }
-    }
-    return JS_NewStringLen(ctx, buf, pos);
-}
-
-/* OS dependent: return the UTC time in ms since 1970. */
-static int64_t date_now(void) {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
-}
-
-static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
-                                   int argc, JSValueConst *argv)
-{
-    // Date(y, mon, d, h, m, s, ms)
-    JSValue rv;
-    int i, n;
-    double a, val;
-
-    if (JS_IsUndefined(new_target)) {
-        /* invoked as function */
-        argc = 0;
-    }
-    n = argc;
-    if (n == 0) {
-        val = date_now();
-    } else if (n == 1) {
-        JSValue v, dv;
-        if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
-            JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
-            if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
-                if (JS_ToFloat64(ctx, &val, p->u.object_data))
-                    return JS_EXCEPTION;
-                val = time_clip(val);
-                goto has_val;
-            }
-        }
-        v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
-        if (JS_IsString(v)) {
-            dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
-            JS_FreeValue(ctx, v);
-            if (JS_IsException(dv))
-                return JS_EXCEPTION;
-            if (JS_ToFloat64Free(ctx, &val, dv))
-                return JS_EXCEPTION;
-        } else {
-            if (JS_ToFloat64Free(ctx, &val, v))
-                return JS_EXCEPTION;
-        }
-        val = time_clip(val);
-    } else {
-        double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
-        if (n > 7)
-            n = 7;
-        for(i = 0; i < n; i++) {
-            if (JS_ToFloat64(ctx, &a, argv[i]))
-                return JS_EXCEPTION;
-            if (!isfinite(a))
-                break;
-            fields[i] = trunc(a);
-            if (i == 0 && fields[0] >= 0 && fields[0] < 100)
-                fields[0] += 1900;
-        }
-        val = (i == n) ? set_date_fields(fields, 1) : NAN;
-    }
-has_val:
-#if 0
-    JSValueConst args[3];
-    args[0] = new_target;
-    args[1] = ctx->class_proto[JS_CLASS_DATE];
-    args[2] = JS_NewFloat64(ctx, val);
-    rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
-#else
-    rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
-    if (!JS_IsException(rv))
-        JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
-#endif
-    if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
-        /* invoked as a function, return (new Date()).toString(); */
-        JSValue s;
-        s = get_date_string(ctx, rv, 0, NULL, 0x13);
-        JS_FreeValue(ctx, rv);
-        rv = s;
-    }
-    return rv;
-}
-
-static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
-                           int argc, JSValueConst *argv)
-{
-    // UTC(y, mon, d, h, m, s, ms)
-    double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
-    int i, n;
-    double a;
-
-    n = argc;
-    if (n == 0)
-        return JS_NAN;
-    if (n > 7)
-        n = 7;
-    for(i = 0; i < n; i++) {
-        if (JS_ToFloat64(ctx, &a, argv[i]))
-            return JS_EXCEPTION;
-        if (!isfinite(a))
-            return JS_NAN;
-        fields[i] = trunc(a);
-        if (i == 0 && fields[0] >= 0 && fields[0] < 100)
-            fields[0] += 1900;
-    }
-    return JS_NewFloat64(ctx, set_date_fields(fields, 0));
-}
-
-/* Date string parsing */
-
-static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
-    if (sp[*pp] == c) {
-        *pp += 1;
-        return TRUE;
-    } else {
-        return FALSE;
-    }
-}
-
-/* skip spaces, update offset, return next char */
-static int string_skip_spaces(const uint8_t *sp, int *pp) {
-    int c;
-    while ((c = sp[*pp]) == ' ')
-        *pp += 1;
-    return c;
-}
-
-/* skip dashes dots and commas */
-static int string_skip_separators(const uint8_t *sp, int *pp) {
-    int c;
-    while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
-        *pp += 1;
-    return c;
-}
-
-/* skip a word, stop on spaces, digits and separators, update offset */
-static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
-    int c;
-    while (!strchr(stoplist, c = sp[*pp]))
-        *pp += 1;
-    return c;
-}
-
-/* parse a numeric field (max_digits = 0 -> no maximum) */
-static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
-                              int min_digits, int max_digits)
-{
-    int v = 0;
-    int c, p = *pp, p_start;
-
-    p_start = p;
-    while ((c = sp[p]) >= '0' && c <= '9') {
-        /* arbitrary limit to 9 digits */
-        if (v >= 100000000)
-            return FALSE;
-        v = v * 10 + c - '0';
-        p++;
-        if (p - p_start == max_digits)
-            break;
-    }
-    if (p - p_start < min_digits)
-        return FALSE;
-    *pval = v;
-    *pp = p;
-    return TRUE;
-}
-
-static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
-    /* parse optional fractional part as milliseconds and truncate. */
-    /* spec does not indicate which rounding should be used */
-    int mul = 100, ms = 0, c, p_start, p = *pp;
-
-    c = sp[p];
-    if (c == '.' || c == ',') {
-        p++;
-        p_start = p;
-        while ((c = sp[p]) >= '0' && c <= '9') {
-            ms += (c - '0') * mul;
-            mul /= 10;
-            p++;
-            if (p - p_start == 9)
-                break;
-        }
-        if (p > p_start) {
-            /* only consume the separator if digits are present */
-            *pval = ms;
-            *pp = p;
-        }
-    }
-    return TRUE;
-}
-
-static uint8_t upper_ascii(uint8_t c) {
-    return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
-}
-
-static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
-    int tz = 0, sgn, hh, mm, p = *pp;
-
-    sgn = sp[p++];
-    if (sgn == '+' || sgn == '-') {
-        int n = p;
-        if (!string_get_digits(sp, &p, &hh, 1, 0))
-            return FALSE;
-        n = p - n;
-        if (strict && n != 2 && n != 4)
-            return FALSE;
-        while (n > 4) {
-            n -= 2;
-            hh /= 100;
-        }
-        if (n > 2) {
-            mm = hh % 100;
-            hh = hh / 100;
-        } else {
-            mm = 0;
-            if (string_skip_char(sp, &p, ':')  /* optional separator */
-            &&  !string_get_digits(sp, &p, &mm, 2, 2))
-                return FALSE;
-        }
-        if (hh > 23 || mm > 59)
-            return FALSE;
-        tz = hh * 60 + mm;
-        if (sgn != '+')
-            tz = -tz;
-    } else
-    if (sgn != 'Z') {
-        return FALSE;
-    }
-    *pp = p;
-    *tzp = tz;
-    return TRUE;
-}
-
-static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
-    int p = *pp;
-    while (*s != '\0') {
-        if (upper_ascii(sp[p]) != upper_ascii(*s++))
-            return FALSE;
-        p++;
-    }
-    *pp = p;
-    return TRUE;
-}
-
-static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
-    int n, i;
-
-    for (n = 0; n < count; n++) {
-        for (i = 0;; i++) {
-            if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
-                break;
-            if (i == 2)
-                return n;
-        }
-    }
-    return -1;
-}
-
-static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
-    int n;
-
-    n = find_abbrev(sp, *pp, month_names, 12);
-    if (n < 0)
-        return FALSE;
-
-    *pval = n + 1;
-    *pp += 3;
-    return TRUE;
-}
-
-/* parse toISOString format */
-static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
-    int sgn, i, p = 0;
-
-    /* initialize fields to the beginning of the Epoch */
-    for (i = 0; i < 9; i++) {
-        fields[i] = (i == 2);
-    }
-    *is_local = FALSE;
-
-    /* year is either yyyy digits or [+-]yyyyyy */
-    sgn = sp[p];
-    if (sgn == '-' || sgn == '+') {
-        p++;
-        if (!string_get_digits(sp, &p, &fields[0], 6, 6))
-            return FALSE;
-        if (sgn == '-') {
-            if (fields[0] == 0)
-                return FALSE; // reject -000000
-            fields[0] = -fields[0];
-        }
-    } else {
-        if (!string_get_digits(sp, &p, &fields[0], 4, 4))
-            return FALSE;
-    }
-    if (string_skip_char(sp, &p, '-')) {
-        if (!string_get_digits(sp, &p, &fields[1], 2, 2))  /* month */
-            return FALSE;
-        if (fields[1] < 1)
-            return FALSE;
-        fields[1] -= 1;
-        if (string_skip_char(sp, &p, '-')) {
-            if (!string_get_digits(sp, &p, &fields[2], 2, 2))  /* day */
-                return FALSE;
-            if (fields[2] < 1)
-                return FALSE;
-        }
-    }
-    if (string_skip_char(sp, &p, 'T')) {
-        *is_local = TRUE;
-        if (!string_get_digits(sp, &p, &fields[3], 2, 2)  /* hour */
-        ||  !string_skip_char(sp, &p, ':')
-        ||  !string_get_digits(sp, &p, &fields[4], 2, 2)) {  /* minute */
-            fields[3] = 100;  // reject unconditionally
-            return TRUE;
-        }
-        if (string_skip_char(sp, &p, ':')) {
-            if (!string_get_digits(sp, &p, &fields[5], 2, 2))  /* second */
-                return FALSE;
-            string_get_milliseconds(sp, &p, &fields[6]);
-        }
-    }
-    /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
-    if (sp[p]) {
-        *is_local = FALSE;
-        if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
-            return FALSE;
-    }
-    /* error if extraneous characters */
-    return sp[p] == '\0';
-}
-
-static struct {
-    char name[6];
-    int16_t offset;
-} const js_tzabbr[] = {
-    { "GMT",   0 },         // Greenwich Mean Time
-    { "UTC",   0 },         // Coordinated Universal Time
-    { "UT",    0 },         // Universal Time
-    { "Z",     0 },         // Zulu Time
-    { "EDT",  -4 * 60 },    // Eastern Daylight Time
-    { "EST",  -5 * 60 },    // Eastern Standard Time
-    { "CDT",  -5 * 60 },    // Central Daylight Time
-    { "CST",  -6 * 60 },    // Central Standard Time
-    { "MDT",  -6 * 60 },    // Mountain Daylight Time
-    { "MST",  -7 * 60 },    // Mountain Standard Time
-    { "PDT",  -7 * 60 },    // Pacific Daylight Time
-    { "PST",  -8 * 60 },    // Pacific Standard Time
-    { "WET",  +0 * 60 },    // Western European Time
-    { "WEST", +1 * 60 },    // Western European Summer Time
-    { "CET",  +1 * 60 },    // Central European Time
-    { "CEST", +2 * 60 },    // Central European Summer Time
-    { "EET",  +2 * 60 },    // Eastern European Time
-    { "EEST", +3 * 60 },    // Eastern European Summer Time
-};
-
-static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
-    for (size_t i = 0; i < countof(js_tzabbr); i++) {
-        if (string_match(sp, pp, js_tzabbr[i].name)) {
-            *offset = js_tzabbr[i].offset;
-            return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-/* parse toString, toUTCString and other formats */
-static BOOL js_date_parse_otherstring(const uint8_t *sp,
-                                      int fields[minimum_length(9)],
-                                      BOOL *is_local) {
-    int c, i, val, p = 0, p_start;
-    int num[3];
-    BOOL has_year = FALSE;
-    BOOL has_mon = FALSE;
-    BOOL has_time = FALSE;
-    int num_index = 0;
-
-    /* initialize fields to the beginning of 2001-01-01 */
-    fields[0] = 2001;
-    fields[1] = 1;
-    fields[2] = 1;
-    for (i = 3; i < 9; i++) {
-        fields[i] = 0;
-    }
-    *is_local = TRUE;
-
-    while (string_skip_spaces(sp, &p)) {
-        p_start = p;
-        if ((c = sp[p]) == '+' || c == '-') {
-            if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
-                *is_local = FALSE;
-            } else {
-                p++;
-                if (string_get_digits(sp, &p, &val, 1, 0)) {
-                    if (c == '-') {
-                        if (val == 0)
-                            return FALSE;
-                        val = -val;
-                    }
-                    fields[0] = val;
-                    has_year = TRUE;
-                }
-            }
-        } else
-        if (string_get_digits(sp, &p, &val, 1, 0)) {
-            if (string_skip_char(sp, &p, ':')) {
-                /* time part */
-                fields[3] = val;
-                if (!string_get_digits(sp, &p, &fields[4], 1, 2))
-                    return FALSE;
-                if (string_skip_char(sp, &p, ':')) {
-                    if (!string_get_digits(sp, &p, &fields[5], 1, 2))
-                        return FALSE;
-                    string_get_milliseconds(sp, &p, &fields[6]);
-                }
-                has_time = TRUE;
-            } else {
-                if (p - p_start > 2) {
-                    fields[0] = val;
-                    has_year = TRUE;
-                } else
-                if (val < 1 || val > 31) {
-                    fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
-                    has_year = TRUE;
-                } else {
-                    if (num_index == 3)
-                        return FALSE;
-                    num[num_index++] = val;
-                }
-            }
-        } else
-        if (string_get_month(sp, &p, &fields[1])) {
-            has_mon = TRUE;
-            string_skip_until(sp, &p, "0123456789 -/(");
-        } else
-        if (has_time && string_match(sp, &p, "PM")) {
-            if (fields[3] < 12)
-                fields[3] += 12;
-            continue;
-        } else
-        if (has_time && string_match(sp, &p, "AM")) {
-            if (fields[3] == 12)
-                fields[3] -= 12;
-            continue;
-        } else
-        if (string_get_tzabbr(sp, &p, &fields[8])) {
-            *is_local = FALSE;
-            continue;
-        } else
-        if (c == '(') {  /* skip parenthesized phrase */
-            int level = 0;
-            while ((c = sp[p]) != '\0') {
-                p++;
-                level += (c == '(');
-                level -= (c == ')');
-                if (!level)
-                    break;
-            }
-            if (level > 0)
-                return FALSE;
-        } else
-        if (c == ')') {
-            return FALSE;
-        } else {
-            if (has_year + has_mon + has_time + num_index)
-                return FALSE;
-            /* skip a word */
-            string_skip_until(sp, &p, " -/(");
-        }
-        string_skip_separators(sp, &p);
-    }
-    if (num_index + has_year + has_mon > 3)
-        return FALSE;
-
-    switch (num_index) {
-    case 0:
-        if (!has_year)
-            return FALSE;
-        break;
-    case 1:
-        if (has_mon)
-            fields[2] = num[0];
-        else
-            fields[1] = num[0];
-        break;
-    case 2:
-        if (has_year) {
-            fields[1] = num[0];
-            fields[2] = num[1];
-        } else
-        if (has_mon) {
-            fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
-            fields[2] = num[0];
-        } else {
-            fields[1] = num[0];
-            fields[2] = num[1];
-        }
-        break;
-    case 3:
-        fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
-        fields[1] = num[0];
-        fields[2] = num[1];
-        break;
-    default:
-        return FALSE;
-    }
-    if (fields[1] < 1 || fields[2] < 1)
-        return FALSE;
-    fields[1] -= 1;
-    return TRUE;
-}
-
-static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
-                             int argc, JSValueConst *argv)
-{
-    JSValue s, rv;
-    int fields[9];
-    double fields1[9];
-    double d;
-    int i, c;
-    JSString *sp;
-    uint8_t buf[128];
-    BOOL is_local;
-
-    rv = JS_NAN;
-
-    s = JS_ToString(ctx, argv[0]);
-    if (JS_IsException(s))
-        return JS_EXCEPTION;
-
-    sp = JS_VALUE_GET_STRING(s);
-    /* convert the string as a byte array */
-    for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
-        c = string_get(sp, i);
-        if (c > 255)
-            c = (c == 0x2212) ? '-' : 'x';
-        buf[i] = c;
-    }
-    buf[i] = '\0';
-    if (js_date_parse_isostring(buf, fields, &is_local)
-    ||  js_date_parse_otherstring(buf, fields, &is_local)) {
-        static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
-        BOOL valid = TRUE;
-        /* check field maximum values */
-        for (i = 1; i < 6; i++) {
-            if (fields[i] > field_max[i])
-                valid = FALSE;
-        }
-        /* special case 24:00:00.000 */
-        if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
-            valid = FALSE;
-        if (valid) {
-            for(i = 0; i < 7; i++)
-                fields1[i] = fields[i];
-            d = set_date_fields(fields1, is_local) - fields[8] * 60000;
-            rv = JS_NewFloat64(ctx, d);
+            string_buffer_putc16(b, c);
+        } else {
+            encodeURI_hex(b, c);
         }
     }
-    JS_FreeValue(ctx, s);
-    return rv;
-}
-
-static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
-                           int argc, JSValueConst *argv)
-{
-    // now()
-    return JS_NewInt64(ctx, date_now());
+    JS_FreeValue(ctx, str);
+    return string_buffer_end(b);
 }
 
-static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
-                                          int argc, JSValueConst *argv)
+static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
+                                  int argc, JSValueConst *argv)
 {
-    // Symbol_toPrimitive(hint)
-    JSValueConst obj = this_val;
-    JSAtom hint = JS_ATOM_NULL;
-    int hint_num;
+    JSValue str;
+    StringBuffer b_s, *b = &b_s;
+    JSString *p;
+    int i, len, c, n;
 
-    if (!JS_IsObject(obj))
-        return JS_ThrowTypeErrorNotAnObject(ctx);
+    str = JS_ToString(ctx, argv[0]);
+    if (JS_IsException(str))
+        return str;
 
-    if (JS_IsString(argv[0])) {
-        hint = JS_ValueToAtom(ctx, argv[0]);
-        if (hint == JS_ATOM_NULL)
-            return JS_EXCEPTION;
-        JS_FreeAtom(ctx, hint);
-    }
-    switch (hint) {
-    case JS_ATOM_number:
-    case JS_ATOM_integer:
-        hint_num = HINT_NUMBER;
-        break;
-    case JS_ATOM_string:
-    case JS_ATOM_default:
-        hint_num = HINT_STRING;
-        break;
-    default:
-        return JS_ThrowTypeError(ctx, "invalid hint");
+    string_buffer_init(ctx, b, 0);
+    p = JS_VALUE_GET_STRING(str);
+    for (i = 0, len = p->len; i < len; i++) {
+        c = string_get(p, i);
+        if (c == '%') {
+            if (i + 6 <= len
+            &&  string_get(p, i + 1) == 'u'
+            &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {
+                c = n;
+                i += 6 - 1;
+            } else
+            if (i + 3 <= len
+            &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {
+                c = n;
+                i += 3 - 1;
+            }
+        }
+        string_buffer_putc16(b, c);
     }
-    return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
+    JS_FreeValue(ctx, str);
+    return string_buffer_end(b);
 }
 
-static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
-                                         int argc, JSValueConst *argv)
-{
-    // getTimezoneOffset()
-    double v;
+/* global object */
 
-    if (JS_ThisTimeValue(ctx, &v, this_val))
-        return JS_EXCEPTION;
-    if (isnan(v))
-        return JS_NAN;
-    else
-        /* assuming -8.64e15 <= v <= -8.64e15 */
-        return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
-}
+static const JSCFunctionListEntry js_global_funcs[] = {
+    JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
+    JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
+    JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
+    JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
 
-static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
-                               int argc, JSValueConst *argv)
-{
-    // getTime()
-    double v;
+    JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
+    JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
+    JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
+    JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
+    JS_CFUNC_DEF("escape", 1, js_global_escape ),
+    JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
+    JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
+    JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+    JS_PROP_UNDEFINED_DEF("undefined", 0 ),
+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
+};
 
-    if (JS_ThisTimeValue(ctx, &v, this_val))
-        return JS_EXCEPTION;
-    return JS_NewFloat64(ctx, v);
-}
+/* Date */
 
-static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
-                               int argc, JSValueConst *argv)
-{
-    // setTime(v)
-    double v;
+static int64_t math_mod(int64_t a, int64_t b) {
+    /* return positive modulo */
+    int64_t m = a % b;
+    return m + (m < 0) * b;
+}
 
-    if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
-        return JS_EXCEPTION;
-    return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
+static int64_t floor_div(int64_t a, int64_t b) {
+    /* integer division rounding toward -Infinity */
+    int64_t m = a % b;
+    return (a - (m + (m < 0) * b)) / b;
 }
 
-static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
-                               int argc, JSValueConst *argv)
-{
-    // setYear(y)
-    double y;
-    JSValueConst args[1];
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+                             int argc, JSValueConst *argv);
 
-    if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
-        return JS_EXCEPTION;
-    y = +y;
-    if (isfinite(y)) {
-        y = trunc(y);
-        if (y >= 0 && y < 100)
-            y += 1900;
+static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+{
+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+        JSObject *p = JS_VALUE_GET_OBJ(this_val);
+        if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
+            return JS_ToFloat64(ctx, valp, p->u.object_data);
     }
-    args[0] = JS_NewFloat64(ctx, y);
-    return set_date_field(ctx, this_val, 1, args, 0x011);
+    JS_ThrowTypeError(ctx, "not a Date object");
+    return -1;
 }
 
-static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
-                              int argc, JSValueConst *argv)
+static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
 {
-    // toJSON(key)
-    JSValue obj, tv, method, rv;
-    double d;
-
-    rv = JS_EXCEPTION;
-    tv = JS_UNDEFINED;
-
-    obj = JS_ToObject(ctx, this_val);
-    tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
-    if (JS_IsException(tv))
-        goto exception;
-    if (JS_IsNumber(tv)) {
-        if (JS_ToFloat64(ctx, &d, tv) < 0)
-            goto exception;
-        if (!isfinite(d)) {
-            rv = JS_NULL;
-            goto done;
+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+        JSObject *p = JS_VALUE_GET_OBJ(this_val);
+        if (p->class_id == JS_CLASS_DATE) {
+            JS_FreeValue(ctx, p->u.object_data);
+            p->u.object_data = JS_NewFloat64(ctx, v);
+            return JS_DupValue(ctx, p->u.object_data);
         }
     }
-    method = JS_GetPropertyStr(ctx, obj, "toISOString");
-    if (JS_IsException(method))
-        goto exception;
-    if (!JS_IsFunction(ctx, method)) {
-        JS_ThrowTypeError(ctx, "object needs toISOString method");
-        JS_FreeValue(ctx, method);
-        goto exception;
-    }
-    rv = JS_CallFree(ctx, method, obj, 0, NULL);
-exception:
-done:
-    JS_FreeValue(ctx, obj);
-    JS_FreeValue(ctx, tv);
-    return rv;
+    return JS_ThrowTypeError(ctx, "not a Date object");
 }
 
-static const JSCFunctionListEntry js_date_funcs[] = {
-    JS_CFUNC_DEF("now", 0, js_Date_now ),
-    JS_CFUNC_DEF("parse", 1, js_Date_parse ),
-    JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
-};
-
-static const JSCFunctionListEntry js_date_proto_funcs[] = {
-    JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
-    JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
-    JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
-    JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
-    JS_ALIAS_DEF("toGMTString", "toUTCString" ),
-    JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
-    JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
-    JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
-    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
-    JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
-    JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
-    JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
-    JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
-    JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
-    JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
-    JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
-    JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
-    JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
-    JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
-    JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
-    JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
-    JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
-    JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
-    JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
-    JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
-    JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
-    JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
-    JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
-    JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
-    JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
-    JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
-    JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
-    JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
-    JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
-    JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
-    JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
-    JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
-    JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
-    JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
-    JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
-    JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
-    JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
-    JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
-    JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
-    JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
-    JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
-    JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
-};
-
-JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
-{
-    JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
-    if (JS_IsException(obj))
-        return JS_EXCEPTION;
-    JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
-    return obj;
+static int64_t days_from_year(int64_t y) {
+    return 365 * (y - 1970) + floor_div(y - 1969, 4) -
+        floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
 }
 
-void JS_AddIntrinsicDate(JSContext *ctx)
-{
-    JSValueConst obj;
-
-    /* Date */
-    ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
-    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
-                               countof(js_date_proto_funcs));
-    obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
-                                   ctx->class_proto[JS_CLASS_DATE]);
-    JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
+static int64_t days_in_year(int64_t y) {
+    return 365 + !(y % 4) - !(y % 100) + !(y % 400);
 }
 
-/* eval */
-
-void JS_AddIntrinsicEval(JSContext *ctx)
-{
-    ctx->eval_internal = __JS_EvalInternal;
+/* return the year, update days */
+static int64_t year_from_days(int64_t *days) {
+    int64_t y, d1, nd, d = *days;
+    y = floor_div(d * 10000, 3652425) + 1970;
+    /* the initial approximation is very good, so only a few
+       iterations are necessary */
+    for(;;) {
+        d1 = d - days_from_year(y);
+        if (d1 < 0) {
+            y--;
+            d1 += days_in_year(y);
+        } else {
+            nd = days_in_year(y);
+            if (d1 < nd)
+                break;
+            d1 -= nd;
+            y++;
+        }
+    }
+    *days = d1;
+    return y;
 }
 
-#ifdef CONFIG_BIGNUM
-
-/* Operators */
+static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static char const day_names[] = "SunMonTueWedThuFriSat";
 
-static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
+static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
+                                       double fields[minimum_length(9)], int is_local, int force)
 {
-    JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
-    int i, j;
-    JSBinaryOperatorDefEntry *ent;
+    double dval;
+    int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
 
-    if (opset) {
-        for(i = 0; i < JS_OVOP_COUNT; i++) {
-            if (opset->self_ops[i])
-                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
-        }
-        for(j = 0; j < opset->left.count; j++) {
-            ent = &opset->left.tab[j];
-            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
-                if (ent->ops[i])
-                    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
-            }
-        }
-        js_free_rt(rt, opset->left.tab);
-        for(j = 0; j < opset->right.count; j++) {
-            ent = &opset->right.tab[j];
-            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
-                if (ent->ops[i])
-                    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
-            }
+    if (JS_ThisTimeValue(ctx, &dval, obj))
+        return -1;
+
+    if (isnan(dval)) {
+        if (!force)
+            return FALSE; /* NaN */
+        d = 0;        /* initialize all fields to 0 */
+    } else {
+        d = dval;     /* assuming -8.64e15 <= dval <= -8.64e15 */
+        if (is_local) {
+            tz = -getTimezoneOffset(d);
+            d += tz * 60000;
         }
-        js_free_rt(rt, opset->right.tab);
-        js_free_rt(rt, opset);
     }
-}
 
-static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
-                                 JS_MarkFunc *mark_func)
-{
-    JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
-    int i, j;
-    JSBinaryOperatorDefEntry *ent;
+    /* result is >= 0, we can use % */
+    h = math_mod(d, 86400000);
+    days = (d - h) / 86400000;
+    ms = h % 1000;
+    h = (h - ms) / 1000;
+    s = h % 60;
+    h = (h - s) / 60;
+    m = h % 60;
+    h = (h - m) / 60;
+    wd = math_mod(days + 4, 7); /* week day */
+    y = year_from_days(&days);
 
-    if (opset) {
-        for(i = 0; i < JS_OVOP_COUNT; i++) {
-            if (opset->self_ops[i])
-                JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
-                             mark_func);
-        }
-        for(j = 0; j < opset->left.count; j++) {
-            ent = &opset->left.tab[j];
-            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
-                if (ent->ops[i])
-                    JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
-                                 mark_func);
-            }
-        }
-        for(j = 0; j < opset->right.count; j++) {
-            ent = &opset->right.tab[j];
-            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
-                if (ent->ops[i])
-                    JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
-                                 mark_func);
-            }
-        }
+    for(i = 0; i < 11; i++) {
+        md = month_days[i];
+        if (i == 1)
+            md += days_in_year(y) - 365;
+        if (days < md)
+            break;
+        days -= md;
     }
+    fields[0] = y;
+    fields[1] = i;
+    fields[2] = days + 1;
+    fields[3] = h;
+    fields[4] = m;
+    fields[5] = s;
+    fields[6] = ms;
+    fields[7] = wd;
+    fields[8] = tz;
+    return TRUE;
 }
 
+static double time_clip(double t) {
+    if (t >= -8.64e15 && t <= 8.64e15)
+        return trunc(t) + 0.0;  /* convert -0 to +0 */
+    else
+        return NAN;
+}
 
-/* create an OperatorSet object */
-static JSValue js_operators_create_internal(JSContext *ctx,
-                                            int argc, JSValueConst *argv,
-                                            BOOL is_primitive)
-{
-    JSValue opset_obj, prop, obj;
-    JSOperatorSetData *opset, *opset1;
-    JSBinaryOperatorDef *def;
-    JSValueConst arg;
-    int i, j;
-    JSBinaryOperatorDefEntry *new_tab;
-    JSBinaryOperatorDefEntry *ent;
-    uint32_t op_count;
+/* The spec mandates the use of 'double' and it specifies the order
+   of the operations */
+static double set_date_fields(double fields[minimum_length(7)], int is_local) {
+    double y, m, dt, ym, mn, day, h, s, milli, time, tv;
+    int yi, mi, i;
+    int64_t days;
+    volatile double temp;  /* enforce evaluation order */
 
-    if (ctx->rt->operator_count == UINT32_MAX) {
-        return JS_ThrowTypeError(ctx, "too many operators");
-    }
-    opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
-    if (JS_IsException(opset_obj))
-        goto fail;
-    opset = js_mallocz(ctx, sizeof(*opset));
-    if (!opset)
-        goto fail;
-    JS_SetOpaque(opset_obj, opset);
-    if (argc >= 1) {
-        arg = argv[0];
-        /* self operators */
-        for(i = 0; i < JS_OVOP_COUNT; i++) {
-            prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
-            if (JS_IsException(prop))
-                goto fail;
-            if (!JS_IsUndefined(prop)) {
-                if (check_function(ctx, prop)) {
-                    JS_FreeValue(ctx, prop);
-                    goto fail;
-                }
-                opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
-            }
-        }
+    /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
+    y = fields[0];
+    m = fields[1];
+    dt = fields[2];
+    ym = y + floor(m / 12);
+    mn = fmod(m, 12);
+    if (mn < 0)
+        mn += 12;
+    if (ym < -271821 || ym > 275760)
+        return NAN;
+
+    yi = ym;
+    mi = mn;
+    days = days_from_year(yi);
+    for(i = 0; i < mi; i++) {
+        days += month_days[i];
+        if (i == 1)
+            days += days_in_year(yi) - 365;
     }
-    /* left & right operators */
-    for(j = 1; j < argc; j++) {
-        arg = argv[j];
-        prop = JS_GetPropertyStr(ctx, arg, "left");
-        if (JS_IsException(prop))
-            goto fail;
-        def = &opset->right;
-        if (JS_IsUndefined(prop)) {
-            prop = JS_GetPropertyStr(ctx, arg, "right");
-            if (JS_IsException(prop))
-                goto fail;
-            if (JS_IsUndefined(prop)) {
-                JS_ThrowTypeError(ctx, "left or right property must be present");
-                goto fail;
-            }
-            def = &opset->left;
-        }
-        /* get the operator set */
-        obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
-        JS_FreeValue(ctx, prop);
-        if (JS_IsException(obj))
-            goto fail;
-        prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
-        JS_FreeValue(ctx, obj);
-        if (JS_IsException(prop))
-            goto fail;
-        opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
-        if (!opset1) {
-            JS_FreeValue(ctx, prop);
-            goto fail;
-        }
-        op_count = opset1->operator_counter;
-        JS_FreeValue(ctx, prop);
+    day = days + dt - 1;
 
-        /* we assume there are few entries */
-        new_tab = js_realloc(ctx, def->tab,
-                             (def->count + 1) * sizeof(def->tab[0]));
-        if (!new_tab)
-            goto fail;
-        def->tab = new_tab;
-        def->count++;
-        ent = def->tab + def->count - 1;
-        memset(ent, 0, sizeof(def->tab[0]));
-        ent->operator_index = op_count;
-
-        for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
-            prop = JS_GetPropertyStr(ctx, arg,
-                                     js_overloadable_operator_names[i]);
-            if (JS_IsException(prop))
-                goto fail;
-            if (!JS_IsUndefined(prop)) {
-                if (check_function(ctx, prop)) {
-                    JS_FreeValue(ctx, prop);
-                    goto fail;
-                }
-                ent->ops[i] = JS_VALUE_GET_OBJ(prop);
-            }
-        }
+    /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
+    h = fields[3];
+    m = fields[4];
+    s = fields[5];
+    milli = fields[6];
+    /* Use a volatile intermediary variable to ensure order of evaluation
+     * as specified in ECMA. This fixes a test262 error on
+     * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
+     * Without the volatile qualifier, the compile can generate code
+     * that performs the computation in a different order or with instructions
+     * that produce a different result such as FMA (float multiply and add).
+     */
+    time = h * 3600000;
+    time += (temp = m * 60000);
+    time += (temp = s * 1000);
+    time += milli;
+
+    /* emulate 21.4.1.16 MakeDate ( day, time ) */
+    tv = (temp = day * 86400000) + time;   /* prevent generation of FMA */
+    if (!isfinite(tv))
+        return NAN;
+
+    /* adjust for local time and clip */
+    if (is_local) {
+        int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
+        tv += getTimezoneOffset(ti) * 60000;
     }
-    opset->is_primitive = is_primitive;
-    opset->operator_counter = ctx->rt->operator_count++;
-    return opset_obj;
- fail:
-    JS_FreeValue(ctx, opset_obj);
-    return JS_EXCEPTION;
+    return time_clip(tv);
 }
 
-static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
-                                int argc, JSValueConst *argv)
+static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
+                              int argc, JSValueConst *argv, int magic)
 {
-    return js_operators_create_internal(ctx, argc, argv, FALSE);
+    // get_date_field(obj, n, is_local)
+    double fields[9];
+    int res, n, is_local;
+
+    is_local = magic & 0x0F;
+    n = (magic >> 4) & 0x0F;
+    res = get_date_fields(ctx, this_val, fields, is_local, 0);
+    if (res < 0)
+        return JS_EXCEPTION;
+    if (!res)
+        return JS_NAN;
+
+    if (magic & 0x100) {    // getYear
+        fields[0] -= 1900;
+    }
+    return JS_NewFloat64(ctx, fields[n]);
 }
 
-static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
-                                                  int argc, JSValueConst *argv)
+static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
+                              int argc, JSValueConst *argv, int magic)
 {
-    JSValue opset_obj, prop;
-    JSOperatorSetData *opset;
-    const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
-    JSOverloadableOperatorEnum op;
-    int i;
+    // _field(obj, first_field, end_field, args, is_local)
+    double fields[9];
+    int res, first_field, end_field, is_local, i, n;
+    double d, a;
 
-    opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
-                               JS_ATOM_Symbol_operatorSet);
-    if (JS_IsException(opset_obj))
-        goto fail;
-    opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
-    if (!opset)
-        goto fail;
-    for(i = 0; i < countof(ops); i++) {
-        op = ops[i];
-        prop = JS_GetPropertyStr(ctx, argv[0],
-                                 js_overloadable_operator_names[op]);
-        if (JS_IsException(prop))
-            goto fail;
-        if (!JS_IsUndefined(prop)) {
-            if (!JS_IsNull(prop) && check_function(ctx, prop)) {
-                JS_FreeValue(ctx, prop);
-                goto fail;
-            }
-            if (opset->self_ops[op])
-                JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
-            if (JS_IsNull(prop)) {
-                opset->self_ops[op] = NULL;
-            } else {
-                opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
-            }
-        }
+    d = NAN;
+    first_field = (magic >> 8) & 0x0F;
+    end_field = (magic >> 4) & 0x0F;
+    is_local = magic & 0x0F;
+
+    res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
+    if (res < 0)
+        return JS_EXCEPTION;
+
+    // Argument coercion is observable and must be done unconditionally.
+    n = min_int(argc, end_field - first_field);
+    for(i = 0; i < n; i++) {
+        if (JS_ToFloat64(ctx, &a, argv[i]))
+            return JS_EXCEPTION;
+        if (!isfinite(a))
+            res = FALSE;
+        fields[first_field + i] = trunc(a);
     }
-    JS_FreeValue(ctx, opset_obj);
-    return JS_UNDEFINED;
- fail:
-    JS_FreeValue(ctx, opset_obj);
-    return JS_EXCEPTION;
-}
-
-static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
-{
-    JSValue opset_obj;
+    if (res && argc > 0)
+        d = set_date_fields(fields, is_local);
 
-    if (!JS_IsObject(obj)) /* in case the prototype is not defined */
-        return 0;
-    opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
-    if (JS_IsException(opset_obj))
-        return -1;
-    /* cannot be modified by the user */
-    JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
-                           opset_obj, 0);
-    return 0;
+    return JS_SetThisTimeValue(ctx, this_val, d);
 }
 
-static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
-                                       int argc, JSValueConst *argv)
+/* fmt:
+   0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
+   1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
+   2: toISOString: "2018-01-02T23:02:56.927Z"
+   3: toLocaleString: "1/2/2018, 11:40:40 PM"
+   part: 1=date, 2=time 3=all
+   XXX: should use a variant of strftime().
+ */
+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+                               int argc, JSValueConst *argv, int magic)
 {
-    return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
-}
+    // _string(obj, fmt, part)
+    char buf[64];
+    double fields[9];
+    int res, fmt, part, pos;
+    int y, mon, d, h, m, s, ms, wd, tz;
 
-static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
-                                   int argc, JSValueConst *argv)
-{
-    JSValue func_obj, proto, opset_obj;
+    fmt = (magic >> 4) & 0x0F;
+    part = magic & 0x0F;
 
-    func_obj = JS_UNDEFINED;
-    proto = JS_NewObject(ctx);
-    if (JS_IsException(proto))
+    res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
+    if (res < 0)
         return JS_EXCEPTION;
-    opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
-    if (JS_IsException(opset_obj))
-        goto fail;
-    JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
-                           opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
-    func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
-                                0, JS_CFUNC_constructor, 0);
-    if (JS_IsException(func_obj))
-        goto fail;
-    JS_SetConstructor2(ctx, func_obj, proto,
-                       0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
-    JS_FreeValue(ctx, proto);
-    return func_obj;
- fail:
-    JS_FreeValue(ctx, proto);
-    JS_FreeValue(ctx, func_obj);
-    return JS_EXCEPTION;
-}
-
-static const JSCFunctionListEntry js_operators_funcs[] = {
-    JS_CFUNC_DEF("create", 1, js_operators_create ),
-    JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
-};
-
-/* must be called after all overloadable base types are initialized */
-void JS_AddIntrinsicOperators(JSContext *ctx)
-{
-    JSValue obj;
-
-    ctx->allow_operator_overloading = TRUE;
-    obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
-    JS_SetPropertyFunctionList(ctx, obj,
-                               js_operators_funcs,
-                               countof(js_operators_funcs));
-    JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
-                           obj,
-                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
-    /* add default operatorSets */
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
-    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
-}
-#endif /* CONFIG_BIGNUM */
-
-/* BigInt */
+    if (!res) {
+        if (fmt == 2)
+            return JS_ThrowRangeError(ctx, "Date value is NaN");
+        else
+            return JS_NewString(ctx, "Invalid Date");
+    }
 
-static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
-{
-    uint32_t tag;
+    y = fields[0];
+    mon = fields[1];
+    d = fields[2];
+    h = fields[3];
+    m = fields[4];
+    s = fields[5];
+    ms = fields[6];
+    wd = fields[7];
+    tz = fields[8];
 
- redo:
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-    case JS_TAG_BOOL:
-        val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
-        break;
-    case JS_TAG_BIG_INT:
-        break;
-    case JS_TAG_FLOAT64:
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_FLOAT:
-#endif
-        {
-            bf_t *a, a_s;
+    pos = 0;
 
-            a = JS_ToBigFloat(ctx, &a_s, val);
-            if (!a) {
-                JS_FreeValue(ctx, val);
-                return JS_EXCEPTION;
+    if (part & 1) { /* date part */
+        switch(fmt) {
+        case 0:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%.3s, %02d %.3s %0*d ",
+                            day_names + wd * 3, d,
+                            month_names + mon * 3, 4 + (y < 0), y);
+            break;
+        case 1:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%.3s %.3s %02d %0*d",
+                            day_names + wd * 3,
+                            month_names + mon * 3, d, 4 + (y < 0), y);
+            if (part == 3) {
+                buf[pos++] = ' ';
             }
-            if (!bf_is_finite(a)) {
-                JS_FreeValue(ctx, val);
-                val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt");
+            break;
+        case 2:
+            if (y >= 0 && y <= 9999) {
+                pos += snprintf(buf + pos, sizeof(buf) - pos,
+                                "%04d", y);
             } else {
-                JSValue val1 = JS_NewBigInt(ctx);
-                bf_t *r;
-                int ret;
-                if (JS_IsException(val1)) {
-                    JS_FreeValue(ctx, val);
-                    return JS_EXCEPTION;
-                }
-                r = JS_GetBigInt(val1);
-                ret = bf_set(r, a);
-                ret |= bf_rint(r, BF_RNDZ);
-                JS_FreeValue(ctx, val);
-                if (ret & BF_ST_MEM_ERROR) {
-                    JS_FreeValue(ctx, val1);
-                    val = JS_ThrowOutOfMemory(ctx);
-                } else if (ret & BF_ST_INEXACT) {
-                    JS_FreeValue(ctx, val1);
-                    val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
-                } else {
-                    val = JS_CompactBigInt(ctx, val1);
-                }
+                pos += snprintf(buf + pos, sizeof(buf) - pos,
+                                "%+07d", y);
+            }
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "-%02d-%02dT", mon + 1, d);
+            break;
+        case 3:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
+            if (part == 3) {
+                buf[pos++] = ',';
+                buf[pos++] = ' ';
             }
-            if (a == &a_s)
-                bf_delete(a);
+            break;
         }
-        break;
-#ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_DECIMAL:
-        val = JS_ToStringFree(ctx, val);
-        if (JS_IsException(val))
+    }
+    if (part & 2) { /* time part */
+        switch(fmt) {
+        case 0:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d:%02d:%02d GMT", h, m, s);
             break;
-        goto redo;
-#endif
-    case JS_TAG_STRING:
-        val = JS_StringToBigIntErr(ctx, val);
-        break;
-    case JS_TAG_OBJECT:
-        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
-        if (JS_IsException(val))
+        case 1:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d:%02d:%02d GMT", h, m, s);
+            if (tz < 0) {
+                buf[pos++] = '-';
+                tz = -tz;
+            } else {
+                buf[pos++] = '+';
+            }
+            /* tz is >= 0, can use % */
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d%02d", tz / 60, tz % 60);
+            /* XXX: tack the time zone code? */
             break;
-        goto redo;
-    case JS_TAG_NULL:
-    case JS_TAG_UNDEFINED:
-    default:
-        JS_FreeValue(ctx, val);
-        return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
+        case 2:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d:%02d:%02d.%03dZ", h, m, s, ms);
+            break;
+        case 3:
+            pos += snprintf(buf + pos, sizeof(buf) - pos,
+                            "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
+                            (h < 12) ? 'A' : 'P');
+            break;
+        }
     }
-    return val;
+    return JS_NewStringLen(ctx, buf, pos);
 }
 
-static JSValue js_bigint_constructor(JSContext *ctx,
-                                     JSValueConst new_target,
-                                     int argc, JSValueConst *argv)
-{
-    if (!JS_IsUndefined(new_target))
-        return JS_ThrowTypeError(ctx, "not a constructor");
-    return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
+/* OS dependent: return the UTC time in ms since 1970. */
+static int64_t date_now(void) {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
 }
 
-static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
+static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+                                   int argc, JSValueConst *argv)
 {
-    if (JS_IsBigInt(ctx, this_val))
-        return JS_DupValue(ctx, this_val);
+    // Date(y, mon, d, h, m, s, ms)
+    JSValue rv;
+    int i, n;
+    double a, val;
 
-    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
-        JSObject *p = JS_VALUE_GET_OBJ(this_val);
-        if (p->class_id == JS_CLASS_BIG_INT) {
-            if (JS_IsBigInt(ctx, p->u.object_data))
-                return JS_DupValue(ctx, p->u.object_data);
+    if (JS_IsUndefined(new_target)) {
+        /* invoked as function */
+        argc = 0;
+    }
+    n = argc;
+    if (n == 0) {
+        val = date_now();
+    } else if (n == 1) {
+        JSValue v, dv;
+        if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+            JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+            if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
+                if (JS_ToFloat64(ctx, &val, p->u.object_data))
+                    return JS_EXCEPTION;
+                val = time_clip(val);
+                goto has_val;
+            }
+        }
+        v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+        if (JS_IsString(v)) {
+            dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
+            JS_FreeValue(ctx, v);
+            if (JS_IsException(dv))
+                return JS_EXCEPTION;
+            if (JS_ToFloat64Free(ctx, &val, dv))
+                return JS_EXCEPTION;
+        } else {
+            if (JS_ToFloat64Free(ctx, &val, v))
+                return JS_EXCEPTION;
+        }
+        val = time_clip(val);
+    } else {
+        double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+        if (n > 7)
+            n = 7;
+        for(i = 0; i < n; i++) {
+            if (JS_ToFloat64(ctx, &a, argv[i]))
+                return JS_EXCEPTION;
+            if (!isfinite(a))
+                break;
+            fields[i] = trunc(a);
+            if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+                fields[0] += 1900;
         }
+        val = (i == n) ? set_date_fields(fields, 1) : NAN;
     }
-    return JS_ThrowTypeError(ctx, "not a BigInt");
+has_val:
+#if 0
+    JSValueConst args[3];
+    args[0] = new_target;
+    args[1] = ctx->class_proto[JS_CLASS_DATE];
+    args[2] = JS_NewFloat64(ctx, val);
+    rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
+#else
+    rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
+    if (!JS_IsException(rv))
+        JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
+#endif
+    if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
+        /* invoked as a function, return (new Date()).toString(); */
+        JSValue s;
+        s = get_date_string(ctx, rv, 0, NULL, 0x13);
+        JS_FreeValue(ctx, rv);
+        rv = s;
+    }
+    return rv;
 }
 
-static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
-                                  int argc, JSValueConst *argv)
+static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
+                           int argc, JSValueConst *argv)
 {
-    JSValue val;
-    int base;
-    JSValue ret;
+    // UTC(y, mon, d, h, m, s, ms)
+    double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+    int i, n;
+    double a;
 
-    val = js_thisBigIntValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (argc == 0 || JS_IsUndefined(argv[0])) {
-        base = 10;
-    } else {
-        base = js_get_radix(ctx, argv[0]);
-        if (base < 0)
-            goto fail;
+    n = argc;
+    if (n == 0)
+        return JS_NAN;
+    if (n > 7)
+        n = 7;
+    for(i = 0; i < n; i++) {
+        if (JS_ToFloat64(ctx, &a, argv[i]))
+            return JS_EXCEPTION;
+        if (!isfinite(a))
+            return JS_NAN;
+        fields[i] = trunc(a);
+        if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+            fields[0] += 1900;
     }
-    ret = js_bigint_to_string1(ctx, val, base);
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
-}
-
-static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
-                                 int argc, JSValueConst *argv)
-{
-    return js_thisBigIntValue(ctx, this_val);
+    return JS_NewFloat64(ctx, set_date_fields(fields, 0));
 }
 
-#ifdef CONFIG_BIGNUM
-static JSValue js_bigint_div(JSContext *ctx,
-                              JSValueConst this_val,
-                              int argc, JSValueConst *argv, int magic)
-{
-    bf_t a_s, b_s, *a, *b, *r, *q;
-    int status;
-    JSValue q_val, r_val;
+/* Date string parsing */
 
-    q_val = JS_NewBigInt(ctx);
-    if (JS_IsException(q_val))
-        return JS_EXCEPTION;
-    r_val = JS_NewBigInt(ctx);
-    if (JS_IsException(r_val))
-        goto fail;
-    b = NULL;
-    a = JS_ToBigInt(ctx, &a_s, argv[0]);
-    if (!a)
-        goto fail;
-    b = JS_ToBigInt(ctx, &b_s, argv[1]);
-    if (!b) {
-        JS_FreeBigInt(ctx, a, &a_s);
-        goto fail;
-    }
-    q = JS_GetBigInt(q_val);
-    r = JS_GetBigInt(r_val);
-    status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
-    JS_FreeBigInt(ctx, a, &a_s);
-    JS_FreeBigInt(ctx, b, &b_s);
-    if (unlikely(status)) {
-        throw_bf_exception(ctx, status);
-        goto fail;
-    }
-    q_val = JS_CompactBigInt(ctx, q_val);
-    if (magic & 0x10) {
-        JSValue ret;
-        ret = JS_NewArray(ctx);
-        if (JS_IsException(ret))
-            goto fail;
-        JS_SetPropertyUint32(ctx, ret, 0, q_val);
-        JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
-        return ret;
+static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
+    if (sp[*pp] == c) {
+        *pp += 1;
+        return TRUE;
     } else {
-        JS_FreeValue(ctx, r_val);
-        return q_val;
+        return FALSE;
     }
- fail:
-    JS_FreeValue(ctx, q_val);
-    JS_FreeValue(ctx, r_val);
-    return JS_EXCEPTION;
 }
 
-static JSValue js_bigint_sqrt(JSContext *ctx,
-                               JSValueConst this_val,
-                               int argc, JSValueConst *argv, int magic)
-{
-    bf_t a_s, *a, *r, *rem;
-    int status;
-    JSValue r_val, rem_val;
+/* skip spaces, update offset, return next char */
+static int string_skip_spaces(const uint8_t *sp, int *pp) {
+    int c;
+    while ((c = sp[*pp]) == ' ')
+        *pp += 1;
+    return c;
+}
 
-    r_val = JS_NewBigInt(ctx);
-    if (JS_IsException(r_val))
-        return JS_EXCEPTION;
-    rem_val = JS_NewBigInt(ctx);
-    if (JS_IsException(rem_val))
-        return JS_EXCEPTION;
-    r = JS_GetBigInt(r_val);
-    rem = JS_GetBigInt(rem_val);
+/* skip dashes dots and commas */
+static int string_skip_separators(const uint8_t *sp, int *pp) {
+    int c;
+    while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
+        *pp += 1;
+    return c;
+}
 
-    a = JS_ToBigInt(ctx, &a_s, argv[0]);
-    if (!a)
-        goto fail;
-    status = bf_sqrtrem(r, rem, a);
-    JS_FreeBigInt(ctx, a, &a_s);
-    if (unlikely(status & ~BF_ST_INEXACT)) {
-        throw_bf_exception(ctx, status);
-        goto fail;
-    }
-    r_val = JS_CompactBigInt(ctx, r_val);
-    if (magic) {
-        JSValue ret;
-        ret = JS_NewArray(ctx);
-        if (JS_IsException(ret))
-            goto fail;
-        JS_SetPropertyUint32(ctx, ret, 0, r_val);
-        JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
-        return ret;
-    } else {
-        JS_FreeValue(ctx, rem_val);
-        return r_val;
-    }
- fail:
-    JS_FreeValue(ctx, r_val);
-    JS_FreeValue(ctx, rem_val);
-    return JS_EXCEPTION;
+/* skip a word, stop on spaces, digits and separators, update offset */
+static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
+    int c;
+    while (!strchr(stoplist, c = sp[*pp]))
+        *pp += 1;
+    return c;
 }
 
-static JSValue js_bigint_op1(JSContext *ctx,
-                              JSValueConst this_val,
-                              int argc, JSValueConst *argv,
-                              int magic)
+/* parse a numeric field (max_digits = 0 -> no maximum) */
+static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
+                              int min_digits, int max_digits)
 {
-    bf_t a_s, *a;
-    int64_t res;
+    int v = 0;
+    int c, p = *pp, p_start;
 
-    a = JS_ToBigInt(ctx, &a_s, argv[0]);
-    if (!a)
-        return JS_EXCEPTION;
-    switch(magic) {
-    case 0: /* floorLog2 */
-        if (a->sign || a->expn <= 0) {
-            res = -1;
-        } else {
-            res = a->expn - 1;
-        }
-        break;
-    case 1: /* ctz */
-        if (bf_is_zero(a)) {
-            res = -1;
-        } else {
-            res = bf_get_exp_min(a);
-        }
-        break;
-    default:
-        abort();
+    p_start = p;
+    while ((c = sp[p]) >= '0' && c <= '9') {
+        /* arbitrary limit to 9 digits */
+        if (v >= 100000000)
+            return FALSE;
+        v = v * 10 + c - '0';
+        p++;
+        if (p - p_start == max_digits)
+            break;
     }
-    JS_FreeBigInt(ctx, a, &a_s);
-    return JS_NewBigInt64(ctx, res);
+    if (p - p_start < min_digits)
+        return FALSE;
+    *pval = v;
+    *pp = p;
+    return TRUE;
 }
-#endif
 
-static JSValue js_bigint_asUintN(JSContext *ctx,
-                                  JSValueConst this_val,
-                                  int argc, JSValueConst *argv, int asIntN)
-{
-    uint64_t bits;
-    bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
-    JSValue res;
+static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
+    /* parse optional fractional part as milliseconds and truncate. */
+    /* spec does not indicate which rounding should be used */
+    int mul = 100, ms = 0, c, p_start, p = *pp;
 
-    if (JS_ToIndex(ctx, &bits, argv[0]))
-        return JS_EXCEPTION;
-    res = JS_NewBigInt(ctx);
-    if (JS_IsException(res))
-        return JS_EXCEPTION;
-    r = JS_GetBigInt(res);
-    a = JS_ToBigInt(ctx, &a_s, argv[1]);
-    if (!a) {
-        JS_FreeValue(ctx, res);
-        return JS_EXCEPTION;
+    c = sp[p];
+    if (c == '.' || c == ',') {
+        p++;
+        p_start = p;
+        while ((c = sp[p]) >= '0' && c <= '9') {
+            ms += (c - '0') * mul;
+            mul /= 10;
+            p++;
+            if (p - p_start == 9)
+                break;
+        }
+        if (p > p_start) {
+            /* only consume the separator if digits are present */
+            *pval = ms;
+            *pp = p;
+        }
     }
-    /* XXX: optimize */
-    r = JS_GetBigInt(res);
-    bf_init(ctx->bf_ctx, mask);
-    bf_set_ui(mask, 1);
-    bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
-    bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
-    bf_logic_and(r, a, mask);
-    if (asIntN && bits != 0) {
-        bf_set_ui(mask, 1);
-        bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
-        if (bf_cmpu(r, mask) >= 0) {
-            bf_set_ui(mask, 1);
-            bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
-            bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
-        }
-    }
-    bf_delete(mask);
-    JS_FreeBigInt(ctx, a, &a_s);
-    return JS_CompactBigInt(ctx, res);
+    return TRUE;
 }
 
-static const JSCFunctionListEntry js_bigint_funcs[] = {
-    JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
-    JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
-#ifdef CONFIG_BIGNUM
-    /* QuickJS extensions */
-    JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
-    JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
-    JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
-    JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
-    JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
-    JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
-    JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
-    JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
-    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
-    JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
-    JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
-    JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
-#endif
-};
-
-static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
-    JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
-    JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
-    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
-};
-
-void JS_AddIntrinsicBigInt(JSContext *ctx)
-{
-    JSRuntime *rt = ctx->rt;
-    JSValueConst obj1;
-
-    rt->bigint_ops.to_string = js_bigint_to_string;
-    rt->bigint_ops.from_string = js_string_to_bigint;
-    rt->bigint_ops.unary_arith = js_unary_arith_bigint;
-    rt->bigint_ops.binary_arith = js_binary_arith_bigint;
-    rt->bigint_ops.compare = js_compare_bigfloat;
-
-    ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
-    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
-                               js_bigint_proto_funcs,
-                               countof(js_bigint_proto_funcs));
-    obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
-                                    ctx->class_proto[JS_CLASS_BIG_INT]);
-    JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
-                               countof(js_bigint_funcs));
+static uint8_t upper_ascii(uint8_t c) {
+    return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
 }
 
-#ifdef CONFIG_BIGNUM
-
-/* BigFloat */
-
-static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
-{
-    if (JS_IsBigFloat(this_val))
-        return JS_DupValue(ctx, this_val);
+static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
+    int tz = 0, sgn, hh, mm, p = *pp;
 
-    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
-        JSObject *p = JS_VALUE_GET_OBJ(this_val);
-        if (p->class_id == JS_CLASS_BIG_FLOAT) {
-            if (JS_IsBigFloat(p->u.object_data))
-                return JS_DupValue(ctx, p->u.object_data);
+    sgn = sp[p++];
+    if (sgn == '+' || sgn == '-') {
+        int n = p;
+        if (!string_get_digits(sp, &p, &hh, 1, 0))
+            return FALSE;
+        n = p - n;
+        if (strict && n != 2 && n != 4)
+            return FALSE;
+        while (n > 4) {
+            n -= 2;
+            hh /= 100;
+        }
+        if (n > 2) {
+            mm = hh % 100;
+            hh = hh / 100;
+        } else {
+            mm = 0;
+            if (string_skip_char(sp, &p, ':')  /* optional separator */
+            &&  !string_get_digits(sp, &p, &mm, 2, 2))
+                return FALSE;
         }
+        if (hh > 23 || mm > 59)
+            return FALSE;
+        tz = hh * 60 + mm;
+        if (sgn != '+')
+            tz = -tz;
+    } else
+    if (sgn != 'Z') {
+        return FALSE;
+    }
+    *pp = p;
+    *tzp = tz;
+    return TRUE;
+}
+
+static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
+    int p = *pp;
+    while (*s != '\0') {
+        if (upper_ascii(sp[p]) != upper_ascii(*s++))
+            return FALSE;
+        p++;
     }
-    return JS_ThrowTypeError(ctx, "not a bigfloat");
+    *pp = p;
+    return TRUE;
 }
 
-static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
-                                    int argc, JSValueConst *argv)
-{
-    JSValue val;
-    int base;
-    JSValue ret;
+static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
+    int n, i;
 
-    val = js_thisBigFloatValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (argc == 0 || JS_IsUndefined(argv[0])) {
-        base = 10;
-    } else {
-        base = js_get_radix(ctx, argv[0]);
-        if (base < 0)
-            goto fail;
+    for (n = 0; n < count; n++) {
+        for (i = 0;; i++) {
+            if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
+                break;
+            if (i == 2)
+                return n;
+        }
     }
-    ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
+    return -1;
 }
 
-static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
-                                   int argc, JSValueConst *argv)
-{
-    return js_thisBigFloatValue(ctx, this_val);
-}
+static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
+    int n;
 
-static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
-{
-    int rnd_mode;
-    if (JS_ToInt32Sat(ctx, &rnd_mode, val))
-        return -1;
-    if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
-        JS_ThrowRangeError(ctx, "invalid rounding mode");
-        return -1;
-    }
-    return rnd_mode;
+    n = find_abbrev(sp, *pp, month_names, 12);
+    if (n < 0)
+        return FALSE;
+
+    *pval = n + 1;
+    *pp += 3;
+    return TRUE;
 }
 
-static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
-                                 int argc, JSValueConst *argv)
-{
-    JSValue val, ret;
-    int64_t f;
-    int rnd_mode, radix;
+/* parse toISOString format */
+static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
+    int sgn, i, p = 0;
 
-    val = js_thisBigFloatValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_ToInt64Sat(ctx, &f, argv[0]))
-        goto fail;
-    if (f < 0 || f > BF_PREC_MAX) {
-        JS_ThrowRangeError(ctx, "invalid number of digits");
-        goto fail;
-    }
-    rnd_mode = BF_RNDNA;
-    radix = 10;
-    /* XXX: swap parameter order for rounding mode and radix */
-    if (argc > 1) {
-        rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
-        if (rnd_mode < 0)
-            goto fail;
-    }
-    if (argc > 2) {
-        radix = js_get_radix(ctx, argv[2]);
-        if (radix < 0)
-            goto fail;
+    /* initialize fields to the beginning of the Epoch */
+    for (i = 0; i < 9; i++) {
+        fields[i] = (i == 2);
     }
-    ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
-}
-
-static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
-{
-    BOOL res;
-    uint32_t tag;
+    *is_local = FALSE;
 
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_BIG_FLOAT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            res = bf_is_finite(&p->num);
+    /* year is either yyyy digits or [+-]yyyyyy */
+    sgn = sp[p];
+    if (sgn == '-' || sgn == '+') {
+        p++;
+        if (!string_get_digits(sp, &p, &fields[0], 6, 6))
+            return FALSE;
+        if (sgn == '-') {
+            if (fields[0] == 0)
+                return FALSE; // reject -000000
+            fields[0] = -fields[0];
         }
-        break;
-    default:
-        res = FALSE;
-        break;
+    } else {
+        if (!string_get_digits(sp, &p, &fields[0], 4, 4))
+            return FALSE;
     }
-    return res;
-}
-
-static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
-                                       int argc, JSValueConst *argv)
-{
-    JSValue val, ret;
-    int64_t f;
-    int rnd_mode, radix;
-
-    val = js_thisBigFloatValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_ToInt64Sat(ctx, &f, argv[0]))
-        goto fail;
-    if (!js_bigfloat_is_finite(ctx, val)) {
-        ret = JS_ToString(ctx, val);
-    } else if (JS_IsUndefined(argv[0])) {
-        ret = js_ftoa(ctx, val, 10, 0,
-                      BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
-    } else {
-        if (f < 0 || f > BF_PREC_MAX) {
-            JS_ThrowRangeError(ctx, "invalid number of digits");
-            goto fail;
+    if (string_skip_char(sp, &p, '-')) {
+        if (!string_get_digits(sp, &p, &fields[1], 2, 2))  /* month */
+            return FALSE;
+        if (fields[1] < 1)
+            return FALSE;
+        fields[1] -= 1;
+        if (string_skip_char(sp, &p, '-')) {
+            if (!string_get_digits(sp, &p, &fields[2], 2, 2))  /* day */
+                return FALSE;
+            if (fields[2] < 1)
+                return FALSE;
         }
-        rnd_mode = BF_RNDNA;
-        radix = 10;
-        if (argc > 1) {
-            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
-            if (rnd_mode < 0)
-                goto fail;
+    }
+    if (string_skip_char(sp, &p, 'T')) {
+        *is_local = TRUE;
+        if (!string_get_digits(sp, &p, &fields[3], 2, 2)  /* hour */
+        ||  !string_skip_char(sp, &p, ':')
+        ||  !string_get_digits(sp, &p, &fields[4], 2, 2)) {  /* minute */
+            fields[3] = 100;  // reject unconditionally
+            return TRUE;
         }
-        if (argc > 2) {
-            radix = js_get_radix(ctx, argv[2]);
-            if (radix < 0)
-                goto fail;
+        if (string_skip_char(sp, &p, ':')) {
+            if (!string_get_digits(sp, &p, &fields[5], 2, 2))  /* second */
+                return FALSE;
+            string_get_milliseconds(sp, &p, &fields[6]);
         }
-        ret = js_ftoa(ctx, val, radix, f + 1,
-                      rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
     }
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
+    /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
+    if (sp[p]) {
+        *is_local = FALSE;
+        if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
+            return FALSE;
+    }
+    /* error if extraneous characters */
+    return sp[p] == '\0';
 }
 
-static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
-                                     int argc, JSValueConst *argv)
-{
-    JSValue val, ret;
-    int64_t p;
-    int rnd_mode, radix;
+static struct {
+    char name[6];
+    int16_t offset;
+} const js_tzabbr[] = {
+    { "GMT",   0 },         // Greenwich Mean Time
+    { "UTC",   0 },         // Coordinated Universal Time
+    { "UT",    0 },         // Universal Time
+    { "Z",     0 },         // Zulu Time
+    { "EDT",  -4 * 60 },    // Eastern Daylight Time
+    { "EST",  -5 * 60 },    // Eastern Standard Time
+    { "CDT",  -5 * 60 },    // Central Daylight Time
+    { "CST",  -6 * 60 },    // Central Standard Time
+    { "MDT",  -6 * 60 },    // Mountain Daylight Time
+    { "MST",  -7 * 60 },    // Mountain Standard Time
+    { "PDT",  -7 * 60 },    // Pacific Daylight Time
+    { "PST",  -8 * 60 },    // Pacific Standard Time
+    { "WET",  +0 * 60 },    // Western European Time
+    { "WEST", +1 * 60 },    // Western European Summer Time
+    { "CET",  +1 * 60 },    // Central European Time
+    { "CEST", +2 * 60 },    // Central European Summer Time
+    { "EET",  +2 * 60 },    // Eastern European Time
+    { "EEST", +3 * 60 },    // Eastern European Summer Time
+};
 
-    val = js_thisBigFloatValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_IsUndefined(argv[0]))
-        goto to_string;
-    if (JS_ToInt64Sat(ctx, &p, argv[0]))
-        goto fail;
-    if (!js_bigfloat_is_finite(ctx, val)) {
-    to_string:
-        ret = JS_ToString(ctx, this_val);
-    } else {
-        if (p < 1 || p > BF_PREC_MAX) {
-            JS_ThrowRangeError(ctx, "invalid number of digits");
-            goto fail;
-        }
-        rnd_mode = BF_RNDNA;
-        radix = 10;
-        if (argc > 1) {
-            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
-            if (rnd_mode < 0)
-                goto fail;
-        }
-        if (argc > 2) {
-            radix = js_get_radix(ctx, argv[2]);
-            if (radix < 0)
-                goto fail;
+static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
+    for (size_t i = 0; i < countof(js_tzabbr); i++) {
+        if (string_match(sp, pp, js_tzabbr[i].name)) {
+            *offset = js_tzabbr[i].offset;
+            return TRUE;
         }
-        ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
     }
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
+    return FALSE;
 }
 
-static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
-    JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
-    JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
-    JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
-    JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
-    JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
-};
+/* parse toString, toUTCString and other formats */
+static BOOL js_date_parse_otherstring(const uint8_t *sp,
+                                      int fields[minimum_length(9)],
+                                      BOOL *is_local) {
+    int c, i, val, p = 0, p_start;
+    int num[3];
+    BOOL has_year = FALSE;
+    BOOL has_mon = FALSE;
+    BOOL has_time = FALSE;
+    int num_index = 0;
 
-static JSValue js_bigfloat_constructor(JSContext *ctx,
-                                       JSValueConst new_target,
-                                       int argc, JSValueConst *argv)
-{
-    JSValue val;
-    if (!JS_IsUndefined(new_target))
-        return JS_ThrowTypeError(ctx, "not a constructor");
-    if (argc == 0) {
-        bf_t *r;
-        val = JS_NewBigFloat(ctx);
-        if (JS_IsException(val))
-            return val;
-        r = JS_GetBigFloat(val);
-        bf_set_zero(r, 0);
-    } else {
-        val = JS_DupValue(ctx, argv[0]);
-    redo:
-        switch(JS_VALUE_GET_NORM_TAG(val)) {
-        case JS_TAG_BIG_FLOAT:
-            break;
-        case JS_TAG_FLOAT64:
-            {
-                bf_t *r;
-                double d = JS_VALUE_GET_FLOAT64(val);
-                val = JS_NewBigFloat(ctx);
-                if (JS_IsException(val))
-                    break;
-                r = JS_GetBigFloat(val);
-                if (bf_set_float64(r, d))
-                    goto fail;
-            }
-            break;
-        case JS_TAG_INT:
-            {
-                bf_t *r;
-                int32_t v = JS_VALUE_GET_INT(val);
-                val = JS_NewBigFloat(ctx);
-                if (JS_IsException(val))
-                    break;
-                r = JS_GetBigFloat(val);
-                if (bf_set_si(r, v))
-                    goto fail;
-            }
-            break;
-        case JS_TAG_BIG_INT:
-            /* We keep the full precision of the integer */
-            {
-                JSBigFloat *p = JS_VALUE_GET_PTR(val);
-                val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
-            }
-            break;
-        case JS_TAG_BIG_DECIMAL:
-            val = JS_ToStringFree(ctx, val);
-            if (JS_IsException(val))
-                break;
-            goto redo;
-        case JS_TAG_STRING:
-            {
-                const char *str, *p;
-                size_t len;
-                int err;
+    /* initialize fields to the beginning of 2001-01-01 */
+    fields[0] = 2001;
+    fields[1] = 1;
+    fields[2] = 1;
+    for (i = 3; i < 9; i++) {
+        fields[i] = 0;
+    }
+    *is_local = TRUE;
 
-                str = JS_ToCStringLen(ctx, &len, val);
-                JS_FreeValue(ctx, val);
-                if (!str)
-                    return JS_EXCEPTION;
-                p = str;
-                p += skip_spaces(p);
-                if ((p - str) == len) {
-                    bf_t *r;
-                    val = JS_NewBigFloat(ctx);
-                    if (JS_IsException(val))
-                        break;
-                    r = JS_GetBigFloat(val);
-                    bf_set_zero(r, 0);
-                    err = 0;
-                } else {
-                    val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
-                                  ATOD_TYPE_BIG_FLOAT |
-                                  ATOD_ACCEPT_PREFIX_AFTER_SIGN);
-                    if (JS_IsException(val)) {
-                        JS_FreeCString(ctx, str);
-                        return JS_EXCEPTION;
+    while (string_skip_spaces(sp, &p)) {
+        p_start = p;
+        if ((c = sp[p]) == '+' || c == '-') {
+            if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
+                *is_local = FALSE;
+            } else {
+                p++;
+                if (string_get_digits(sp, &p, &val, 1, 0)) {
+                    if (c == '-') {
+                        if (val == 0)
+                            return FALSE;
+                        val = -val;
                     }
-                    p += skip_spaces(p);
-                    err = ((p - str) != len);
+                    fields[0] = val;
+                    has_year = TRUE;
                 }
-                JS_FreeCString(ctx, str);
-                if (err) {
-                    JS_FreeValue(ctx, val);
-                    return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
+            }
+        } else
+        if (string_get_digits(sp, &p, &val, 1, 0)) {
+            if (string_skip_char(sp, &p, ':')) {
+                /* time part */
+                fields[3] = val;
+                if (!string_get_digits(sp, &p, &fields[4], 1, 2))
+                    return FALSE;
+                if (string_skip_char(sp, &p, ':')) {
+                    if (!string_get_digits(sp, &p, &fields[5], 1, 2))
+                        return FALSE;
+                    string_get_milliseconds(sp, &p, &fields[6]);
+                }
+                has_time = TRUE;
+            } else {
+                if (p - p_start > 2) {
+                    fields[0] = val;
+                    has_year = TRUE;
+                } else
+                if (val < 1 || val > 31) {
+                    fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
+                    has_year = TRUE;
+                } else {
+                    if (num_index == 3)
+                        return FALSE;
+                    num[num_index++] = val;
                 }
             }
-            break;
-        case JS_TAG_OBJECT:
-            val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
-            if (JS_IsException(val))
-                break;
-            goto redo;
-        case JS_TAG_NULL:
-        case JS_TAG_UNDEFINED:
-        default:
-            JS_FreeValue(ctx, val);
-            return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
+        } else
+        if (string_get_month(sp, &p, &fields[1])) {
+            has_mon = TRUE;
+            string_skip_until(sp, &p, "0123456789 -/(");
+        } else
+        if (has_time && string_match(sp, &p, "PM")) {
+            if (fields[3] < 12)
+                fields[3] += 12;
+            continue;
+        } else
+        if (has_time && string_match(sp, &p, "AM")) {
+            if (fields[3] == 12)
+                fields[3] -= 12;
+            continue;
+        } else
+        if (string_get_tzabbr(sp, &p, &fields[8])) {
+            *is_local = FALSE;
+            continue;
+        } else
+        if (c == '(') {  /* skip parenthesized phrase */
+            int level = 0;
+            while ((c = sp[p]) != '\0') {
+                p++;
+                level += (c == '(');
+                level -= (c == ')');
+                if (!level)
+                    break;
+            }
+            if (level > 0)
+                return FALSE;
+        } else
+        if (c == ')') {
+            return FALSE;
+        } else {
+            if (has_year + has_mon + has_time + num_index)
+                return FALSE;
+            /* skip a word */
+            string_skip_until(sp, &p, " -/(");
         }
+        string_skip_separators(sp, &p);
     }
-    return val;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
-}
+    if (num_index + has_year + has_mon > 3)
+        return FALSE;
 
-static JSValue js_bigfloat_get_const(JSContext *ctx,
-                                     JSValueConst this_val, int magic)
-{
-    bf_t *r;
-    JSValue val;
-    val = JS_NewBigFloat(ctx);
-    if (JS_IsException(val))
-        return val;
-    r = JS_GetBigFloat(val);
-    switch(magic) {
-    case 0: /* PI */
-        bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
+    switch (num_index) {
+    case 0:
+        if (!has_year)
+            return FALSE;
         break;
-    case 1: /* LN2 */
-        bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
+    case 1:
+        if (has_mon)
+            fields[2] = num[0];
+        else
+            fields[1] = num[0];
         break;
-    case 2: /* MIN_VALUE */
-    case 3: /* MAX_VALUE */
-        {
-            slimb_t e_range, e;
-            e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
-            bf_set_ui(r, 1);
-            if (magic == 2) {
-                e = -e_range + 2;
-                if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
-                    e -= ctx->fp_env.prec - 1;
-                bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
-            } else {
-                bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
-                            ctx->fp_env.flags);
-                bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
-                bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
-                            ctx->fp_env.flags);
-            }
+    case 2:
+        if (has_year) {
+            fields[1] = num[0];
+            fields[2] = num[1];
+        } else
+        if (has_mon) {
+            fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
+            fields[2] = num[0];
+        } else {
+            fields[1] = num[0];
+            fields[2] = num[1];
         }
         break;
-    case 4: /* EPSILON */
-        bf_set_ui(r, 1);
-        bf_mul_2exp(r, 1 - ctx->fp_env.prec,
-                    ctx->fp_env.prec, ctx->fp_env.flags);
+    case 3:
+        fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
+        fields[1] = num[0];
+        fields[2] = num[1];
         break;
     default:
-        abort();
+        return FALSE;
     }
-    return val;
+    if (fields[1] < 1 || fields[2] < 1)
+        return FALSE;
+    fields[1] -= 1;
+    return TRUE;
 }
 
-static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
-                                      int argc, JSValueConst *argv)
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+                             int argc, JSValueConst *argv)
 {
-    bf_t *a;
-    const char *str;
-    JSValue ret;
-    int radix;
-    JSFloatEnv *fe;
+    JSValue s, rv;
+    int fields[9];
+    double fields1[9];
+    double d;
+    int i, c;
+    JSString *sp;
+    uint8_t buf[128];
+    BOOL is_local;
 
-    str = JS_ToCString(ctx, argv[0]);
-    if (!str)
-        return JS_EXCEPTION;
-    if (JS_ToInt32(ctx, &radix, argv[1])) {
-    fail:
-        JS_FreeCString(ctx, str);
+    rv = JS_NAN;
+
+    s = JS_ToString(ctx, argv[0]);
+    if (JS_IsException(s))
         return JS_EXCEPTION;
+
+    sp = JS_VALUE_GET_STRING(s);
+    /* convert the string as a byte array */
+    for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
+        c = string_get(sp, i);
+        if (c > 255)
+            c = (c == 0x2212) ? '-' : 'x';
+        buf[i] = c;
     }
-    if (radix != 0 && (radix < 2 || radix > 36)) {
-        JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
-        goto fail;
-    }
-    fe = &ctx->fp_env;
-    if (argc > 2) {
-        fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
-        if (!fe)
-            goto fail;
+    buf[i] = '\0';
+    if (js_date_parse_isostring(buf, fields, &is_local)
+    ||  js_date_parse_otherstring(buf, fields, &is_local)) {
+        static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
+        BOOL valid = TRUE;
+        /* check field maximum values */
+        for (i = 1; i < 6; i++) {
+            if (fields[i] > field_max[i])
+                valid = FALSE;
+        }
+        /* special case 24:00:00.000 */
+        if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
+            valid = FALSE;
+        if (valid) {
+            for(i = 0; i < 7; i++)
+                fields1[i] = fields[i];
+            d = set_date_fields(fields1, is_local) - fields[8] * 60000;
+            rv = JS_NewFloat64(ctx, d);
+        }
     }
-    ret = JS_NewBigFloat(ctx);
-    if (JS_IsException(ret))
-        goto done;
-    a = JS_GetBigFloat(ret);
-    /* XXX: use js_atof() */
-    bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
- done:
-    JS_FreeCString(ctx, str);
-    return ret;
-}
-
-static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
-                                    int argc, JSValueConst *argv)
-{
-    JSValueConst val = argv[0];
-    JSBigFloat *p;
-
-    if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
-        return JS_FALSE;
-    p = JS_VALUE_GET_PTR(val);
-    return JS_NewBool(ctx, bf_is_finite(&p->num));
-}
-
-static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
-                                 int argc, JSValueConst *argv)
-{
-    JSValueConst val = argv[0];
-    JSBigFloat *p;
-
-    if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
-        return JS_FALSE;
-    p = JS_VALUE_GET_PTR(val);
-    return JS_NewBool(ctx, bf_is_nan(&p->num));
+    JS_FreeValue(ctx, s);
+    return rv;
 }
 
-enum {
-    MATH_OP_ABS,
-    MATH_OP_FLOOR,
-    MATH_OP_CEIL,
-    MATH_OP_ROUND,
-    MATH_OP_TRUNC,
-    MATH_OP_SQRT,
-    MATH_OP_FPROUND,
-    MATH_OP_ACOS,
-    MATH_OP_ASIN,
-    MATH_OP_ATAN,
-    MATH_OP_ATAN2,
-    MATH_OP_COS,
-    MATH_OP_EXP,
-    MATH_OP_LOG,
-    MATH_OP_POW,
-    MATH_OP_SIN,
-    MATH_OP_TAN,
-    MATH_OP_FMOD,
-    MATH_OP_REM,
-    MATH_OP_SIGN,
-
-    MATH_OP_ADD,
-    MATH_OP_SUB,
-    MATH_OP_MUL,
-    MATH_OP_DIV,
-};
-
-static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
-                           int argc, JSValueConst *argv, int magic)
+static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
+                           int argc, JSValueConst *argv)
 {
-    bf_t a_s, *a, *r;
-    JSFloatEnv *fe;
-    int rnd_mode;
-    JSValue op1, res;
-
-    op1 = JS_ToNumeric(ctx, argv[0]);
-    if (JS_IsException(op1))
-        return op1;
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a) {
-        JS_FreeValue(ctx, op1);
-        return JS_EXCEPTION;
-    }
-    fe = &ctx->fp_env;
-    if (argc > 1) {
-        fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
-        if (!fe)
-            goto fail;
-    }
-    res = JS_NewBigFloat(ctx);
-    if (JS_IsException(res)) {
-    fail:
-        if (a == &a_s)
-            bf_delete(a);
-        JS_FreeValue(ctx, op1);
-        return JS_EXCEPTION;
-    }
-    r = JS_GetBigFloat(res);
-    switch (magic) {
-    case MATH_OP_ABS:
-        bf_set(r, a);
-        r->sign = 0;
-        break;
-    case MATH_OP_FLOOR:
-        rnd_mode = BF_RNDD;
-        goto rint;
-    case MATH_OP_CEIL:
-        rnd_mode = BF_RNDU;
-        goto rint;
-    case MATH_OP_ROUND:
-        rnd_mode = BF_RNDNA;
-        goto rint;
-    case MATH_OP_TRUNC:
-        rnd_mode = BF_RNDZ;
-    rint:
-        bf_set(r, a);
-        fe->status |= bf_rint(r, rnd_mode);
-        break;
-    case MATH_OP_SQRT:
-        fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_FPROUND:
-        bf_set(r, a);
-        fe->status |= bf_round(r, fe->prec, fe->flags);
-        break;
-    case MATH_OP_ACOS:
-        fe->status |= bf_acos(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_ASIN:
-        fe->status |= bf_asin(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_ATAN:
-        fe->status |= bf_atan(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_COS:
-        fe->status |= bf_cos(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_EXP:
-        fe->status |= bf_exp(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_LOG:
-        fe->status |= bf_log(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_SIN:
-        fe->status |= bf_sin(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_TAN:
-        fe->status |= bf_tan(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_SIGN:
-        if (bf_is_nan(a) || bf_is_zero(a)) {
-            bf_set(r, a);
-        } else {
-            bf_set_si(r, 1 - 2 * a->sign);
-        }
-        break;
-    default:
-        abort();
-    }
-    if (a == &a_s)
-        bf_delete(a);
-    JS_FreeValue(ctx, op1);
-    return res;
+    // now()
+    return JS_NewInt64(ctx, date_now());
 }
 
-static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
-                            int argc, JSValueConst *argv, int magic)
+static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
+                                          int argc, JSValueConst *argv)
 {
-    bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
-    JSFloatEnv *fe;
-    JSValue op1, op2, res;
+    // Symbol_toPrimitive(hint)
+    JSValueConst obj = this_val;
+    JSAtom hint = JS_ATOM_NULL;
+    int hint_num;
 
-    op1 = JS_ToNumeric(ctx, argv[0]);
-    if (JS_IsException(op1))
-        return op1;
-    op2 = JS_ToNumeric(ctx, argv[1]);
-    if (JS_IsException(op2)) {
-        JS_FreeValue(ctx, op1);
-        return op2;
-    }
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    if (!a)
-        goto fail1;
-    b = JS_ToBigFloat(ctx, &b_s, op2);
-    if (!b)
-        goto fail2;
-    fe = &ctx->fp_env;
-    if (argc > 2) {
-        fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
-        if (!fe)
-            goto fail;
-    }
-    res = JS_NewBigFloat(ctx);
-    if (JS_IsException(res)) {
-    fail:
-        if (b == &b_s)
-            bf_delete(b);
-    fail2:
-        if (a == &a_s)
-            bf_delete(a);
-    fail1:
-        JS_FreeValue(ctx, op1);
-        JS_FreeValue(ctx, op2);
-        return JS_EXCEPTION;
+    if (!JS_IsObject(obj))
+        return JS_ThrowTypeErrorNotAnObject(ctx);
+
+    if (JS_IsString(argv[0])) {
+        hint = JS_ValueToAtom(ctx, argv[0]);
+        if (hint == JS_ATOM_NULL)
+            return JS_EXCEPTION;
+        JS_FreeAtom(ctx, hint);
     }
-    r = JS_GetBigFloat(res);
-    switch (magic) {
-    case MATH_OP_ATAN2:
-        fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_POW:
-        fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
-        break;
-    case MATH_OP_FMOD:
-        fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
-        break;
-    case MATH_OP_REM:
-        fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
-        break;
-    case MATH_OP_ADD:
-        fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_SUB:
-        fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_MUL:
-        fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
+    switch (hint) {
+    case JS_ATOM_number:
+    case JS_ATOM_integer:
+        hint_num = HINT_NUMBER;
         break;
-    case MATH_OP_DIV:
-        fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
+    case JS_ATOM_string:
+    case JS_ATOM_default:
+        hint_num = HINT_STRING;
         break;
     default:
-        abort();
+        return JS_ThrowTypeError(ctx, "invalid hint");
     }
-    if (a == &a_s)
-        bf_delete(a);
-    if (b == &b_s)
-        bf_delete(b);
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    return res;
+    return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
 }
 
-static const JSCFunctionListEntry js_bigfloat_funcs[] = {
-    JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
-    JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
-    JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
-    JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
-    JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
-    JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
-    JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
-    JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
-    JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
-    JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
-    JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
-    JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
-    JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
-    JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
-    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
-    JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
-    JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
-    JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
-    JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
-    JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
-    JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
-    JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
-    JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
-    JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
-    JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
-    JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
-    JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
-    JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
-    JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
-    JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
-    JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
-    JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
-};
-
-/* FloatEnv */
-
-static JSValue js_float_env_constructor(JSContext *ctx,
-                                        JSValueConst new_target,
-                                        int argc, JSValueConst *argv)
+static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+                                         int argc, JSValueConst *argv)
 {
-    JSValue obj;
-    JSFloatEnv *fe;
-    int64_t prec;
-    int flags, rndmode;
-
-    prec = ctx->fp_env.prec;
-    flags = ctx->fp_env.flags;
-    if (!JS_IsUndefined(argv[0])) {
-        if (JS_ToInt64Sat(ctx, &prec, argv[0]))
-            return JS_EXCEPTION;
-        if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
-            return JS_ThrowRangeError(ctx, "invalid precision");
-        flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
-        if (argc > 1 && !JS_IsUndefined(argv[1])) {
-            if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
-                return JS_EXCEPTION;
-            if (rndmode < BF_RNDN || rndmode > BF_RNDF)
-                return JS_ThrowRangeError(ctx, "invalid rounding mode");
-            flags = rndmode;
-        }
-    }
+    // getTimezoneOffset()
+    double v;
 
-    obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
-    if (JS_IsException(obj))
-        return JS_EXCEPTION;
-    fe = js_malloc(ctx, sizeof(*fe));
-    if (!fe)
+    if (JS_ThisTimeValue(ctx, &v, this_val))
         return JS_EXCEPTION;
-    fe->prec = prec;
-    fe->flags = flags;
-    fe->status = 0;
-    JS_SetOpaque(obj, fe);
-    return obj;
+    if (isnan(v))
+        return JS_NAN;
+    else
+        /* assuming -8.64e15 <= v <= -8.64e15 */
+        return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
 }
 
-static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
+static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
+                               int argc, JSValueConst *argv)
 {
-    JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
-    js_free_rt(rt, fe);
-}
+    // getTime()
+    double v;
 
-static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
-{
-    return JS_NewInt64(ctx, ctx->fp_env.prec);
+    if (JS_ThisTimeValue(ctx, &v, this_val))
+        return JS_EXCEPTION;
+    return JS_NewFloat64(ctx, v);
 }
 
-static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
+static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
+                               int argc, JSValueConst *argv)
 {
-    return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
+    // setTime(v)
+    double v;
+
+    if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
+        return JS_EXCEPTION;
+    return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
 }
 
-static JSValue js_float_env_setPrec(JSContext *ctx,
-                                    JSValueConst this_val,
-                                    int argc, JSValueConst *argv)
+static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
+                               int argc, JSValueConst *argv)
 {
-    JSValueConst func;
-    int exp_bits, flags, saved_flags;
-    JSValue ret;
-    limb_t saved_prec;
-    int64_t prec;
+    // setYear(y)
+    double y;
+    JSValueConst args[1];
 
-    func = argv[0];
-    if (JS_ToInt64Sat(ctx, &prec, argv[1]))
+    if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
         return JS_EXCEPTION;
-    if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
-        return JS_ThrowRangeError(ctx, "invalid precision");
-    exp_bits = BF_EXP_BITS_MAX;
-
-    if (argc > 2 && !JS_IsUndefined(argv[2])) {
-        if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
-            return JS_EXCEPTION;
-        if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
-            return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+    y = +y;
+    if (isfinite(y)) {
+        y = trunc(y);
+        if (y >= 0 && y < 100)
+            y += 1900;
     }
+    args[0] = JS_NewFloat64(ctx, y);
+    return set_date_field(ctx, this_val, 1, args, 0x011);
+}
 
-    flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
-
-    saved_prec = ctx->fp_env.prec;
-    saved_flags = ctx->fp_env.flags;
+static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
+                              int argc, JSValueConst *argv)
+{
+    // toJSON(key)
+    JSValue obj, tv, method, rv;
+    double d;
 
-    ctx->fp_env.prec = prec;
-    ctx->fp_env.flags = flags;
+    rv = JS_EXCEPTION;
+    tv = JS_UNDEFINED;
 
-    ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
-    /* always restore the floating point precision */
-    ctx->fp_env.prec = saved_prec;
-    ctx->fp_env.flags = saved_flags;
-    return ret;
+    obj = JS_ToObject(ctx, this_val);
+    tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
+    if (JS_IsException(tv))
+        goto exception;
+    if (JS_IsNumber(tv)) {
+        if (JS_ToFloat64(ctx, &d, tv) < 0)
+            goto exception;
+        if (!isfinite(d)) {
+            rv = JS_NULL;
+            goto done;
+        }
+    }
+    method = JS_GetPropertyStr(ctx, obj, "toISOString");
+    if (JS_IsException(method))
+        goto exception;
+    if (!JS_IsFunction(ctx, method)) {
+        JS_ThrowTypeError(ctx, "object needs toISOString method");
+        JS_FreeValue(ctx, method);
+        goto exception;
+    }
+    rv = JS_CallFree(ctx, method, obj, 0, NULL);
+exception:
+done:
+    JS_FreeValue(ctx, obj);
+    JS_FreeValue(ctx, tv);
+    return rv;
 }
 
-#define FE_PREC      (-1)
-#define FE_EXP       (-2)
-#define FE_RNDMODE   (-3)
-#define FE_SUBNORMAL (-4)
+static const JSCFunctionListEntry js_date_funcs[] = {
+    JS_CFUNC_DEF("now", 0, js_Date_now ),
+    JS_CFUNC_DEF("parse", 1, js_Date_parse ),
+    JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
+};
+
+static const JSCFunctionListEntry js_date_proto_funcs[] = {
+    JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
+    JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
+    JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
+    JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
+    JS_ALIAS_DEF("toGMTString", "toUTCString" ),
+    JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
+    JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
+    JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
+    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
+    JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
+    JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
+    JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
+    JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
+    JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
+    JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
+    JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
+    JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
+    JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
+    JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
+    JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
+    JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
+    JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
+    JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
+    JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
+    JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
+    JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
+    JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
+    JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
+    JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
+    JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
+    JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
+    JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
+    JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
+    JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
+    JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
+    JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
+    JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
+    JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
+    JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
+    JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
+    JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
+    JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
+    JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
+    JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
+    JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
+    JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
+    JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
+};
 
-static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
+JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
 {
-    JSFloatEnv *fe;
-    fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
-    if (!fe)
+    JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
+    if (JS_IsException(obj))
         return JS_EXCEPTION;
-    switch(magic) {
-    case FE_PREC:
-        return JS_NewInt64(ctx, fe->prec);
-    case FE_EXP:
-        return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
-    case FE_RNDMODE:
-        return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
-    case FE_SUBNORMAL:
-        return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL);
-    default:
-        return JS_NewBool(ctx, fe->status & magic);
-    }
+    JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
+    return obj;
 }
 
-static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
+void JS_AddIntrinsicDate(JSContext *ctx)
 {
-    JSFloatEnv *fe;
-    int b;
-    int64_t prec;
+    JSValueConst obj;
 
-    fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
-    if (!fe)
-        return JS_EXCEPTION;
-    switch(magic) {
-    case FE_PREC:
-        if (JS_ToInt64Sat(ctx, &prec, val))
-            return JS_EXCEPTION;
-        if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
-            return JS_ThrowRangeError(ctx, "invalid precision");
-        fe->prec = prec;
-        break;
-    case FE_EXP:
-        if (JS_ToInt32Sat(ctx, &b, val))
-            return JS_EXCEPTION;
-        if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
-            return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
-        fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
-            bf_set_exp_bits(b);
-        break;
-    case FE_RNDMODE:
-        b = bigfloat_get_rnd_mode(ctx, val);
-        if (b < 0)
-            return JS_EXCEPTION;
-        fe->flags = (fe->flags & ~BF_RND_MASK) | b;
-        break;
-    case FE_SUBNORMAL:
-        b = JS_ToBool(ctx, val);
-        fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
-        break;
-    default:
-        b = JS_ToBool(ctx, val);
-        fe->status = (fe->status & ~magic) & ((-b) & magic);
-        break;
-    }
-    return JS_UNDEFINED;
+    /* Date */
+    ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
+                               countof(js_date_proto_funcs));
+    obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
+                                   ctx->class_proto[JS_CLASS_DATE]);
+    JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
 }
 
-static JSValue js_float_env_clearStatus(JSContext *ctx,
-                                        JSValueConst this_val,
-                                        int argc, JSValueConst *argv)
+/* eval */
+
+void JS_AddIntrinsicEval(JSContext *ctx)
 {
-    JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
-    if (!fe)
-        return JS_EXCEPTION;
-    fe->status = 0;
-    return JS_UNDEFINED;
+    ctx->eval_internal = __JS_EvalInternal;
 }
 
-static const JSCFunctionListEntry js_float_env_funcs[] = {
-    JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
-    JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
-    JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
-    JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
-    JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
-    JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
-    JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
-    JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
-    JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
-    JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
-    JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
-    JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
-    JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
-    JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
-};
-
-static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
-    JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, FE_PREC ),
-    JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, FE_EXP ),
-    JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, FE_RNDMODE ),
-    JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, FE_SUBNORMAL ),
-    JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, BF_ST_INVALID_OP ),
-    JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
-    JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, BF_ST_OVERFLOW ),
-    JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
-    JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
-                         js_float_env_proto_set_status, BF_ST_INEXACT ),
-    JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
-};
+/* BigInt */
 
-void JS_AddIntrinsicBigFloat(JSContext *ctx)
+static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
 {
-    JSRuntime *rt = ctx->rt;
-    JSValueConst obj1;
+    uint32_t tag;
 
-    rt->bigfloat_ops.to_string = js_bigfloat_to_string;
-    rt->bigfloat_ops.from_string = js_string_to_bigfloat;
-    rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
-    rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
-    rt->bigfloat_ops.compare = js_compare_bigfloat;
-    rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
-    rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
-
-    ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
-    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
-                               js_bigfloat_proto_funcs,
-                               countof(js_bigfloat_proto_funcs));
-    obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
-                                    ctx->class_proto[JS_CLASS_BIG_FLOAT]);
-    JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
-                               countof(js_bigfloat_funcs));
-
-    ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
-    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
-                               js_float_env_proto_funcs,
-                               countof(js_float_env_proto_funcs));
-    obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
-                                        js_float_env_constructor, 1,
-                                        ctx->class_proto[JS_CLASS_FLOAT_ENV]);
-    JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
-                               countof(js_float_env_funcs));
-}
-
-/* BigDecimal */
-
-static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
-                                   BOOL allow_null_or_undefined)
-{
  redo:
-    switch(JS_VALUE_GET_NORM_TAG(val)) {
-    case JS_TAG_BIG_DECIMAL:
-        break;
-    case JS_TAG_NULL:
-        if (!allow_null_or_undefined)
-            goto fail;
-        /* fall thru */
-    case JS_TAG_BOOL:
+    tag = JS_VALUE_GET_NORM_TAG(val);
+    switch(tag) {
     case JS_TAG_INT:
-        {
-            bfdec_t *r;
-            int32_t v = JS_VALUE_GET_INT(val);
-
-            val = JS_NewBigDecimal(ctx);
-            if (JS_IsException(val))
-                break;
-            r = JS_GetBigDecimal(val);
-            if (bfdec_set_si(r, v)) {
-                JS_FreeValue(ctx, val);
-                val = JS_EXCEPTION;
-                break;
-            }
-        }
+    case JS_TAG_BOOL:
+        val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
         break;
-    case JS_TAG_FLOAT64:
+    case JS_TAG_SHORT_BIG_INT:
     case JS_TAG_BIG_INT:
-    case JS_TAG_BIG_FLOAT:
-        val = JS_ToStringFree(ctx, val);
-        if (JS_IsException(val))
-            break;
-        goto redo;
-    case JS_TAG_STRING:
+        break;
+    case JS_TAG_FLOAT64:
         {
-            const char *str, *p;
-            size_t len;
-            int err;
-
-            str = JS_ToCStringLen(ctx, &len, val);
-            JS_FreeValue(ctx, val);
-            if (!str)
-                return JS_EXCEPTION;
-            p = str;
-            p += skip_spaces(p);
-            if ((p - str) == len) {
-                bfdec_t *r;
-                val = JS_NewBigDecimal(ctx);
-                if (JS_IsException(val))
-                    break;
-                r = JS_GetBigDecimal(val);
-                bfdec_set_zero(r, 0);
-                err = 0;
+            double d = JS_VALUE_GET_FLOAT64(val);
+            JSBigInt *r;
+            int res;
+            r = js_bigint_from_float64(ctx, &res, d);
+            if (!r) {
+                if (res == 0) {
+                    val = JS_EXCEPTION;
+                } else if (res == 1) {
+                    val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
+                } else {
+                    val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt");                }
             } else {
-                val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
-                if (JS_IsException(val)) {
-                    JS_FreeCString(ctx, str);
-                    return JS_EXCEPTION;
-                }
-                p += skip_spaces(p);
-                err = ((p - str) != len);
-            }
-            JS_FreeCString(ctx, str);
-            if (err) {
-                JS_FreeValue(ctx, val);
-                return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
+                val = JS_CompactBigInt(ctx, r);
             }
         }
         break;
+    case JS_TAG_STRING:
+        val = JS_StringToBigIntErr(ctx, val);
+        break;
     case JS_TAG_OBJECT:
         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
         if (JS_IsException(val))
             break;
         goto redo;
+    case JS_TAG_NULL:
     case JS_TAG_UNDEFINED:
-        {
-            bfdec_t *r;
-            if (!allow_null_or_undefined)
-                goto fail;
-            val = JS_NewBigDecimal(ctx);
-            if (JS_IsException(val))
-                break;
-            r = JS_GetBigDecimal(val);
-            bfdec_set_nan(r);
-        }
-        break;
     default:
-    fail:
         JS_FreeValue(ctx, val);
-        return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
+        return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
     }
     return val;
 }
 
-static JSValue js_bigdecimal_constructor(JSContext *ctx,
-                                         JSValueConst new_target,
-                                         int argc, JSValueConst *argv)
+static JSValue js_bigint_constructor(JSContext *ctx,
+                                     JSValueConst new_target,
+                                     int argc, JSValueConst *argv)
 {
-    JSValue val;
     if (!JS_IsUndefined(new_target))
         return JS_ThrowTypeError(ctx, "not a constructor");
-    if (argc == 0) {
-        bfdec_t *r;
-        val = JS_NewBigDecimal(ctx);
-        if (JS_IsException(val))
-            return val;
-        r = JS_GetBigDecimal(val);
-        bfdec_set_zero(r, 0);
-    } else {
-        val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
-    }
-    return val;
+    return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
 }
 
-static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
+static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
 {
-    if (JS_IsBigDecimal(this_val))
+    if (JS_IsBigInt(ctx, this_val))
         return JS_DupValue(ctx, this_val);
 
     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
         JSObject *p = JS_VALUE_GET_OBJ(this_val);
-        if (p->class_id == JS_CLASS_BIG_DECIMAL) {
-            if (JS_IsBigDecimal(p->u.object_data))
+        if (p->class_id == JS_CLASS_BIG_INT) {
+            if (JS_IsBigInt(ctx, p->u.object_data))
                 return JS_DupValue(ctx, p->u.object_data);
         }
     }
-    return JS_ThrowTypeError(ctx, "not a bigdecimal");
+    return JS_ThrowTypeError(ctx, "not a BigInt");
 }
 
-static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
-                                      int argc, JSValueConst *argv)
+static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
+                                  int argc, JSValueConst *argv)
 {
     JSValue val;
+    int base;
+    JSValue ret;
 
-    val = js_thisBigDecimalValue(ctx, this_val);
+    val = js_thisBigIntValue(ctx, this_val);
     if (JS_IsException(val))
         return val;
-    return JS_ToStringFree(ctx, val);
-}
-
-static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
-                                   int argc, JSValueConst *argv)
-{
-    return js_thisBigDecimalValue(ctx, this_val);
-}
-
-static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
-{
-    const char *str;
-    size_t size;
-    int rnd_mode;
-
-    str = JS_ToCStringLen(ctx, &size, obj);
-    if (!str)
-        return -1;
-    if (strlen(str) != size)
-        goto invalid_rounding_mode;
-    if (!strcmp(str, "floor")) {
-        rnd_mode = BF_RNDD;
-    } else if (!strcmp(str, "ceiling")) {
-        rnd_mode = BF_RNDU;
-    } else if (!strcmp(str, "down")) {
-        rnd_mode = BF_RNDZ;
-    } else if (!strcmp(str, "up")) {
-        rnd_mode = BF_RNDA;
-    } else if (!strcmp(str, "half-even")) {
-        rnd_mode = BF_RNDN;
-    } else if (!strcmp(str, "half-up")) {
-        rnd_mode = BF_RNDNA;
-    } else {
-    invalid_rounding_mode:
-        JS_FreeCString(ctx, str);
-        JS_ThrowTypeError(ctx, "invalid rounding mode");
-        return -1;
-    }
-    JS_FreeCString(ctx, str);
-    return rnd_mode;
-}
-
-typedef struct {
-    int64_t prec;
-    bf_flags_t flags;
-} BigDecimalEnv;
-
-static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
-                                 JSValueConst obj)
-{
-    JSValue prop;
-    int64_t val;
-    BOOL has_prec;
-    int rnd_mode;
-
-    if (!JS_IsObject(obj)) {
-        JS_ThrowTypeErrorNotAnObject(ctx);
-        return -1;
-    }
-    prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
-    if (JS_IsException(prop))
-        return -1;
-    rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
-    JS_FreeValue(ctx, prop);
-    if (rnd_mode < 0)
-        return -1;
-    fe->flags = rnd_mode;
-
-    prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
-    if (JS_IsException(prop))
-        return -1;
-    has_prec = FALSE;
-    if (!JS_IsUndefined(prop)) {
-        if (JS_ToInt64SatFree(ctx, &val, prop))
-            return -1;
-        if (val < 1 || val > BF_PREC_MAX)
-            goto invalid_precision;
-        fe->prec = val;
-        has_prec = TRUE;
-    }
-
-    prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
-    if (JS_IsException(prop))
-        return -1;
-    if (!JS_IsUndefined(prop)) {
-        if (has_prec) {
-            JS_FreeValue(ctx, prop);
-            JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
-            return -1;
-        }
-        if (JS_ToInt64SatFree(ctx, &val, prop))
-            return -1;
-        if (val < 0 || val > BF_PREC_MAX) {
-        invalid_precision:
-            JS_ThrowTypeError(ctx, "invalid precision");
-            return -1;
-        }
-        fe->prec = val;
-        fe->flags |= BF_FLAG_RADPNT_PREC;
-        has_prec = TRUE;
-    }
-    if (!has_prec) {
-        JS_ThrowTypeError(ctx, "precision must be present");
-        return -1;
-    }
-    return 0;
-}
-
-
-static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
-                                 int argc, JSValueConst *argv, int magic)
-{
-    bfdec_t *a, *b, r_s, *r = &r_s;
-    JSValue op1, op2, res;
-    BigDecimalEnv fe_s, *fe = &fe_s;
-    int op_count, ret;
-
-    if (magic == MATH_OP_SQRT ||
-        magic == MATH_OP_ROUND)
-        op_count = 1;
-    else
-        op_count = 2;
-
-    op1 = JS_ToNumeric(ctx, argv[0]);
-    if (JS_IsException(op1))
-        return op1;
-    a = JS_ToBigDecimal(ctx, op1);
-    if (!a) {
-        JS_FreeValue(ctx, op1);
-        return JS_EXCEPTION;
-    }
-    if (op_count >= 2) {
-        op2 = JS_ToNumeric(ctx, argv[1]);
-        if (JS_IsException(op2)) {
-            JS_FreeValue(ctx, op1);
-            return op2;
-        }
-        b = JS_ToBigDecimal(ctx, op2);
-        if (!b)
-            goto fail;
-    } else {
-        op2 = JS_UNDEFINED;
-        b = NULL;
-    }
-    fe->flags = BF_RNDZ;
-    fe->prec = BF_PREC_INF;
-    if (op_count < argc) {
-        if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
-            goto fail;
-    }
-
-    res = JS_NewBigDecimal(ctx);
-    if (JS_IsException(res)) {
-    fail:
-        JS_FreeValue(ctx, op1);
-        JS_FreeValue(ctx, op2);
-        return JS_EXCEPTION;
-    }
-    r = JS_GetBigDecimal(res);
-    switch (magic) {
-    case MATH_OP_ADD:
-        ret = bfdec_add(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_SUB:
-        ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_MUL:
-        ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_DIV:
-        ret = bfdec_div(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_FMOD:
-        ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
-        break;
-    case MATH_OP_SQRT:
-        ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
-        break;
-    case MATH_OP_ROUND:
-        ret = bfdec_set(r, a);
-        if (!(ret & BF_ST_MEM_ERROR))
-            ret = bfdec_round(r, fe->prec, fe->flags);
-        break;
-    default:
-        abort();
-    }
-    JS_FreeValue(ctx, op1);
-    JS_FreeValue(ctx, op2);
-    ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
-        BF_ST_OVERFLOW;
-    if (ret != 0) {
-        JS_FreeValue(ctx, res);
-        return throw_bf_exception(ctx, ret);
+    if (argc == 0 || JS_IsUndefined(argv[0])) {
+        base = 10;
     } else {
-        return res;
-    }
-}
-
-static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
-                                 int argc, JSValueConst *argv)
-{
-    JSValue val, ret;
-    int64_t f;
-    int rnd_mode;
-
-    val = js_thisBigDecimalValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_ToInt64Sat(ctx, &f, argv[0]))
-        goto fail;
-    if (f < 0 || f > BF_PREC_MAX) {
-        JS_ThrowRangeError(ctx, "invalid number of digits");
-        goto fail;
-    }
-    rnd_mode = BF_RNDNA;
-    if (argc > 1) {
-        rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
-        if (rnd_mode < 0)
+        base = js_get_radix(ctx, argv[0]);
+        if (base < 0)
             goto fail;
     }
-    ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+    ret = js_bigint_to_string1(ctx, val, base);
     JS_FreeValue(ctx, val);
     return ret;
  fail:
@@ -52605,123 +50079,100 @@ static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
     return JS_EXCEPTION;
 }
 
-static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
-                                       int argc, JSValueConst *argv)
+static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
+                                 int argc, JSValueConst *argv)
 {
-    JSValue val, ret;
-    int64_t f;
-    int rnd_mode;
-
-    val = js_thisBigDecimalValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_ToInt64Sat(ctx, &f, argv[0]))
-        goto fail;
-    if (JS_IsUndefined(argv[0])) {
-        ret = js_bigdecimal_to_string1(ctx, val, 0,
-                  BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
-    } else {
-        if (f < 0 || f > BF_PREC_MAX) {
-            JS_ThrowRangeError(ctx, "invalid number of digits");
-            goto fail;
-        }
-        rnd_mode = BF_RNDNA;
-        if (argc > 1) {
-            rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
-            if (rnd_mode < 0)
-                goto fail;
-        }
-        ret = js_bigdecimal_to_string1(ctx, val, f + 1,
-                      rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
-    }
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
+    return js_thisBigIntValue(ctx, this_val);
 }
 
-static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
-                                     int argc, JSValueConst *argv)
+static JSValue js_bigint_asUintN(JSContext *ctx,
+                                  JSValueConst this_val,
+                                  int argc, JSValueConst *argv, int asIntN)
 {
-    JSValue val, ret;
-    int64_t p;
-    int rnd_mode;
-
-    val = js_thisBigDecimalValue(ctx, this_val);
-    if (JS_IsException(val))
-        return val;
-    if (JS_IsUndefined(argv[0])) {
-        return JS_ToStringFree(ctx, val);
-    }
-    if (JS_ToInt64Sat(ctx, &p, argv[0]))
-        goto fail;
-    if (p < 1 || p > BF_PREC_MAX) {
-        JS_ThrowRangeError(ctx, "invalid number of digits");
-        goto fail;
-    }
-    rnd_mode = BF_RNDNA;
-    if (argc > 1) {
-        rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
-        if (rnd_mode < 0)
-            goto fail;
+    uint64_t bits;
+    JSValue res, a;
+    
+    if (JS_ToIndex(ctx, &bits, argv[0]))
+        return JS_EXCEPTION;
+    a = JS_ToBigInt(ctx, argv[1]);
+    if (JS_IsException(a))
+        return JS_EXCEPTION;
+    if (bits == 0) {
+        JS_FreeValue(ctx, a);
+        res = __JS_NewShortBigInt(ctx, 0);
+    } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) {
+        /* fast case */
+        if (bits >= JS_SHORT_BIG_INT_BITS) {
+            res = a;
+        } else {
+            uint64_t v;
+            int shift;
+            shift = 64 - bits;
+            v = JS_VALUE_GET_SHORT_BIG_INT(a);
+            v = v << shift;
+            if (asIntN)
+                v = (int64_t)v >> shift;
+            else
+                v = v >> shift;
+            res = __JS_NewShortBigInt(ctx, v);
+        }
+    } else {
+        JSBigInt *r, *p = JS_VALUE_GET_PTR(a);
+        if (bits >= p->len * JS_LIMB_BITS) {
+            res = a;
+        } else {
+            int len, shift, i;
+            js_limb_t v;
+            len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS;
+            r = js_bigint_new(ctx, len);
+            if (!r) {
+                JS_FreeValue(ctx, a);
+                return JS_EXCEPTION;
+            }
+            r->len = len;
+            for(i = 0; i < len - 1; i++)
+                r->tab[i] = p->tab[i];
+            shift = (-bits) & (JS_LIMB_BITS - 1);
+            /* 0 <= shift <= JS_LIMB_BITS - 1 */
+            v = p->tab[len - 1] << shift;
+            if (asIntN)
+                v = (js_slimb_t)v >> shift;
+            else
+                v = v >> shift;
+            r->tab[len - 1] = v;
+            r = js_bigint_normalize(ctx, r);
+            JS_FreeValue(ctx, a);
+            res = JS_CompactBigInt(ctx, r);
+        }
     }
-    ret = js_bigdecimal_to_string1(ctx, val, p,
-                                   rnd_mode | BF_FTOA_FORMAT_FIXED);
-    JS_FreeValue(ctx, val);
-    return ret;
- fail:
-    JS_FreeValue(ctx, val);
-    return JS_EXCEPTION;
+    return res;
 }
 
-static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
-    JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
-    JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
-    JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
-    JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
-    JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
+static const JSCFunctionListEntry js_bigint_funcs[] = {
+    JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
+    JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
 };
 
-static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
-    JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
-    JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
-    JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
-    JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
-    JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
-    JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
-    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
+static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
+    JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
+    JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
 };
 
-void JS_AddIntrinsicBigDecimal(JSContext *ctx)
+void JS_AddIntrinsicBigInt(JSContext *ctx)
 {
-    JSRuntime *rt = ctx->rt;
     JSValueConst obj1;
 
-    rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
-    rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
-    rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
-    rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
-    rt->bigdecimal_ops.compare = js_compare_bigdecimal;
-
-    ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
-    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
-                               js_bigdecimal_proto_funcs,
-                               countof(js_bigdecimal_proto_funcs));
-    obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
-                                    js_bigdecimal_constructor, 1,
-                                    ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
-    JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
-                               countof(js_bigdecimal_funcs));
-}
-
-void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
-{
-    ctx->bignum_ext = enable;
+    ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+                               js_bigint_proto_funcs,
+                               countof(js_bigint_proto_funcs));
+    obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
+                                    ctx->class_proto[JS_CLASS_BIG_INT]);
+    JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
+                               countof(js_bigint_funcs));
 }
 
-#endif /* CONFIG_BIGNUM */
-
 static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
     "EvalError", "RangeError", "ReferenceError",
     "SyntaxError", "TypeError", "URIError",
@@ -54141,18 +51592,33 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
             v64 = d;
             is_int = (v64 == d);
         }
-    } else if (tag == JS_TAG_BIG_INT) {
-        JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
-
+    } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) {
+        JSBigIntBuf buf1;
+        JSBigInt *p1;
+        int sz = (64 / JS_LIMB_BITS);
+        if (tag == JS_TAG_SHORT_BIG_INT)
+            p1 = js_bigint_set_short(&buf1, argv[0]);
+        else
+            p1 = JS_VALUE_GET_PTR(argv[0]);
+        
         if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
-            if (bf_get_int64(&v64, &p1->num, 0) != 0)
-                goto done;
+            if (p1->len > sz)
+                goto done; /* does not fit an int64 : cannot be found */
         } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
-            if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
+            if (js_bigint_sign(p1))
+                goto done; /* v < 0 */
+            if (p1->len <= sz) {
+                /* OK */
+            } else if (p1->len == sz + 1 && p1->tab[sz] == 0) {
+                /* 2^63 <= v <= 2^64-1 */
+            } else {
                 goto done;
+            }
         } else {
             goto done;
         }
+        if (JS_ToBigInt64(ctx, &v64, argv[0]))
+            goto exception;
         d = 0;
         is_bigint = 1;
     } else {
@@ -54273,15 +51739,12 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
         }
         break;
     case JS_CLASS_BIG_INT64_ARRAY:
-        if (is_bigint || (is_math_mode(ctx) && is_int &&
-                          v64 >= -MAX_SAFE_INTEGER &&
-                          v64 <= MAX_SAFE_INTEGER)) {
+        if (is_bigint) {
             goto scan64;
         }
         break;
     case JS_CLASS_BIG_UINT64_ARRAY:
-        if (is_bigint || (is_math_mode(ctx) && is_int &&
-                          v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
+        if (is_bigint) {
             const uint64_t *pv;
             uint64_t v;
         scan64:
@@ -55701,7 +53164,7 @@ static JSValue js_atomics_store(JSContext *ctx,
         return JS_EXCEPTION;
     if (size_log2 == 3) {
         int64_t v64;
-        ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
+        ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
         if (JS_IsException(ret))
             return ret;
         if (JS_ToBigInt64(ctx, &v64, ret)) {
index edc7b47b328dede999b60659f060fb08e5c7e4f9..e908885cf207316a6f13f7a6dd1f30f989ae9f62 100644 (file)
--- a/quickjs.h
+++ b/quickjs.h
@@ -64,6 +64,14 @@ typedef uint32_t JSAtom;
 #define JS_NAN_BOXING
 #endif
 
+#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
+#define JS_LIMB_BITS 64
+#else
+#define JS_LIMB_BITS 32
+#endif
+
+#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS
+    
 enum {
     /* all tags with a reference count are negative */
     JS_TAG_FIRST       = -11, /* first negative tag */
@@ -83,7 +91,8 @@ enum {
     JS_TAG_UNINITIALIZED = 4,
     JS_TAG_CATCH_OFFSET = 5,
     JS_TAG_EXCEPTION   = 6,
-    JS_TAG_FLOAT64     = 7,
+    JS_TAG_SHORT_BIG_INT = 7,
+    JS_TAG_FLOAT64     = 8,
     /* any larger tag is FLOAT64 if JS_NAN_BOXING */
 };
 
@@ -108,6 +117,7 @@ typedef const struct __JSValue *JSValueConst;
 #define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
 #define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
 #define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
+#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v)
 #define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
 
 #define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
@@ -127,6 +137,11 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
     return 0;
 }
 
+static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
+{
+    return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
+}
+
 #elif defined(JS_NAN_BOXING)
 
 typedef uint64_t JSValue;
@@ -136,6 +151,7 @@ typedef uint64_t JSValue;
 #define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
 #define JS_VALUE_GET_INT(v) (int)(v)
 #define JS_VALUE_GET_BOOL(v) (int)(v)
+#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v)
 #define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
 
 #define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
@@ -192,12 +208,22 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
     return tag == (JS_NAN >> 32);
 }
 
+static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
+{
+    return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
+}
+
 #else /* !JS_NAN_BOXING */
 
 typedef union JSValueUnion {
     int32_t int32;
     double float64;
     void *ptr;
+#if JS_SHORT_BIG_INT_BITS == 32
+    int32_t short_big_int;
+#else
+    int64_t short_big_int;
+#endif
 } JSValueUnion;
 
 typedef struct JSValue {
@@ -213,6 +239,7 @@ typedef struct JSValue {
 #define JS_VALUE_GET_INT(v) ((v).u.int32)
 #define JS_VALUE_GET_BOOL(v) ((v).u.int32)
 #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
+#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int)
 #define JS_VALUE_GET_PTR(v) ((v).u.ptr)
 
 #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
@@ -242,6 +269,14 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
     return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
 }
 
+static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d)
+{
+    JSValue v;
+    v.tag = JS_TAG_SHORT_BIG_INT;
+    v.u.short_big_int = d;
+    return v;
+}
+
 #endif /* !JS_NAN_BOXING */
 
 #define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
@@ -576,7 +611,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v)
 static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
 {
     int tag = JS_VALUE_GET_TAG(v);
-    return tag == JS_TAG_BIG_INT;
+    return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT;
 }
 
 static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
index 63790b6e8e8876621dc83c70d2de27300cfedb08..871770e41d88295c3ad80223691c2466410916a7 100644 (file)
@@ -687,29 +687,6 @@ function float_arith(n)
     return n * 1000;
 }
 
-function bigfloat_arith(n)
-{
-    var i, j, sum, a, incr, a0;
-    global_res = 0;
-    a0 = BigFloat("0.1");
-    incr = BigFloat("1.1");
-    for(j = 0; j < n; j++) {
-        sum = 0;
-        a = a0;
-        for(i = 0; i < 1000; i++) {
-            sum += a * a;
-            a += incr;
-        }
-        global_res += sum;
-    }
-    return n * 1000;
-}
-
-function float256_arith(n)
-{
-    return BigFloatEnv.setPrec(bigfloat_arith.bind(null, n), 237, 19);
-}
-
 function bigint_arith(n, bits)
 {
     var i, j, sum, a, incr, a0, sum0;
@@ -728,6 +705,11 @@ function bigint_arith(n, bits)
     return n * 1000;
 }
 
+function bigint32_arith(n)
+{
+    return bigint_arith(n, 32);
+}
+
 function bigint64_arith(n)
 {
     return bigint_arith(n, 64);
@@ -1231,13 +1213,10 @@ function main(argc, argv, g)
 
     if (typeof BigInt === "function") {
         /* BigInt test */
+        test_list.push(bigint32_arith);
         test_list.push(bigint64_arith);
         test_list.push(bigint256_arith);
     }
-    if (typeof BigFloat === "function") {
-        /* BigFloat test */
-        test_list.push(float256_arith);
-    }
     test_list.push(sort_bench);
 
     for (i = 1; i < argc;) {
diff --git a/tests/test_bigfloat.js b/tests/test_bigfloat.js
deleted file mode 100644 (file)
index c35fb72..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-"use strict";
-
-function assert(actual, expected, message) {
-    if (arguments.length == 1)
-        expected = true;
-
-    if (actual === expected)
-        return;
-
-    if (actual !== null && expected !== null
-    &&  typeof actual == 'object' && typeof expected == 'object'
-    &&  actual.toString() === expected.toString())
-        return;
-
-    throw Error("assertion failed: got |" + actual + "|" +
-                ", expected |" + expected + "|" +
-                (message ? " (" + message + ")" : ""));
-}
-
-function assertThrows(err, func)
-{
-    var ex;
-    ex = false;
-    try {
-        func();
-    } catch(e) {
-        ex = true;
-        assert(e instanceof err);
-    }
-    assert(ex, true, "exception expected");
-}
-
-// load more elaborate version of assert if available
-try { __loadScript("test_assert.js"); } catch(e) {}
-
-/*----------------*/
-
-/* a must be < b */
-function test_less(a, b)
-{
-    assert(a < b);
-    assert(!(b < a));
-    assert(a <= b);
-    assert(!(b <= a));
-    assert(b > a);
-    assert(!(a > b));
-    assert(b >= a);
-    assert(!(a >= b));
-    assert(a != b);
-    assert(!(a == b));
-}
-
-/* a must be numerically equal to b */
-function test_eq(a, b)
-{
-    assert(a == b);
-    assert(b == a);
-    assert(!(a != b));
-    assert(!(b != a));
-    assert(a <= b);
-    assert(b <= a);
-    assert(!(a < b));
-    assert(a >= b);
-    assert(b >= a);
-    assert(!(a > b));
-}
-
-function test_divrem(div1, a, b, q)
-{
-    var div, divrem, t;
-    div = BigInt[div1];
-    divrem = BigInt[div1 + "rem"];
-    assert(div(a, b) == q);
-    t = divrem(a, b);
-    assert(t[0] == q);
-    assert(a == b * q + t[1]);
-}
-
-function test_idiv1(div, a, b, r)
-{
-    test_divrem(div, a, b, r[0]);
-    test_divrem(div, -a, b, r[1]);
-    test_divrem(div, a, -b, r[2]);
-    test_divrem(div, -a, -b, r[3]);
-}
-
-/* QuickJS BigInt extensions */
-function test_bigint_ext()
-{
-    var r;
-    assert(BigInt.floorLog2(0n) === -1n);
-    assert(BigInt.floorLog2(7n) === 2n);
-
-    assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n);
-    r = BigInt.sqrtrem(0xffffffc000000000000000n);
-    assert(r[0] === 17592185913343n);
-    assert(r[1] === 35167191957503n);
-
-    test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]);
-    test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]);
-    test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]);
-    test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]);
-}
-
-function test_bigfloat()
-{
-    var e, a, b, sqrt2;
-
-    assert(typeof 1n === "bigint");
-    assert(typeof 1l === "bigfloat");
-    assert(1 == 1.0l);
-    assert(1 !== 1.0l);
-
-    test_less(2l, 3l);
-    test_eq(3l, 3l);
-
-    test_less(2, 3l);
-    test_eq(3, 3l);
-
-    test_less(2.1, 3l);
-    test_eq(Math.sqrt(9), 3l);
-
-    test_less(2n, 3l);
-    test_eq(3n, 3l);
-
-    e = new BigFloatEnv(128);
-    assert(e.prec == 128);
-    a = BigFloat.sqrt(2l, e);
-    assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
-    assert(e.inexact === true);
-    assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l);
-
-    b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
-    assert(a === b);
-
-    assert(BigFloat.isNaN(BigFloat(NaN)));
-    assert(BigFloat.isFinite(1l));
-    assert(!BigFloat.isFinite(1l/0l));
-
-    assert(BigFloat.abs(-3l) === 3l);
-    assert(BigFloat.sign(-3l) === -1l);
-
-    assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l);
-    assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l);
-    assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l);
-
-    assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l);
-    assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l);
-    assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l);
-
-    assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l);
-    assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l);
-    assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l);
-    assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l);
-
-    assert(BigFloat.floor(2.5l) === 2l);
-    assert(BigFloat.ceil(2.5l) === 3l);
-    assert(BigFloat.trunc(-2.5l) === -2l);
-    assert(BigFloat.round(2.5l) === 3l);
-
-    assert(BigFloat.fmod(3l,2l) === 1l);
-    assert(BigFloat.remainder(3l,2l) === -1l);
-
-    /* string conversion */
-    assert((1234.125l).toString(), "1234.125");
-    assert((1234.125l).toFixed(2), "1234.13");
-    assert((1234.125l).toFixed(2, "down"), "1234.12");
-    assert((1234.125l).toExponential(), "1.234125e+3");
-    assert((1234.125l).toExponential(5), "1.23413e+3");
-    assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3");
-    assert((1234.125l).toPrecision(6), "1234.13");
-    assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12");
-
-    /* string conversion with binary base */
-    assert((0x123.438l).toString(16), "123.438");
-    assert((0x323.438l).toString(16), "323.438");
-    assert((0x723.438l).toString(16), "723.438");
-    assert((0xf23.438l).toString(16), "f23.438");
-    assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44");
-    assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44");
-    assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44");
-    assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44");
-    assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044");
-    assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0");
-    assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44");
-    assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43");
-    assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44");
-    assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44");
-    assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44");
-    assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8");
-}
-
-function test_bigdecimal()
-{
-    assert(1m === 1m);
-    assert(1m !== 2m);
-    test_less(1m, 2m);
-    test_eq(2m, 2m);
-
-    test_less(1, 2m);
-    test_eq(2, 2m);
-
-    test_less(1.1, 2m);
-    test_eq(Math.sqrt(4), 2m);
-
-    test_less(2n, 3m);
-    test_eq(3n, 3m);
-
-    assert(BigDecimal("1234.1") === 1234.1m);
-    assert(BigDecimal("    1234.1") === 1234.1m);
-    assert(BigDecimal("    1234.1  ") === 1234.1m);
-
-    assert(BigDecimal(0.1) === 0.1m);
-    assert(BigDecimal(123) === 123m);
-    assert(BigDecimal(true) === 1m);
-
-    assert(123m + 1m === 124m);
-    assert(123m - 1m === 122m);
-
-    assert(3.2m * 3m === 9.6m);
-    assert(10m / 2m === 5m);
-    assertThrows(RangeError, () => { 10m / 3m } );
-
-    assert(10m % 3m === 1m);
-    assert(-10m % 3m === -1m);
-
-    assert(1234.5m ** 3m === 1881365963.625m);
-    assertThrows(RangeError, () => { 2m ** 3.1m } );
-    assertThrows(RangeError, () => { 2m ** -3m } );
-
-    assert(BigDecimal.sqrt(2m,
-                       { roundingMode: "half-even",
-                         maximumSignificantDigits: 4 }) === 1.414m);
-    assert(BigDecimal.sqrt(101m,
-                       { roundingMode: "half-even",
-                         maximumFractionDigits: 3 }) === 10.050m);
-    assert(BigDecimal.sqrt(0.002m,
-                       { roundingMode: "half-even",
-                         maximumFractionDigits: 3 }) === 0.045m);
-
-    assert(BigDecimal.round(3.14159m,
-                       { roundingMode: "half-even",
-                         maximumFractionDigits: 3 }) === 3.142m);
-
-    assert(BigDecimal.add(3.14159m, 0.31212m,
-                          { roundingMode: "half-even",
-                            maximumFractionDigits: 2 }) === 3.45m);
-    assert(BigDecimal.sub(3.14159m, 0.31212m,
-                          { roundingMode: "down",
-                            maximumFractionDigits: 2 }) === 2.82m);
-    assert(BigDecimal.mul(3.14159m, 0.31212m,
-                          { roundingMode: "half-even",
-                            maximumFractionDigits: 3 }) === 0.981m);
-    assert(BigDecimal.mod(3.14159m, 0.31211m,
-                          { roundingMode: "half-even",
-                            maximumFractionDigits: 4 }) === 0.0205m);
-    assert(BigDecimal.div(20m, 3m,
-                       { roundingMode: "half-even",
-                         maximumSignificantDigits: 3 }) === 6.67m);
-    assert(BigDecimal.div(20m, 3m,
-                       { roundingMode: "half-even",
-                         maximumFractionDigits: 50 }) ===
-           6.66666666666666666666666666666666666666666666666667m);
-
-    /* string conversion */
-    assert((1234.125m).toString(), "1234.125");
-    assert((1234.125m).toFixed(2), "1234.13");
-    assert((1234.125m).toFixed(2, "down"), "1234.12");
-    assert((1234.125m).toExponential(), "1.234125e+3");
-    assert((1234.125m).toExponential(5), "1.23413e+3");
-    assert((1234.125m).toExponential(5, "down"), "1.23412e+3");
-    assert((1234.125m).toPrecision(6), "1234.13");
-    assert((1234.125m).toPrecision(6, "down"), "1234.12");
-    assert((-1234.125m).toPrecision(6, "floor"), "-1234.13");
-}
-
-test_bigint_ext();
-test_bigfloat();
-test_bigdecimal();
diff --git a/tests/test_bigint.js b/tests/test_bigint.js
new file mode 100644 (file)
index 0000000..a0d028c
--- /dev/null
@@ -0,0 +1,249 @@
+"use strict";
+
+function assert(actual, expected, message) {
+    if (arguments.length == 1)
+        expected = true;
+
+    if (actual === expected)
+        return;
+
+    if (actual !== null && expected !== null
+    &&  typeof actual == 'object' && typeof expected == 'object'
+    &&  actual.toString() === expected.toString())
+        return;
+
+    throw Error("assertion failed: got |" + actual + "|" +
+                ", expected |" + expected + "|" +
+                (message ? " (" + message + ")" : ""));
+}
+
+function assertThrows(err, func)
+{
+    var ex;
+    ex = false;
+    try {
+        func();
+    } catch(e) {
+        ex = true;
+        assert(e instanceof err);
+    }
+    assert(ex, true, "exception expected");
+}
+
+// load more elaborate version of assert if available
+try { __loadScript("test_assert.js"); } catch(e) {}
+
+/*----------------*/
+
+function bigint_pow(a, n)
+{
+    var r, i;
+    r = 1n;
+    for(i = 0n; i < n; i++)
+        r *= a;
+    return r;
+}
+
+/* a must be < b */
+function test_less(a, b)
+{
+    assert(a < b);
+    assert(!(b < a));
+    assert(a <= b);
+    assert(!(b <= a));
+    assert(b > a);
+    assert(!(a > b));
+    assert(b >= a);
+    assert(!(a >= b));
+    assert(a != b);
+    assert(!(a == b));
+}
+
+/* a must be numerically equal to b */
+function test_eq(a, b)
+{
+    assert(a == b);
+    assert(b == a);
+    assert(!(a != b));
+    assert(!(b != a));
+    assert(a <= b);
+    assert(b <= a);
+    assert(!(a < b));
+    assert(a >= b);
+    assert(b >= a);
+    assert(!(a > b));
+}
+
+function test_bigint1()
+{
+    var a, r;
+
+    test_less(2n, 3n);
+    test_eq(3n, 3n);
+
+    test_less(2, 3n);
+    test_eq(3, 3n);
+
+    test_less(2.1, 3n);
+    test_eq(Math.sqrt(4), 2n);
+
+    a = bigint_pow(3n, 100n);
+    assert((a - 1n) != a);
+    assert(a == 515377520732011331036461129765621272702107522001n);
+    assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
+
+    r = 1n << 31n;
+    assert(r, 2147483648n, "1 << 31n === 2147483648n");
+
+    r = 1n << 32n;
+    assert(r, 4294967296n, "1 << 32n === 4294967296n");
+}
+
+function test_bigint2()
+{
+    assert(BigInt(""), 0n);
+    assert(BigInt("  123"), 123n);
+    assert(BigInt("  123   "), 123n);
+    assertThrows(SyntaxError, () => { BigInt("+") } );
+    assertThrows(SyntaxError, () => { BigInt("-") } );
+    assertThrows(SyntaxError, () => { BigInt("\x00a") } );
+    assertThrows(SyntaxError, () => { BigInt("  123  r") } );
+}
+
+function test_bigint3()
+{
+    assert(Number(0xffffffffffffffffn), 18446744073709552000);
+    assert(Number(-0xffffffffffffffffn), -18446744073709552000);
+    assert(100000000000000000000n == 1e20, true);
+    assert(100000000000000000001n == 1e20, false);
+    assert((1n << 100n).toString(10), "1267650600228229401496703205376");
+    assert((-1n << 100n).toString(36), "-3ewfdnca0n6ld1ggvfgg");
+    assert((1n << 100n).toString(8), "2000000000000000000000000000000000");
+
+    assert(0x5a4653ca673768565b41f775n << 78n, 8443945299673273647701379149826607537748959488376832n);
+    assert(-0x5a4653ca673768565b41f775n << 78n, -8443945299673273647701379149826607537748959488376832n);
+    assert(0x5a4653ca673768565b41f775n >> 78n, 92441n);
+    assert(-0x5a4653ca673768565b41f775n >> 78n, -92442n);
+
+    assert(~0x5a653ca6n, -1516584103n);
+    assert(0x5a463ca6n | 0x67376856n, 2138537206n);
+    assert(0x5a463ca6n & 0x67376856n, 1107699718n);
+    assert(0x5a463ca6n ^ 0x67376856n, 1030837488n);
+
+    assert(3213213213213213432453243n / 123434343439n, 26031760073331n);
+    assert(-3213213213213213432453243n / 123434343439n, -26031760073331n);
+    assert(-3213213213213213432453243n % -123434343439n, -26953727934n);
+    assert(3213213213213213432453243n % 123434343439n, 26953727934n);
+
+    assert((-2n) ** 127n, -170141183460469231731687303715884105728n);
+    assert((2n) ** 127n, 170141183460469231731687303715884105728n);
+    assert((-256n) ** 11n, -309485009821345068724781056n);
+    assert((7n) ** 20n, 79792266297612001n);
+}
+
+/* pi computation */
+
+/* return floor(log2(a)) for a > 0 and 0 for a = 0 */
+function floor_log2(a)
+{
+    var k_max, a1, k, i;
+    k_max = 0n;
+    while ((a >> (2n ** k_max)) != 0n) {
+        k_max++;
+    }
+    k = 0n;
+    a1 = a;
+    for(i = k_max - 1n; i >= 0n; i--) {
+        a1 = a >> (2n ** i);
+        if (a1 != 0n) {
+            a = a1;
+            k |= (1n << i);
+        }
+    }
+    return k;
+}
+
+/* return ceil(log2(a)) for a > 0 */
+function ceil_log2(a)
+{
+    return floor_log2(a - 1n) + 1n;
+}
+
+/* return floor(sqrt(a)) (not efficient but simple) */
+function int_sqrt(a)
+{
+    var l, u, s;
+    if (a == 0n)
+        return a;
+    l = ceil_log2(a);
+    u = 1n << ((l + 1n) / 2n);
+    /* u >= floor(sqrt(a)) */
+    for(;;) {
+        s = u;
+        u = ((a / s) + s) / 2n;
+        if (u >= s)
+            break;
+    }
+    return s;
+}
+
+/* return pi * 2**prec */
+function calc_pi(prec) {
+    const CHUD_A = 13591409n;
+    const CHUD_B = 545140134n;
+    const CHUD_C = 640320n;
+    const CHUD_C3 = 10939058860032000n; /* C^3/24 */
+    const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
+
+    /* return [P, Q, G] */
+    function chud_bs(a, b, need_G) {
+        var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
+        if (a == (b - 1n)) {
+            G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
+            P = G * (CHUD_B * b + CHUD_A);
+            if (b & 1n)
+                P = -P;
+            Q = b * b * b * CHUD_C3;
+        } else {
+            c = (a + b) >> 1n;
+            [P1, Q1, G1] = chud_bs(a, c, true);
+            [P2, Q2, G2] = chud_bs(c, b, need_G);
+            P = P1 * Q2 + P2 * G1;
+            Q = Q1 * Q2;
+            if (need_G)
+                G = G1 * G2;
+            else
+                G = 0n;
+        }
+        return [P, Q, G];
+    }
+
+    var n, P, Q, G;
+    /* number of serie terms */
+    n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
+    [P, Q, G] = chud_bs(0n, n, false);
+    Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
+    G = int_sqrt(CHUD_C << (2n * prec));
+    return (Q * G) >> prec;
+}
+
+function compute_pi(n_digits) {
+    var r, n_digits, n_bits, out;
+    /* we add more bits to reduce the probability of bad rounding for
+      the last digits */
+    n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
+    r = calc_pi(n_bits);
+    r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
+    out = r.toString();
+    return out[0] + "." + out.slice(1);
+}
+
+function test_pi()
+{
+    assert(compute_pi(2000), "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009");
+}
+
+test_bigint1();
+test_bigint2();
+test_bigint3();
+test_pi();
diff --git a/tests/test_bignum.js b/tests/test_bignum.js
deleted file mode 100644 (file)
index 1520d82..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-"use strict";
-
-function assert(actual, expected, message) {
-    if (arguments.length == 1)
-        expected = true;
-
-    if (actual === expected)
-        return;
-
-    if (actual !== null && expected !== null
-    &&  typeof actual == 'object' && typeof expected == 'object'
-    &&  actual.toString() === expected.toString())
-        return;
-
-    throw Error("assertion failed: got |" + actual + "|" +
-                ", expected |" + expected + "|" +
-                (message ? " (" + message + ")" : ""));
-}
-
-function assertThrows(err, func)
-{
-    var ex;
-    ex = false;
-    try {
-        func();
-    } catch(e) {
-        ex = true;
-        assert(e instanceof err);
-    }
-    assert(ex, true, "exception expected");
-}
-
-// load more elaborate version of assert if available
-try { __loadScript("test_assert.js"); } catch(e) {}
-
-/*----------------*/
-
-function bigint_pow(a, n)
-{
-    var r, i;
-    r = 1n;
-    for(i = 0n; i < n; i++)
-        r *= a;
-    return r;
-}
-
-/* a must be < b */
-function test_less(a, b)
-{
-    assert(a < b);
-    assert(!(b < a));
-    assert(a <= b);
-    assert(!(b <= a));
-    assert(b > a);
-    assert(!(a > b));
-    assert(b >= a);
-    assert(!(a >= b));
-    assert(a != b);
-    assert(!(a == b));
-}
-
-/* a must be numerically equal to b */
-function test_eq(a, b)
-{
-    assert(a == b);
-    assert(b == a);
-    assert(!(a != b));
-    assert(!(b != a));
-    assert(a <= b);
-    assert(b <= a);
-    assert(!(a < b));
-    assert(a >= b);
-    assert(b >= a);
-    assert(!(a > b));
-}
-
-function test_bigint1()
-{
-    var a, r;
-
-    test_less(2n, 3n);
-    test_eq(3n, 3n);
-
-    test_less(2, 3n);
-    test_eq(3, 3n);
-
-    test_less(2.1, 3n);
-    test_eq(Math.sqrt(4), 2n);
-
-    a = bigint_pow(3n, 100n);
-    assert((a - 1n) != a);
-    assert(a == 515377520732011331036461129765621272702107522001n);
-    assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
-
-    r = 1n << 31n;
-    assert(r, 2147483648n, "1 << 31n === 2147483648n");
-
-    r = 1n << 32n;
-    assert(r, 4294967296n, "1 << 32n === 4294967296n");
-}
-
-function test_bigint2()
-{
-    assert(BigInt(""), 0n);
-    assert(BigInt("  123"), 123n);
-    assert(BigInt("  123   "), 123n);
-    assertThrows(SyntaxError, () => { BigInt("+") } );
-    assertThrows(SyntaxError, () => { BigInt("-") } );
-    assertThrows(SyntaxError, () => { BigInt("\x00a") } );
-    assertThrows(SyntaxError, () => { BigInt("  123  r") } );
-}
-
-test_bigint1();
-test_bigint2();
diff --git a/tests/test_op_overloading.js b/tests/test_op_overloading.js
deleted file mode 100644 (file)
index 269abb2..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-"use strict";
-
-function assert(actual, expected, message) {
-    if (arguments.length == 1)
-        expected = true;
-
-    if (actual === expected)
-        return;
-
-    if (actual !== null && expected !== null
-    &&  typeof actual == 'object' && typeof expected == 'object'
-    &&  actual.toString() === expected.toString())
-        return;
-
-    throw Error("assertion failed: got |" + actual + "|" +
-                ", expected |" + expected + "|" +
-                (message ? " (" + message + ")" : ""));
-}
-
-/* operators overloading with Operators.create() */
-function test_operators_create() {
-    class Vec2
-    {
-        constructor(x, y) {
-            this.x = x;
-            this.y = y;
-        }
-        static mul_scalar(p1, a) {
-            var r = new Vec2();
-            r.x = p1.x * a;
-            r.y = p1.y * a;
-            return r;
-        }
-        toString() {
-            return "Vec2(" + this.x + "," + this.y + ")";
-        }
-    }
-
-    Vec2.prototype[Symbol.operatorSet] = Operators.create(
-    {
-        "+"(p1, p2) {
-            var r = new Vec2();
-            r.x = p1.x + p2.x;
-            r.y = p1.y + p2.y;
-            return r;
-        },
-        "-"(p1, p2) {
-            var r = new Vec2();
-            r.x = p1.x - p2.x;
-            r.y = p1.y - p2.y;
-            return r;
-        },
-        "=="(a, b) {
-            return a.x == b.x && a.y == b.y;
-        },
-        "<"(a, b) {
-            var r;
-            /* lexicographic order */
-            if (a.x == b.x)
-                r = (a.y < b.y);
-            else
-                r = (a.x < b.x);
-            return r;
-        },
-        "++"(a) {
-            var r = new Vec2();
-            r.x = a.x + 1;
-            r.y = a.y + 1;
-            return r;
-        }
-    },
-    {
-        left: Number,
-        "*"(a, b) {
-            return Vec2.mul_scalar(b, a);
-        }
-    },
-    {
-        right: Number,
-        "*"(a, b) {
-            return Vec2.mul_scalar(a, b);
-        }
-    });
-
-    var a = new Vec2(1, 2);
-    var b = new Vec2(3, 4);
-    var r;
-
-    r = a * 2 + 3 * b;
-    assert(r.x === 11 && r.y === 16);
-    assert(a == a, true);
-    assert(a == b, false);
-    assert(a != a, false);
-    assert(a < b, true);
-    assert(a <= b, true);
-    assert(b < a, false);
-    assert(b <= a, false);
-    assert(a <= a, true);
-    assert(a >= a, true);
-    a++;
-    assert(a.x === 2 && a.y === 3);
-    r = ++a;
-    assert(a.x === 3 && a.y === 4);
-    assert(r === a);
-}
-
-/* operators overloading thru inheritance */
-function test_operators()
-{
-    var Vec2;
-
-    function mul_scalar(p1, a) {
-        var r = new Vec2();
-        r.x = p1.x * a;
-        r.y = p1.y * a;
-        return r;
-    }
-
-    var vec2_ops = Operators({
-        "+"(p1, p2) {
-            var r = new Vec2();
-            r.x = p1.x + p2.x;
-            r.y = p1.y + p2.y;
-            return r;
-        },
-        "-"(p1, p2) {
-            var r = new Vec2();
-            r.x = p1.x - p2.x;
-            r.y = p1.y - p2.y;
-            return r;
-        },
-        "=="(a, b) {
-            return a.x == b.x && a.y == b.y;
-        },
-        "<"(a, b) {
-            var r;
-            /* lexicographic order */
-            if (a.x == b.x)
-                r = (a.y < b.y);
-            else
-                r = (a.x < b.x);
-            return r;
-        },
-        "++"(a) {
-            var r = new Vec2();
-            r.x = a.x + 1;
-            r.y = a.y + 1;
-            return r;
-        }
-    },
-    {
-        left: Number,
-        "*"(a, b) {
-            return mul_scalar(b, a);
-        }
-    },
-    {
-        right: Number,
-        "*"(a, b) {
-            return mul_scalar(a, b);
-        }
-    });
-
-    Vec2 = class Vec2 extends vec2_ops
-    {
-        constructor(x, y) {
-            super();
-            this.x = x;
-            this.y = y;
-        }
-        toString() {
-            return "Vec2(" + this.x + "," + this.y + ")";
-        }
-    }
-
-    var a = new Vec2(1, 2);
-    var b = new Vec2(3, 4);
-    var r;
-
-    r = a * 2 + 3 * b;
-    assert(r.x === 11 && r.y === 16);
-    assert(a == a, true);
-    assert(a == b, false);
-    assert(a != a, false);
-    assert(a < b, true);
-    assert(a <= b, true);
-    assert(b < a, false);
-    assert(b <= a, false);
-    assert(a <= a, true);
-    assert(a >= a, true);
-    a++;
-    assert(a.x === 2 && a.y === 3);
-    r = ++a;
-    assert(a.x === 3 && a.y === 4);
-    assert(r === a);
-}
-
-function test_default_op()
-{
-    assert(Object(1) + 2, 3);
-    assert(Object(1) + true, 2);
-    assert(-Object(1), -1);
-}
-
-test_operators_create();
-test_operators();
-test_default_op();
diff --git a/tests/test_qjscalc.js b/tests/test_qjscalc.js
deleted file mode 100644 (file)
index e97dd31..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-"use math";
-"use strict";
-
-function assert(actual, expected, message) {
-    if (arguments.length == 1)
-        expected = true;
-
-    if (actual === expected)
-        return;
-
-    if (actual !== null && expected !== null
-    &&  typeof actual == 'object' && typeof expected == 'object'
-    &&  actual.toString() === expected.toString())
-        return;
-
-    throw Error("assertion failed: got |" + actual + "|" +
-                ", expected |" + expected + "|" +
-                (message ? " (" + message + ")" : ""));
-}
-
-function assertThrows(err, func)
-{
-    var ex;
-    ex = false;
-    try {
-        func();
-    } catch(e) {
-        ex = true;
-        assert(e instanceof err);
-    }
-    assert(ex, true, "exception expected");
-}
-
-// load more elaborate version of assert if available
-try { __loadScript("test_assert.js"); } catch(e) {}
-
-/*----------------*/
-
-function pow(a, n)
-{
-    var r, i;
-    r = 1;
-    for(i = 0; i < n; i++)
-        r *= a;
-    return r;
-}
-
-function test_integer()
-{
-    var a, r;
-    a = pow(3, 100);
-    assert((a - 1) != a);
-    assert(a == 515377520732011331036461129765621272702107522001);
-    assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1);
-    assert(Integer.isInteger(1) === true);
-    assert(Integer.isInteger(1.0) === false);
-
-    assert(Integer.floorLog2(0) === -1);
-    assert(Integer.floorLog2(7) === 2);
-
-    r = 1 << 31;
-    assert(r, 2147483648, "1 << 31 === 2147483648");
-
-    r = 1 << 32;
-    assert(r, 4294967296, "1 << 32 === 4294967296");
-
-    r = (1 << 31) < 0;
-    assert(r, false, "(1 << 31) < 0 === false");
-
-    assert(typeof 1 === "number");
-    assert(typeof 9007199254740991 === "number");
-    assert(typeof 9007199254740992 === "bigint");
-}
-
-function test_float()
-{
-    assert(typeof 1.0 === "bigfloat");
-    assert(1 == 1.0);
-    assert(1 !== 1.0);
-}
-
-/* jscalc tests */
-
-function test_modulo()
-{
-    var i, p, a, b;
-
-    /* Euclidian modulo operator */
-    assert((-3) % 2 == 1);
-    assert(3 % (-2) == 1);
-
-    p = 101;
-    for(i = 1; i < p; i++) {
-        a = Integer.invmod(i, p);
-        assert(a >= 0 && a < p);
-        assert((i * a) % p == 1);
-    }
-
-    assert(Integer.isPrime(2^107-1));
-    assert(!Integer.isPrime((2^107-1) * (2^89-1)));
-    a = Integer.factor((2^89-1)*2^3*11*13^2*1009);
-    assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]);
-}
-
-function test_fraction()
-{
-    assert((1/3 + 1).toString(), "4/3")
-    assert((2/3)^30, 1073741824/205891132094649);
-    assert(1/3 < 2/3);
-    assert(1/3 < 1);
-    assert(1/3 == 1.0/3);
-    assert(1.0/3 < 2/3);
-}
-
-function test_mod()
-{
-    var a, b, p;
-
-    a = Mod(3, 101);
-    b = Mod(-1, 101);
-    assert((a + b) == Mod(2, 101));
-    assert(a ^ 100 == Mod(1, 101));
-
-    p = 2 ^ 607 - 1; /* mersenne prime */
-    a = Mod(3, p) ^ (p - 1);
-    assert(a == Mod(1, p));
-}
-
-function test_polynomial()
-{
-    var a, b, q, r, t, i;
-    a = (1 + X) ^ 4;
-    assert(a == X^4+4*X^3+6*X^2+4*X+1);
-
-    r = (1 + X);
-    q = (1+X+X^2);
-    b = (1 - X^2);
-    a = q * b + r;
-    t = Polynomial.divrem(a, b);
-    assert(t[0] == q);
-    assert(t[1] == r);
-
-    a = 1 + 2*X + 3*X^2;
-    assert(a.apply(0.1) == 1.23);
-
-    a = 1-2*X^2+2*X^3;
-    assert(deriv(a) == (6*X^2-4*X));
-    assert(deriv(integ(a)) == a);
-
-    a = (X-1)*(X-2)*(X-3)*(X-4)*(X-0.1);
-    r = polroots(a);
-    for(i = 0; i < r.length; i++) {
-        b = abs(a.apply(r[i]));
-        assert(b <= 1e-13);
-    }
-}
-
-function test_poly_mod()
-{
-    var a, p;
-
-    /* modulo using polynomials */
-    p = X^2 + X + 1;
-    a = PolyMod(3+X, p) ^ 10;
-    assert(a == PolyMod(-3725*X-18357, p));
-
-    a = PolyMod(1/X, 1+X^2);
-    assert(a == PolyMod(-X, X^2+1));
-}
-
-function test_rfunc()
-{
-    var a;
-    a = (X+1)/((X+1)*(X-1));
-    assert(a == 1/(X-1));
-    a = (X + 2) / (X - 2);
-    assert(a.apply(1/3) == -7/5);
-
-    assert(deriv((X^2-X+1)/(X-1)) == (X^2-2*X)/(X^2-2*X+1));
-}
-
-function test_series()
-{
-    var a, b;
-    a = 1+X+O(X^5);
-    b = a.inverse();
-    assert(b == 1-X+X^2-X^3+X^4+O(X^5));
-    assert(deriv(b) == -1+2*X-3*X^2+4*X^3+O(X^4));
-    assert(deriv(integ(b)) == b);
-
-    a = Series(1/(1-X), 5);
-    assert(a == 1+X+X^2+X^3+X^4+O(X^5));
-    b = a.apply(0.1);
-    assert(b == 1.1111);
-
-    assert(exp(3*X^2+O(X^10)) == 1+3*X^2+9/2*X^4+9/2*X^6+27/8*X^8+O(X^10));
-    assert(sin(X+O(X^6)) == X-1/6*X^3+1/120*X^5+O(X^6));
-    assert(cos(X+O(X^6)) == 1-1/2*X^2+1/24*X^4+O(X^6));
-    assert(tan(X+O(X^8)) == X+1/3*X^3+2/15*X^5+17/315*X^7+O(X^8));
-        assert((1+X+O(X^6))^(2+X) == 1+2*X+2*X^2+3/2*X^3+5/6*X^4+5/12*X^5+O(X^6));
-}
-
-function test_matrix()
-{
-    var a, b, r;
-    a = [[1, 2],[3, 4]];
-    b = [3, 4];
-    r = a * b;
-    assert(r == [11, 25]);
-    r = (a^-1) * 2;
-    assert(r == [[-4, 2],[3, -1]]);
-
-    assert(norm2([1,2,3]) == 14);
-
-    assert(diag([1,2,3]) == [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 3 ] ]);
-    assert(trans(a) == [ [ 1, 3 ], [ 2, 4 ] ]);
-    assert(trans([1,2,3]) == [[1,2,3]]);
-    assert(trace(a) == 5);
-
-    assert(charpoly(Matrix.hilbert(4)) == X^4-176/105*X^3+3341/12600*X^2-41/23625*X+1/6048000);
-    assert(det(Matrix.hilbert(4)) == 1/6048000);
-
-    a = [[1,2,1],[-2,-3,1],[3,5,0]];
-    assert(rank(a) == 2);
-    assert(ker(a) == [ [ 5 ], [ -3 ], [ 1 ] ]);
-
-    assert(dp([1, 2, 3], [3, -4, -7]) === -26);
-    assert(cp([1, 2, 3], [3, -4, -7]) == [ -2, 16, -10 ]);
-}
-
-function assert_eq(a, ref)
-{
-    assert(abs(a / ref - 1.0) <= 1e-15);
-}
-
-function test_trig()
-{
-    assert_eq(sin(1/2), 0.479425538604203);
-    assert_eq(sin(2+3*I), 9.154499146911428-4.168906959966565*I);
-    assert_eq(cos(2+3*I), -4.189625690968807-9.109227893755337*I);
-    assert_eq((2+0.5*I)^(1.1-0.5*I), 2.494363021357619-0.23076804554558092*I);
-    assert_eq(sqrt(2*I), 1 + I);
-}
-
-test_integer();
-test_float();
-
-test_modulo();
-test_fraction();
-test_mod();
-test_polynomial();
-test_poly_mod();
-test_rfunc();
-test_series();
-test_matrix();
-test_trig();