From 61e8b9442840bf94a2a9b0b872c8b3a197c1eac3 Mon Sep 17 00:00:00 2001 From: Fabrice Bellard Date: Tue, 18 Mar 2025 18:29:10 +0100 Subject: [PATCH] removed bignum support and qjscalc - added optimized BigInt implementation --- Makefile | 43 +- TODO | 1 - cutils.h | 20 + examples/pi_bigdecimal.js | 68 - examples/pi_bigfloat.js | 66 - libbf.c | 8475 ---------------------------------- libbf.h | 535 --- qjs.c | 52 - qjsc.c | 28 +- qjscalc.js | 2657 ----------- quickjs-atom.h | 17 - quickjs-opcode.h | 5 +- quickjs.c | 7005 +++++++++------------------- quickjs.h | 39 +- tests/microbench.js | 33 +- tests/test_bigfloat.js | 279 -- tests/test_bigint.js | 249 + tests/test_bignum.js | 114 - tests/test_op_overloading.js | 207 - tests/test_qjscalc.js | 256 - 20 files changed, 2553 insertions(+), 17596 deletions(-) delete mode 100644 examples/pi_bigdecimal.js delete mode 100644 examples/pi_bigfloat.js delete mode 100644 libbf.c delete mode 100644 libbf.h delete mode 100644 qjscalc.js delete mode 100644 tests/test_bigfloat.js create mode 100644 tests/test_bigint.js delete mode 100644 tests/test_bignum.js delete mode 100644 tests/test_op_overloading.js delete mode 100644 tests/test_qjscalc.js diff --git a/Makefile b/Makefile index cf88a72..a309cb4 100644 --- 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 f243dee..dcf0bcf 100644 --- 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 diff --git a/cutils.h b/cutils.h index f079e5c..32b9757 100644 --- 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 index 7cb7ad6..0000000 --- a/examples/pi_bigdecimal.js +++ /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 index 8372379..0000000 --- a/examples/pi_bigfloat.js +++ /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 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 -#include -#include -#include -#include -#include - -#ifdef __AVX2__ -#include -#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 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 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 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= 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 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 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> 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 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 -#include - -#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 7103e11..f4efebe 100644 --- a/qjs.c +++ b/qjs.c @@ -45,11 +45,6 @@ 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 46f52a6..7a56a3b 100644 --- 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 index 1400dc0..0000000 --- a/qjscalc.js +++ /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= 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); + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); } 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); - } - 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); + 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; } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *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; - } -#endif case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -10059,6 +9860,1491 @@ static inline int to_digit(int c) return 36; } +/* bigint support */ + +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ + +/* 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 + +#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) + +#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 + +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) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); + } + return carry; +} + +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) +{ + int i; + js_limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n) +{ + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;i> JS_LIMB_BITS; + } + return l; +} + +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; + } + return r; +} + +/* 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; + + 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; +} + +/* 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> 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; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* 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) +{ + js_slimb_t i; + + 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); + } + } 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; + } + } + 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; + } + } + 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 { + 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; + } + } + } + } + tabq[i] = q; + } +} + +/* 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) +{ + 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; +} + +/* 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) +{ + 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 JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + 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 JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) +{ + JSBigInt *r = &buf->big_int; + r->len = 1; + r->tab[0] = a; + return r; +} + +/* 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 + } + printf("\n"); +} + +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) +{ + js_bigint_dump1(ctx, str, p->tab, p->len); +} + +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 { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; + } +#endif +} + +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) +{ + 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 JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + 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; +} + +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) +{ + js_limb_t v; + + 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; + } + l--; + } + 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; +} + +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) +{ + return js_bigint_normalize1(ctx, a, a->len); +} + +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) +{ + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} + +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + 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 + } +} + +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) +{ + 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; + } + 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); + } + } 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); + } + } + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); +} + +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) +{ + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); +} + +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + 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; + } +} + +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) +{ + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; + + 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 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; + 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; + default: + abort(); + } + return js_bigint_normalize(ctx, r); +} + +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) +{ + 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; +} + +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + 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; +} + +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift, a_sign, n1; + JSBigInt *r; + + 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]; + } + /* no normalization is needed */ + } else { + mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + 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); + + 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; +} + +/* 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; + } + 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; + } + 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 + /* 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; + } + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + 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 { + 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; + } + } + } + } + return res; +} + +/* 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 +}; + +/* 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) +{ + 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++; + } + 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 { + 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; + } + return r; +} + +/* 2 <= base <= 36 */ +static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +static char *u64toa(char *q, int64_t n, unsigned int base) +{ + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } + return q; +} + +static char *i64toa(char *buf_end, int64_t n, unsigned int base) +{ + char *q = buf_end; + int is_neg; + + is_neg = 0; + if (n < 0) { + is_neg = 1; + n = -n; + } + *--q = '\0'; + q = u64toa(q, n, base); + if (is_neg) + *--q = '-'; + return q; +} + +/* len >= 1. 2 <= radix <= 36 */ +static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) +{ + int digit, i; + + 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'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } + } + return q; +} + +#define JS_RADIX_MAX 36 + +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 +}; + +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 +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + 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 { + 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 { + 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 { + 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)); + } + 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; + } +} + +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) +{ + 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); + } +} + /* XXX: remove */ static double js_strtod(const char *str, int radix, BOOL is_float) { @@ -10125,96 +11411,13 @@ static double js_strtod(const char *str, int radix, BOOL is_float) #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) -#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) -{ - 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); - } -#ifdef CONFIG_BIGNUM - val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); -#else - val = JS_CompactBigInt1(ctx, val, FALSE); -#endif - return val; -} - -#ifdef CONFIG_BIGNUM -static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bf_t *a; - int ret; - JSValue val; - - 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); - } - return val; -} - -static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bfdec_t *a; - int ret; - JSValue val; - - 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); - } - return val; -} -#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 { const char *p, *p_start; int sep, is_neg; @@ -10278,28 +11481,12 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } else { no_radix_prefix: if (!(flags & ATOD_INT_ONLY) && - (atod_type == ATOD_TYPE_FLOAT64 -#ifdef CONFIG_BIGNUM - || atod_type == ATOD_TYPE_BIG_FLOAT -#endif - ) && + (atod_type == ATOD_TYPE_FLOAT64) && 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); - } + double d = 1.0 / 0.0; + if (is_neg) + d = -d; + val = JS_NewFloat64(ctx, d); goto done; } } @@ -10366,39 +11553,14 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, 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 - { + } else { 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; - } + if (is_float && radix != 10) + goto fail; } } @@ -10412,23 +11574,16 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } 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); + { + 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; -#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(); } @@ -10447,14 +11602,6 @@ done: goto done; } -#ifdef CONFIG_BIGNUM -static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) -{ - return js_atof2(ctx, str, pp, radix, flags, NULL); -} -#endif - typedef enum JSToNumberHintEnum { TON_FLAG_NUMBER, TON_FLAG_NUMERIC, @@ -10470,28 +11617,13 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { 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; -#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"); - } - 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"); - } - ret = val; - break; -#endif case JS_TAG_FLOAT64: case JS_TAG_INT: case JS_TAG_EXCEPTION: @@ -10568,12 +11700,10 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, { double d; uint32_t tag; - + val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = JS_FLOAT64_NAN; - return -1; - } + if (JS_IsException(val)) + goto fail; tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10582,24 +11712,14 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(val); break; - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#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; + fail: + *pres = JS_FLOAT64_NAN; + return -1; } static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) @@ -10655,38 +11775,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) } } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - bf_t a_s, *a, r_s, *r = &r_s; - BOOL is_nan; - - 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); - } - break; -#endif default: val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) @@ -10729,15 +11817,6 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) } } 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)) { @@ -10803,15 +11882,6 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) } } 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); - } - return 0; -#endif default: val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { @@ -10883,15 +11953,6 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) } } 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); - } - break; -#endif default: val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { @@ -10958,15 +12019,6 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) } } 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)) { @@ -11002,9 +12054,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) 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: @@ -11022,20 +12071,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } } 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); - } - goto int_clamp; -#endif default: val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { @@ -11066,24 +12101,6 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, 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; @@ -11189,189 +12206,23 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) u.d = JS_VALUE_GET_FLOAT64(val); return (u.u64 >> 63); } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); 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); + JSBigInt *p = JS_VALUE_GET_PTR(val); + return js_bigint_sign(p); } -#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; -#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) -{ - 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; - } - 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)); - } 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); - } - 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); -} - -#endif /* CONFIG_BIGNUM */ - -/* 2 <= base <= 36 */ -static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; - -static char *i64toa(char *buf_end, 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'; - if (base == 10) { - /* division by known base uses multiplication */ - do { - digit = (uint64_t)n % 10; - n = (uint64_t)n / 10; - *--q = '0' + digit; - } while (n != 0); - } else { - do { - digit = (uint64_t)n % base; - n = (uint64_t)n / 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) @@ -11734,14 +12585,9 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToProperty 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 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 + return js_bigint_to_string(ctx, val); default: str = "[unsupported type]"; new_string: @@ -12025,6 +12871,8 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, 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); @@ -12035,27 +12883,6 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, 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: { @@ -12136,48 +12963,34 @@ static double js_pow(double a, double b) } } -JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) -{ - 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); - } - return val; -} - JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - if (is_math_mode(ctx) && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - return JS_NewInt64(ctx, 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 { - return JS_NewBigInt64_1(ctx, v); + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } +#endif } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - JSValue val; - if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { - val = JS_NewInt64(ctx, v); + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(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); - } + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return val; } /* return NaN if bad bigint literal */ @@ -12197,10 +13010,6 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) 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)) { @@ -12222,135 +13031,71 @@ static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) return 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) +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) { uint32_t tag; - bf_t *r; - JSBigFloat *p; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; case JS_TAG_INT: 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)); - 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); - } + goto fail; + case JS_TAG_BOOL: + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); 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; + return val; goto redo; case JS_TAG_OBJECT: val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) - return NULL; + return val; goto redo; default: fail: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to bigint"); - return NULL; + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); } - return r; + return val; } -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) { - return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); + return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val)); } -static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, 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); - } -} - -/* free the bf_t allocated by JS_ToBigInt */ -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) -{ - 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 */ +/* 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; + uint64_t res; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { *pres = 0; return -1; } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); + 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); + } + *pres = res; return 0; } @@ -12359,650 +13104,6 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); } -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; -} - -static JSValue JS_NewBigInt(JSContext *ctx) -{ - 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); -} - -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer) -{ - 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; - } - return val; -} - -/* 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) -{ - return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); -} - -static JSValue throw_bf_exception(JSContext *ctx, int status) -{ - 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); -} - -/* 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) -{ - 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; - 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; - } - break; - 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: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; - } - return r; -} - -#ifdef CONFIG_BIGNUM -/* return NULL if invalid type */ -static bfdec_t *JS_ToBigDecimal(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; -} - -static JSValue JS_NewBigFloat(JSContext *ctx) -{ - 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); -} - -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", - "++", - "--", - "~", -}; - -static int get_ovop_from_opcode(OPCodeEnum op) -{ - 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(); - } -} - -/* return NULL if not present */ -static JSObject *find_binary_op(JSBinaryOperatorDef *def, - uint32_t operator_index, - JSOverloadableOperatorEnum op) -{ - 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; -} - -/* 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) -{ - 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; - - if (opset1->is_primitive && opset2->is_primitive) { - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - return 0; - } - - ovop = get_ovop_from_opcode(op); - - 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; - } - - if (opset1->is_primitive) { - if (is_numeric) { - new_op1 = JS_ToNumeric(ctx, op1); - } else { - new_op1 = JS_ToPrimitive(ctx, op1, hint); - } - if (JS_IsException(new_op1)) - goto exception; - } else { - new_op1 = JS_DupValue(ctx, op1); - } - - if (opset2->is_primitive) { - if (is_numeric) { - new_op2 = JS_ToNumeric(ctx, op2); - } else { - new_op2 = JS_ToPrimitive(ctx, op2, hint); - } - if (JS_IsException(new_op2)) { - JS_FreeValue(ctx, new_op1); - goto exception; - } - } 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 */ - - 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; - } else { - 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; - 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; - } else { - BOOL res = JS_ToBoolFree(ctx, ret); - if (op == OP_lte || op == OP_gte) - res ^= 1; - ret = JS_NewBool(ctx, res); - } - } - 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; -} - -/* 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 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; - } - - 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; - } - - 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 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); - - 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 int js_unary_arith_bigfloat(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bf_t a_s, *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - - 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 int js_unary_arith_bigdecimal(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bfdec_t *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - - 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; - } - *pres = res; - return 0; -} - -#endif /* CONFIG_BIGNUM */ - -static int js_unary_arith_bigint(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - 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; -} - static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) @@ -13010,24 +13111,13 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue op1; int v; uint32_t tag; + JSBigIntBuf buf1; + JSBigInt *p1; 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; - } - } -#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -13059,27 +13149,75 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, sp[-1] = JS_NewInt64(ctx, v64); } break; + 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 JS_TAG_BIG_INT: - handle_bigint: - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; + { + 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; -#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: @@ -13127,25 +13265,18 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) 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)) + 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))) @@ -13158,328 +13289,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return -1; } -static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - 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; - 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); - } - break; - - /* logical operations */ - case OP_shl: - case OP_sar: - { - 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); - } - } - break; - case OP_and: - ret = bf_logic_and(r, a, b); - break; - case OP_or: - ret = bf_logic_or(r, a, b); - break; - case OP_xor: - ret = bf_logic_xor(r, a, b); - break; - default: - abort(); - } - 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 = 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) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - 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; - } - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, res); - goto fail; - } - 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); - break; - case OP_pow: - ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUIRKS); - 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); - if (unlikely(ret & BF_ST_MEM_ERROR)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return -1; -} - -/* b must be a positive integer */ -static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) -{ - bfdec_t b1; - int32_t b2; - int ret; - - bfdec_init(b->ctx, &b1); - ret = bfdec_set(&b1, b); - if (ret) { - bfdec_delete(&b1); - return ret; - } - ret = bfdec_rint(&b1, BF_RNDZ); - if (ret) { - bfdec_delete(&b1); - return BF_ST_INVALID_OP; /* must be an integer */ - } - 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); -} - -static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bfdec_t *r, *a, *b; - int ret; - JSValue res; - - res = JS_NewBigDecimal(ctx); - if (JS_IsException(res)) - goto fail; - r = JS_GetBigDecimal(res); - - 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); - break; - case OP_mod: - ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); - break; - case OP_pow: - ret = js_bfdec_pow(r, a, b); - break; - default: - abort(); - } - 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; - fail: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - 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; + 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 { - sp[-2] = res; + 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 */ + 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_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); break; - case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ - break; - case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ - 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) { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { 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); + + 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; -#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) { - 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); - } - 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; + JSBigIntBuf buf; + JSBigInt *p; + uint32_t len, i; + js_limb_t v, b; + int shift; + + bc_put_u8(s, BC_TAG_BIG_INT); - 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); - - /* 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 (bc_get_leb128(s, &len)) + goto fail; + 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); + } + p = js_bigint_new(s->ctx, + (len + (JS_LIMB_BITS / 8) - 1) / (JS_LIMB_BITS / 8)); if (!p) 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(); - } - - /* sign + exponent */ - if (bc_get_sleb128(s, &e)) - 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"); - 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; - } -#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; + if (bc_get_u64(s, &v)) + goto fail; #endif - a->tab[i] = v; - } + 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); } + 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; @@ -50661,307 +49979,6 @@ void JS_AddIntrinsicEval(JSContext *ctx) ctx->eval_internal = __JS_EvalInternal; } -#ifdef CONFIG_BIGNUM - -/* Operators */ - -static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) -{ - JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); - int i, j; - JSBinaryOperatorDefEntry *ent; - - 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])); - } - } - 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; - - 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); - } - } - } -} - - -/* 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; - - 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); - } - } - } - /* 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); - - /* 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); - } - } - } - opset->is_primitive = is_primitive; - opset->operator_counter = ctx->rt->operator_count++; - return opset_obj; - fail: - JS_FreeValue(ctx, opset_obj); - return JS_EXCEPTION; -} - -static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_operators_create_internal(ctx, argc, argv, FALSE); -} - -static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue opset_obj, prop; - JSOperatorSetData *opset; - const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; - JSOverloadableOperatorEnum op; - int i; - - 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); - } - } - } - 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 (!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; -} - -static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); -} - -static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue func_obj, proto, opset_obj; - - func_obj = JS_UNDEFINED; - proto = JS_NewObject(ctx); - if (JS_IsException(proto)) - 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 */ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) @@ -50975,56 +49992,27 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) case JS_TAG_BOOL: val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif { - bf_t *a, a_s; - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (!bf_is_finite(a)) { - JS_FreeValue(ctx, val); - val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); - } 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); + 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_CompactBigInt(ctx, val1); - } + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } + } else { + val = JS_CompactBigInt(ctx, r); } - if (a == &a_s) - bf_delete(a); } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; -#endif case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); break; @@ -51097,195 +50085,72 @@ static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigIntValue(ctx, this_val); } -#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; - - 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; - } else { - JS_FreeValue(ctx, r_val); - return q_val; - } - 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; - - 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); - - 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; -} - -static JSValue js_bigint_op1(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - bf_t a_s, *a; - int64_t res; - - 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(); - } - JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt64(ctx, res); -} -#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; - + JSValue res, a; + if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) + a = JS_ToBigInt(ctx, argv[1]); + if (JS_IsException(a)) return JS_EXCEPTION; - r = JS_GetBigInt(res); - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - /* 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); + 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); } } - bf_delete(mask); - JS_FreeBigInt(ctx, a, &a_s); - return JS_CompactBigInt(ctx, res); + return res; } 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[] = { @@ -51296,15 +50161,8 @@ static const JSCFunctionListEntry js_bigint_proto_funcs[] = { 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, @@ -51315,1413 +50173,6 @@ void JS_AddIntrinsicBigInt(JSContext *ctx) countof(js_bigint_funcs)); } -#ifdef CONFIG_BIGNUM - -/* BigFloat */ - -static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigFloat(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_FLOAT) { - if (JS_IsBigFloat(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigfloat"); -} - -static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; - - 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; - } - 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; -} - -static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigFloatValue(ctx, this_val); -} - -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; -} - -static JSValue js_bigfloat_toFixed(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 (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; - } - 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; - - 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); - } - break; - default: - res = FALSE; - break; - } - 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; - } - 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; - } - 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; -} - -static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t p; - int rnd_mode, radix; - - 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; - } - 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; -} - -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 ), -}; - -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; - - 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; - } - p += skip_spaces(p); - err = ((p - str) != len); - } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); - } - } - 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"); - } - } - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -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); - break; - case 1: /* LN2 */ - bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); - 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); - } - } - 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); - break; - default: - abort(); - } - return val; -} - -static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - bf_t *a; - const char *str; - JSValue ret; - int radix; - JSFloatEnv *fe; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - fail: - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - 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; - } - 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)); -} - -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) -{ - 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; -} - -static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; - JSFloatEnv *fe; - JSValue op1, op2, res; - - 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; - } - 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); - break; - case MATH_OP_DIV: - fe->status |= bf_div(r, a, b, fe->prec, fe->flags); - 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; -} - -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) -{ - 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; - } - } - - obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); - if (JS_IsException(obj)) - return JS_EXCEPTION; - fe = js_malloc(ctx, sizeof(*fe)); - if (!fe) - return JS_EXCEPTION; - fe->prec = prec; - fe->flags = flags; - fe->status = 0; - JS_SetOpaque(obj, fe); - return obj; -} - -static void js_float_env_finalizer(JSRuntime *rt, JSValue val) -{ - JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); - js_free_rt(rt, fe); -} - -static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt64(ctx, ctx->fp_env.prec); -} - -static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); -} - -static JSValue js_float_env_setPrec(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; - - func = argv[0]; - if (JS_ToInt64Sat(ctx, &prec, argv[1])) - 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"); - } - - flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); - - saved_prec = ctx->fp_env.prec; - saved_flags = ctx->fp_env.flags; - - ctx->fp_env.prec = prec; - ctx->fp_env.flags = flags; - - 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; -} - -#define FE_PREC (-1) -#define FE_EXP (-2) -#define FE_RNDMODE (-3) -#define FE_SUBNORMAL (-4) - -static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSFloatEnv *fe; - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - 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); - } -} - -static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) -{ - JSFloatEnv *fe; - int b; - int64_t prec; - - 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; -} - -static JSValue js_float_env_clearStatus(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - fe->status = 0; - return JS_UNDEFINED; -} - -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 ), -}; - -void JS_AddIntrinsicBigFloat(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValueConst obj1; - - 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: - 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; - } - } - break; - case JS_TAG_FLOAT64: - 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: - { - 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; - } 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"); - } - } - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - 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 val; -} - -static JSValue js_bigdecimal_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; -} - -static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigDecimal(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)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigdecimal"); -} - -static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - - val = js_thisBigDecimalValue(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); - } 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) - goto fail; - } - ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigdecimal_toExponential(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; -} - -static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - 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; - } - 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; -} - -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_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 ), -}; - -void JS_AddIntrinsicBigDecimal(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; -} - -#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)) { diff --git a/quickjs.h b/quickjs.h index edc7b47..e908885 100644 --- 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) diff --git a/tests/microbench.js b/tests/microbench.js index 63790b6..871770e 100644 --- a/tests/microbench.js +++ b/tests/microbench.js @@ -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 index c35fb72..0000000 --- a/tests/test_bigfloat.js +++ /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 index 0000000..a0d028c --- /dev/null +++ b/tests/test_bigint.js @@ -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 index 1520d82..0000000 --- a/tests/test_bignum.js +++ /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 index 269abb2..0000000 --- a/tests/test_op_overloading.js +++ /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 index e97dd31..0000000 --- a/tests/test_qjscalc.js +++ /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();