]> git.kaiwu.me - quickjs.git/commitdiff
2020-01-19 release
authorbellard <6490144+bellard@users.noreply.github.com>
Sun, 6 Sep 2020 16:57:11 +0000 (18:57 +0200)
committerbellard <6490144+bellard@users.noreply.github.com>
Sun, 6 Sep 2020 16:57:11 +0000 (18:57 +0200)
26 files changed:
Changelog
Makefile
TODO
VERSION
doc/jsbignum.html
doc/jsbignum.pdf
doc/jsbignum.texi
doc/quickjs.html
doc/quickjs.pdf
doc/quickjs.texi
examples/pi_bigdecimal.js
libbf.c
libbf.h
qjs.c
qjsc.c
qjscalc.js
quickjs-atom.h
quickjs-libc.c
quickjs-opcode.h
quickjs.c
quickjs.h
repl.js
test262_errors.txt
tests/test_bignum.js
tests/test_op_overloading.js [new file with mode: 0644]
tests/test_qjscalc.js

index 73b125005d6e94a5663af39830e6335397002292..637dce18aa2c4af0ada572c65a7d4a38ae2ba759 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,15 @@
+2020-01-19:
+
+- keep CONFIG_BIGNUM in the makefile
+- added os.chdir()
+- qjs: added -I option
+- more memory checks in the bignum operations
+- modified operator overloading semantics to be closer to the TC39
+  proposal
+- suppressed "use bigint" mode. Simplified "use math" mode
+- BigDecimal: changed suffix from 'd' to 'm'
+- misc bug fixes
+
 2020-01-05:
 
 - always compile the bignum code. Added '--bignum' option to qjs.
index e402d6b4c7af61712975b6c60a99bd4a2e476406..88e605b33e54cf8f9d6491e1910b75c33ab122af 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 #
 # QuickJS Javascript Engine
 # 
-# Copyright (c) 2017-2019 Fabrice Bellard
-# Copyright (c) 2017-2019 Charlie Gordon
+# Copyright (c) 2017-2020 Fabrice Bellard
+# Copyright (c) 2017-2020 Charlie Gordon
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -47,6 +47,8 @@ prefix=/usr/local
 #CONFIG_PROFILE=y
 # use address sanitizer
 #CONFIG_ASAN=y
+# include the code for BigInt/BigFloat/BigDecimal and math mode
+CONFIG_BIGNUM=y
 
 OBJDIR=.obj
 
@@ -94,6 +96,9 @@ ifdef CONFIG_WERROR
 CFLAGS+=-Werror
 endif
 DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
+ifdef CONFIG_BIGNUM
+DEFINES+=-DCONFIG_BIGNUM
+endif
 CFLAGS+=$(DEFINES)
 CFLAGS_DEBUG=$(CFLAGS) -O0
 CFLAGS_SMALL=$(CFLAGS) -Os
@@ -153,9 +158,13 @@ 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)/libbf.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.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 $(OBJDIR)/qjscalc.o $(QJS_LIB_OBJS)
+QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
+ifdef CONFIG_BIGNUM
+QJS_LIB_OBJS+=$(OBJDIR)/libbf.o 
+QJS_OBJS+=$(OBJDIR)/qjscalc.o
+endif
 
 LIBS=-lm
 ifndef CONFIG_WIN32
@@ -215,7 +224,7 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
        $(AR) rcs $@ $^
 endif # CONFIG_LTO
 
-repl.c: $(QJSC) repl.js 
+repl.c: $(QJSC) repl.js
        $(QJSC) -c -o $@ -m repl.js
 
 qjscalc.c: $(QJSC) qjscalc.js
@@ -301,7 +310,10 @@ endif
 HELLO_SRCS=examples/hello.js
 HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
            -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-           -fno-date -fno-module-loader -fno-bigint
+           -fno-date -fno-module-loader
+ifdef CONFIG_BIGNUM
+HELLO_OPTS+=-fno-bigint
+endif
 
 hello.c: $(QJSC) $(HELLO_SRCS)
        $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
@@ -372,20 +384,30 @@ test: qjs
        ./qjs tests/test_loop.js
        ./qjs tests/test_std.js
 ifndef CONFIG_DARWIN
+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_bignum.js
        ./qjs --qjscalc tests/test_qjscalc.js
+endif
 ifdef CONFIG_M32
        ./qjs32 tests/test_closure.js
        ./qjs32 tests/test_op.js
        ./qjs32 tests/test_builtin.js
        ./qjs32 tests/test_loop.js
        ./qjs32 tests/test_std.js
+ifdef CONFIG_BIGNUM
+       ./qjs32 --bignum tests/test_op_overloading.js
        ./qjs32 --bignum tests/test_bignum.js
        ./qjs32 --qjscalc tests/test_qjscalc.js
 endif
+endif
 
 stats: qjs qjs32
        ./qjs -qd
diff --git a/TODO b/TODO
index dbb461256c260a66337439f27952f633993fc689..33a9b9ddf6e7e6621394474d10922b33f666d4e2 100644 (file)
--- a/TODO
+++ b/TODO
@@ -73,6 +73,5 @@ REPL:
 Test262o:   0/11262 errors, 463 excluded
 Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
 
-Test262:   4/67619 errors, 913 excluded, 1660 skipped
-Test262bn: 4/69722 errors, 846 excluded, 672 skipped
-test262 commit: 19fd4bea797646ae9bbfc9d325f14052ca370b54
+Test262:   17/69942 errors, 855 excluded, 581 skipped
+test262 commit: 28b4fcca4b1b1d278dfe0cc0e69c7d9d59b31aab
diff --git a/VERSION b/VERSION
index 242aa927f46b0d5604ff84df3ee05f02c2441f50..ceac5568949598644d806d1022ca31827bb93d47 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2020-01-05
+2020-01-19
index c60dc4541cf46e62a469d74062d069b57d7baf63..096dc04235cf8cf7bee7081c27f2fb12a7c04023 100644 (file)
@@ -1,7 +1,8 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html>
-<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
+<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
 <head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <title>Javascript Bignum Extensions</title>
 
 <meta name="description" content="Javascript Bignum Extensions">
@@ -9,7 +10,6 @@
 <meta name="resource-type" content="document">
 <meta name="distribution" content="global">
 <meta name="Generator" content="makeinfo">
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link href="#SEC_Contents" rel="contents" title="Table of Contents">
 <style type="text/css">
 <!--
@@ -52,61 +52,33 @@ ul.no-bullet {list-style: none}
 <div class="contents">
 <ul class="no-bullet">
 <li><a name="toc-Introduction" href="#Introduction">1 Introduction</a></li>
-<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a>
+<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a></li>
+<li><a name="toc-BigInt-extensions" href="#BigInt-extensions">3 BigInt extensions</a></li>
+<li><a name="toc-BigFloat" href="#BigFloat">4 BigFloat</a>
 <ul class="no-bullet">
-  <li><a name="toc-Introduction-1" href="#Introduction-1">2.1 Introduction</a></li>
-  <li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">2.2 Builtin Object changes</a>
-  <ul class="no-bullet">
-    <li><a name="toc-Symbol-constructor" href="#Symbol-constructor">2.2.1 <code>Symbol</code> constructor</a></li>
-  </ul></li>
-</ul></li>
-<li><a name="toc-The-BigInt-Mode" href="#The-BigInt-Mode">3 The BigInt Mode</a>
-<ul class="no-bullet">
-  <li><a name="toc-Introduction-2" href="#Introduction-2">3.1 Introduction</a></li>
-  <li><a name="toc-Changes-that-introduce-incompatibilities-with-Javascript" href="#Changes-that-introduce-incompatibilities-with-Javascript">3.2 Changes that introduce incompatibilities with Javascript</a>
-  <ul class="no-bullet">
-    <li><a name="toc-Standard-mode" href="#Standard-mode">3.2.1 Standard mode</a></li>
-    <li><a name="toc-Bigint-mode" href="#Bigint-mode">3.2.2 Bigint mode</a></li>
-  </ul></li>
-  <li><a name="toc-Operators" href="#Operators">3.3 Operators</a>
-  <ul class="no-bullet">
-    <li><a name="toc-Arithmetic-operators" href="#Arithmetic-operators">3.3.1 Arithmetic operators</a></li>
-    <li><a name="toc-Logical-operators" href="#Logical-operators">3.3.2 Logical operators</a></li>
-    <li><a name="toc-Relational-operators" href="#Relational-operators">3.3.3 Relational operators</a></li>
-  </ul></li>
-  <li><a name="toc-Number-literals" href="#Number-literals">3.4 Number literals</a></li>
-  <li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">3.5 Builtin Object changes</a>
-  <ul class="no-bullet">
-    <li><a name="toc-BigInt-function" href="#BigInt-function">3.5.1 <code>BigInt</code> function</a></li>
-    <li><a name="toc-BigInt_002eprototype" href="#BigInt_002eprototype">3.5.2 <code>BigInt.prototype</code></a></li>
-    <li><a name="toc-Number-constructor" href="#Number-constructor">3.5.3 <code>Number</code> constructor</a></li>
-    <li><a name="toc-Number_002eprototype" href="#Number_002eprototype">3.5.4 <code>Number.prototype</code></a></li>
-    <li><a name="toc-Math-object" href="#Math-object">3.5.5 <code>Math</code> object</a></li>
-  </ul></li>
-</ul></li>
-<li><a name="toc-Arbitrarily-large-floating-point-numbers" href="#Arbitrarily-large-floating-point-numbers">4 Arbitrarily large floating point numbers</a>
-<ul class="no-bullet">
-  <li><a name="toc-Introduction-3" href="#Introduction-3">4.1 Introduction</a></li>
+  <li><a name="toc-Introduction-1" href="#Introduction-1">4.1 Introduction</a></li>
   <li><a name="toc-Floating-point-rounding" href="#Floating-point-rounding">4.2 Floating point rounding</a></li>
-  <li><a name="toc-Operators-1" href="#Operators-1">4.3 Operators</a></li>
+  <li><a name="toc-Operators" href="#Operators">4.3 Operators</a></li>
   <li><a name="toc-BigFloat-literals" href="#BigFloat-literals">4.4 BigFloat literals</a></li>
-  <li><a name="toc-Builtin-Object-changes-2" href="#Builtin-Object-changes-2">4.5 Builtin Object changes</a>
+  <li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">4.5 Builtin Object changes</a>
   <ul class="no-bullet">
     <li><a name="toc-BigFloat-function" href="#BigFloat-function">4.5.1 <code>BigFloat</code> function</a></li>
     <li><a name="toc-BigFloat_002eprototype" href="#BigFloat_002eprototype">4.5.2 <code>BigFloat.prototype</code></a></li>
     <li><a name="toc-BigFloatEnv-constructor" href="#BigFloatEnv-constructor">4.5.3 <code>BigFloatEnv</code> constructor</a></li>
-    <li><a name="toc-Math-object-1" href="#Math-object-1">4.5.4 <code>Math</code> object</a></li>
   </ul></li>
 </ul></li>
-<li><a name="toc-Math-mode" href="#Math-mode">5 Math mode</a>
+<li><a name="toc-BigDecimal" href="#BigDecimal">5 BigDecimal</a>
 <ul class="no-bullet">
-  <li><a name="toc-Introduction-4" href="#Introduction-4">5.1 Introduction</a></li>
-  <li><a name="toc-Builtin-Object-changes-3" href="#Builtin-Object-changes-3">5.2 Builtin Object changes</a>
+  <li><a name="toc-Operators-1" href="#Operators-1">5.1 Operators</a></li>
+  <li><a name="toc-BigDecimal-literals" href="#BigDecimal-literals">5.2 BigDecimal literals</a></li>
+  <li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">5.3 Builtin Object changes</a>
   <ul class="no-bullet">
-    <li><a name="toc-Symbol-constructor-1" href="#Symbol-constructor-1">5.2.1 <code>Symbol</code> constructor</a></li>
+    <li><a name="toc-The-BigDecimal-function_002e" href="#The-BigDecimal-function_002e">5.3.1 The <code>BigDecimal</code> function.</a></li>
+    <li><a name="toc-Properties-of-the-BigDecimal-object" href="#Properties-of-the-BigDecimal-object">5.3.2 Properties of the <code>BigDecimal</code> object</a></li>
+    <li><a name="toc-Properties-of-the-BigDecimal_002eprototype-object" href="#Properties-of-the-BigDecimal_002eprototype-object">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</a></li>
   </ul></li>
-  <li><a name="toc-Remaining-issues" href="#Remaining-issues">5.3 Remaining issues</a></li>
 </ul></li>
+<li><a name="toc-Math-mode" href="#Math-mode">6 Math mode</a></li>
 
 </ul>
 </div>
@@ -119,379 +91,52 @@ ul.no-bullet {list-style: none}
 language while being 100% backward compatible:
 </p>
 <ul>
-<li> Overloading of the standard operators
-to support new types such as complex numbers, fractions or matrices.
-
-</li><li> Bigint mode where arbitrarily large integers are available by default (no <code>n</code> suffix is necessary as in the TC39 BigInt proposal<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>).
+<li> Operator overloading with a dispatch logic inspired from the proposal available at <a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>.
 
 </li><li> Arbitrarily large floating point numbers (<code>BigFloat</code>) in base 2 using the IEEE 754 semantics.
 
-</li><li> Optional <code>math</code> mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder.
+</li><li> Arbitrarily large floating point numbers (<code>BigDecimal</code>) in base 10 based on the proposal available at
+<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>.
+
+</li><li> <code>math</code> mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (<code>%</code>) is defined as the Euclidian
+remainder. <code>^</code> is an alias to the power operator
+(<code>**</code>). <code>^^</code> is used as the exclusive or operator.
 
 </li></ul>
 
 <p>The extensions are independent from each other except the <code>math</code>
-mode which relies on the bigint mode and the operator overloading.
+mode which relies on BigFloat and operator overloading.
 </p>
 <a name="Operator-overloading"></a>
 <h2 class="chapter">2 Operator overloading</h2>
 
-<a name="Introduction-1"></a>
-<h3 class="section">2.1 Introduction</h3>
-
-<p>If the operands of an operator have at least one object type, a custom
-operator method is searched before doing the legacy Javascript
-<code>ToNumber</code> conversion.
-</p>
-<p>For unary operators, the custom function is looked up in the object
-and has the following name:
-</p>
-<dl compact="compact">
-<dt><code>unary +</code></dt>
-<dd><p><code>Symbol.operatorPlus</code>
-</p>
-</dd>
-<dt><code>unary -</code></dt>
-<dd><p><code>Symbol.operatorNeg</code>
-</p>
-</dd>
-<dt><code>++</code></dt>
-<dd><p><code>Symbol.operatorInc</code>
-</p>
-</dd>
-<dt><code>--</code></dt>
-<dd><p><code>Symbol.operatorDec</code>
-</p>
-</dd>
-<dt><code>~</code></dt>
-<dd><p><code>Symbol.operatorNot</code>
-</p>
-</dd>
-</dl>
-
-<p>For binary operators:
-</p>
-<ul>
-<li> If both operands have the same constructor function, then the operator
-is looked up in the constructor.
-
-</li><li> Otherwise, the property <code>Symbol.operatorOrder</code> is looked up in both
-constructors and converted to <code>Int32</code>. The operator is then
-looked in the constructor with the larger <code>Symbol.operatorOrder</code>
-value. A <code>TypeError</code> is raised if both constructors have the same
-<code>Symbol.operatorOrder</code> value.
-
-</li></ul>
-
-<p>The operator is looked up with the following name:
-</p>
-<dl compact="compact">
-<dt><code>+</code></dt>
-<dd><p><code>Symbol.operatorAdd</code>
-</p>
-</dd>
-<dt><code>-</code></dt>
-<dd><p><code>Symbol.operatorSub</code>
-</p>
-</dd>
-<dt><code>*</code></dt>
-<dd><p><code>Symbol.operatorMul</code>
-</p>
-</dd>
-<dt><code>/</code></dt>
-<dd><p><code>Symbol.operatorDiv</code>
-</p>
-</dd>
-<dt><code>%</code></dt>
-<dd><p><code>Symbol.operatorMod</code>
-</p>
-</dd>
-<dt><code>% (math mode)</code></dt>
-<dd><p><code>Symbol.operatorMathMod</code>
-</p>
-</dd>
-<dt><code>**</code></dt>
-<dd><p><code>Symbol.operatorPow</code>
-</p>
-</dd>
-<dt><code>|</code></dt>
-<dd><p><code>Symbol.operatorOr</code>
-</p>
-</dd>
-<dt><code>^</code></dt>
-<dd><p><code>Symbol.operatorXor</code>
-</p>
-</dd>
-<dt><code>&amp;</code></dt>
-<dd><p><code>Symbol.operatorAnd</code>
-</p>
-</dd>
-<dt><code>&lt;&lt;</code></dt>
-<dd><p><code>Symbol.operatorShl</code>
-</p>
-</dd>
-<dt><code>&gt;&gt;</code></dt>
-<dd><p><code>Symbol.operatorShr</code>
-</p>
-</dd>
-<dt><code>&lt;</code></dt>
-<dd><p><code>Symbol.operatorCmpLT</code>
-</p>
-</dd>
-<dt><code>&gt;</code></dt>
-<dd><p><code>Symbol.operatorCmpLT</code>, operands swapped
-</p>
-</dd>
-<dt><code>&lt;=</code></dt>
-<dd><p><code>Symbol.operatorCmpLE</code>
-</p>
-</dd>
-<dt><code>&gt;=</code></dt>
-<dd><p><code>Symbol.operatorCmpLE</code>, operands swapped
-</p>
-</dd>
-<dt><code>==, !=</code></dt>
-<dd><p><code>Symbol.operatorCmpEQ</code>
-</p>
-</dd>
-</dl>
-
-<p>The return value of <code>Symbol.operatorCmpLT</code>, <code>Symbol.operatorCmpLE</code> and
-<code>Symbol.operatorCmpEQ</code> is converted to <code>Boolean</code>.
+<p>Operator overloading is inspired from the proposal available at
+<a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. It
+implements the same dispatch logic but finds the operator sets by
+looking at the <code>Symbol.operatorSet</code> property in the objects. The
+changes were done in order to simplify the implementation.
 </p>
-<a name="Builtin-Object-changes"></a>
-<h3 class="section">2.2 Builtin Object changes</h3>
-
-<a name="Symbol-constructor"></a>
-<h4 class="subsection">2.2.1 <code>Symbol</code> constructor</h4>
-
-<p>The following global symbols are added for the operator overloading:
-</p><dl compact="compact">
-<dt><code>operatorOrder</code></dt>
-<dt><code>operatorAdd</code></dt>
-<dt><code>operatorSub</code></dt>
-<dt><code>operatorMul</code></dt>
-<dt><code>operatorDiv</code></dt>
-<dt><code>operatorMod</code></dt>
-<dt><code>operatorPow</code></dt>
-<dt><code>operatorShl</code></dt>
-<dt><code>operatorShr</code></dt>
-<dt><code>operatorAnd</code></dt>
-<dt><code>operatorOr</code></dt>
-<dt><code>operatorXor</code></dt>
-<dt><code>operatorCmpLT</code></dt>
-<dt><code>operatorCmpLE</code></dt>
-<dt><code>operatorCmpEQ</code></dt>
-<dt><code>operatorPlus</code></dt>
-<dt><code>operatorNeg</code></dt>
-<dt><code>operatorNot</code></dt>
-<dt><code>operatorInc</code></dt>
-<dt><code>operatorDec</code></dt>
-</dl>
-
-
-<a name="The-BigInt-Mode"></a>
-<h2 class="chapter">3 The BigInt Mode</h2>
-
-<a name="Introduction-2"></a>
-<h3 class="section">3.1 Introduction</h3>
-
-<p>The bigint mode is enabled with the <code>&quot;use bigint&quot;</code> directive. It
-propagates the same way as the strict mode. In bigint mode, all
-integers are considered as <code>bigint</code> (arbitrarily large integer,
-similar to the TC39 BigInt
-proposal<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>)
-instead of <code>number</code> (floating point number). In order to be able
-to exchange data between standard and bigint modes, numbers are
-internally represented as 3 different types:
+<p>More precisely, the following modifications were made:
 </p>
 <ul>
-<li> Small integer (SmallInt): 32 bit integer<a name="DOCF3" href="#FOOT3"><sup>3</sup></a>.
+<li> <code>with operators from</code> is not supported. Operator overloading is always enabled.
 
-</li><li> Big integer (BigInt): arbitrarily large integer.
+</li><li> The dispatch is not based on a static <code>[[OperatorSet]]</code> field in all instances. Instead, a dynamic lookup the of the <code>Symbol.operatorSet</code> property is done. This property is typically added in the prototype of each object.
 
-</li><li> Floating point number (Float).
+</li><li> <code>Operators.create(...dictionaries)</code> is used to create a new OperatorSet object. The <code>Operators</code> function is supported as an helper to be closer to the TC39 proposal.
 
-</li></ul>
-
-<p>In standard mode, the semantics of each operation is modified so that
-when it returns a <code>number</code>, it is either of SmallInt or
-Float. But the difference between SmallInt and Float is not observable
-in standard mode.
-</p>
-<p>In bigint mode, each operation behaves differently whether its
-operands are integer or float. The difference between SmallInt and
-BigInt is not observable (i.e. they are both integers).
-</p>
-<p>The following table summarizes the observable types:
-</p>
-<table>
-<thead><tr><th width="30%">Internal type</th><th width="30%">Observable type<br> (standard mode)</th><th width="30%">Observable type<br> (bigint mode)</th></tr></thead>
-<tr><td width="30%">SmallInt</td><td width="30%">number</td><td width="30%">bigint</td></tr>
-<tr><td width="30%">BigInt</td><td width="30%">bigint</td><td width="30%">bigint</td></tr>
-<tr><td width="30%">Float</td><td width="30%">number</td><td width="30%">number</td></tr>
-</table>
-
-<a name="Changes-that-introduce-incompatibilities-with-Javascript"></a>
-<h3 class="section">3.2 Changes that introduce incompatibilities with Javascript</h3>
+</li><li> <code>[]</code> cannot be overloaded.
 
-<a name="Standard-mode"></a>
-<h4 class="subsection">3.2.1 Standard mode</h4>
-
-<p>There is no incompatibility with Javascript.
-</p>
-<a name="Bigint-mode"></a>
-<h4 class="subsection">3.2.2 Bigint mode</h4>
-
-<p>The following changes are visible:
-</p>
-<ul>
-<li> Integer and Float are different types. Constants are typed. For example: <code>typeof 1.0 === &quot;number&quot;</code> and <code>typeof 1 === &quot;bigint&quot;</code>. Another consequence is that <code>1.0 === 1</code> is false.
-
-</li><li> The range of integers is unlimited. In standard mode: <code>2**53 + 1 === 2**53</code>. This is no longer true with the bignum extensions.
-
-</li><li> Binary bitwise operators do not truncate to 32 bits i.e. <code>0x800000000 | 1 === 0x800000001</code> while it gives <code>1</code> in standard mode.
-
-</li><li> Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with <code>0x1f</code> i.e. <code>1 &lt;&lt; 32 === 4294967296</code> while it gives <code>1</code> in standard mode. However, the <code>&gt;&gt;&gt;</code> operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior<a name="DOCF4" href="#FOOT4"><sup>4</sup></a>.
-
-</li><li> Operators with integer operands never return the minus zero floating point value as result. Hence <code>Object.is(0, -0) === true</code>. Use <code>-0.0</code> to create a minus zero floating point value.
-
-</li><li> The <code>ToPrimitive</code> abstract operation is called with the <code>&quot;integer&quot;</code> preferred type when an integer is required (e.g. for bitwise binary or shift operations).
-
-</li><li> The prototype of integers is no longer <code>Number.prototype</code>. Instead<br> <code>Object.getPrototypeOf(1) === BigInt.prototype</code>. The prototype of floats remains Number.prototype.
-
-</li><li> If the TC39 BigInt proposal is supported, there is no observable difference between integers and <code>bigint</code>s.
+</li><li> In math mode, the BigInt division and power operators can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>.
 
 </li></ul>
 
-<a name="Operators"></a>
-<h3 class="section">3.3 Operators</h3>
-
-<a name="Arithmetic-operators"></a>
-<h4 class="subsection">3.3.1 Arithmetic operators</h4>
-
-<p>The operands are converted to number values as in normal
-Javascript. Then the general case is that an Integer is returned if
-both operands are Integer. Otherwise, a float is returned.
-</p>
-<p>The <code>+</code> operator also accepts strings as input and behaves like
-standard Javascript in this case.
-</p>
-<p>The binary operator <code>%</code> returns the truncated remainder of the
-division. When the result is an Integer type, a dividend of zero yields a
-RangeError exception.
-</p>
-<p>The binary operator <code>%</code> in math mode returns the Euclidian
-remainder of the division i.e. it is always positive. 
-</p>
-<p>The binary operator <code>/</code> returns a float.
-</p>
-<p>The binary operator <code>/</code> in math mode returns a float if one of
-the operands is float. Otherwise, <code>BigInt[Symbol.operatorDiv]</code> is
-invoked.
-</p>
-<p>The returned type of <code>a ** b</code> is Float if <em>a</em> or <em>b</em>
-are Float. If <em>a</em> and <em>b</em> are integers:
-</p><ul>
-<li> <em>b &lt; 0</em> returns a Float in bigint mode. In math mode, <code>BigInt[Symbol.operatorPow]</code> is invoked.
-
-</li><li> <em>b &gt;= 0</em> returns an integer.
-</li></ul>
-
-<p>The unary <code>-</code> and unary <code>+</code> return the same type as their
-operand. They performs no floating point rounding when the result is a
-float.
-</p>
-<p>The unary operators <code>++</code> and <code>--</code> return the same type as
-their operand.
-</p>
-<p>In standard mode:
-</p>
-<p>If the operator returns an Integer and that the result fits a
-SmallInt, it is converted to SmallInt. Otherwise, the Integer is
-converted to a Float.
-</p>
-<p>In bigint mode:
-</p>
-<p>If the operator returns an Integer and that the result fits a
-SmallInt, it is converted to SmallInt. Otherwise it is a BigInt.
-</p>
-<a name="Logical-operators"></a>
-<h4 class="subsection">3.3.2 Logical operators</h4>
-
-<p>In standard mode:
-</p>
-<p>The operands have their standard behavior. If the result fits a
-SmallInt it is converted to a SmallInt. Otherwise it is a Float.
-</p>
-<p>In bigint mode:
-</p>
-<p>The operands are converted to integer values. The floating point
-values are converted to integer by rounding them to zero.
-</p>
-<p>The logical operators are defined assuming the integers are
-represented in two complement notation.
-</p>
-<p>For <code>&lt;&lt;</code> and <code>&lt;&lt;</code>, the shift can be positive or negative. So
-<code>a &lt;&lt; b</code> is defined as <em>\lfloor a/2^{-b} \rfloor</em> and
-<code>a &gt;&gt; b</code> is defined as <em>\lfloor a/2^{b} \rfloor</em>.
-</p>
-<p>The operator <code>&gt;&gt;&gt;</code> is supported for backward compatibility and
-behaves the same way as Javascript i.e. implicit conversion to <code>Uint32</code>.
-</p>
-<p>If the result fits a SmallInt it is converted to a SmallInt. Otherwise
-it is a BigInt.
-</p>
-<a name="Relational-operators"></a>
-<h4 class="subsection">3.3.3 Relational operators</h4>
-
-<p>The relational operators &lt;, &lt;=, &gt;, &gt;=, ==, != work as expected with
-integers and floating point numbers (e.g. <code>1.0 == 1</code> is true).
-</p>
-<p>The strict equality operators === and !== have the usual Javascript
-semantics. In particular, different types never equal, so <code>1.0
-=== 1</code> is false.
-</p>
-<a name="Number-literals"></a>
-<h3 class="section">3.4 Number literals</h3>
+<a name="BigInt-extensions"></a>
+<h2 class="chapter">3 BigInt extensions</h2>
 
-<p>Number literals in bigint mode have a slightly different behavior than
-in standard Javascript:
-</p>
-<ol>
-<li> A number literal without a decimal point or an exponent is considered
-as an Integer. Otherwise it is a Float.
-
-</li><li> Hexadecimal, octal or binary floating point literals are accepted with
-a decimal point or an exponent. The exponent is specified with the
-<code>p</code> letter assuming a base 2. The same convention is used by
-C99. Example: <code>0x1p3</code> is the same as <code>8.0</code>.
-
-</li></ol>
-
-<a name="Builtin-Object-changes-1"></a>
-<h3 class="section">3.5 Builtin Object changes</h3>
-
-<a name="BigInt-function"></a>
-<h4 class="subsection">3.5.1 <code>BigInt</code> function</h4>
-
-<p>The <code>BigInt</code> function cannot be invoked as a constructor. When
-invoked as a function, it converts its first parameter to an
-integer. When a floating point number is given as parameter, it is
-truncated to an integer with infinite precision.
-</p>
-<p><code>BigInt</code> properties:
+<p>A few properties are added to the BigInt object:
 </p>
 <dl compact="compact">
-<dt><code>asIntN(bits, a)</code></dt>
-<dd><p>Set <em>b=a \pmod{2^{bits}}</em>. Return <em>b</em> if <em>b &lt; 2^{bits-1}</em>
-otherwise <em>b-2^{bits}</em>.
-</p>
-</dd>
-<dt><code>asUintN(bits, a)</code></dt>
-<dd><p>Return <em>a \pmod{2^{bits}}</em>.
-</p>
-</dd>
 <dt><code>tdiv(a, b)</code></dt>
 <dd><p>Return <em>trunc(a/b)</em>. <code>b = 0</code> raises a RangeError
 exception.
@@ -542,70 +187,10 @@ raised if <em>a &lt; 0</em>.
 </dd>
 </dl>
 
-<a name="BigInt_002eprototype"></a>
-<h4 class="subsection">3.5.2 <code>BigInt.prototype</code></h4>
+<a name="BigFloat"></a>
+<h2 class="chapter">4 BigFloat</h2>
 
-<p>It is a normal object.
-</p>
-<a name="Number-constructor"></a>
-<h4 class="subsection">3.5.3 <code>Number</code> constructor</h4>
-
-<p>The number constructor returns its argument rounded to a Float using
-the global floating point environment. In bigint mode, the Number
-constructor returns a Float. In standard mode, it returns a SmallInt
-if the value fits it, otherwise a Float.
-</p>
-<a name="Number_002eprototype"></a>
-<h4 class="subsection">3.5.4 <code>Number.prototype</code></h4>
-
-<p>The following properties are modified:
-</p>
-<dl compact="compact">
-<dt><code>toString(radix)</code></dt>
-<dd>
-<p>In bigint mode, integers are converted to the specified radix with
-infinite precision.
-</p>
-</dd>
-<dt><code>toPrecision(p)</code></dt>
-<dt><code>toFixed(p)</code></dt>
-<dt><code>toExponential(p)</code></dt>
-<dd>
-<p>In bigint mode, integers are accepted and converted to string with
-infinite precision.
-</p>
-</dd>
-<dt><code>parseInt(string, radix)</code></dt>
-<dd>
-<p>In bigint mode, an integer is returned and the conversion is done with
-infinite precision.
-</p>
-</dd>
-</dl>
-
-<a name="Math-object"></a>
-<h4 class="subsection">3.5.5 <code>Math</code> object</h4>
-
-<p>The following properties are modified:
-</p>
-<dl compact="compact">
-<dt><code>abs(x)</code></dt>
-<dd><p>Absolute value. Return an integer if <code>x</code> is an Integer. Otherwise
-return a Float. No rounding is performed.
-</p>
-</dd>
-<dt><code>min(a, b)</code></dt>
-<dt><code>max(a, b)</code></dt>
-<dd><p>No rounding is performed. The returned type is the same one as the
-minimum (resp. maximum) value.
-</p>
-</dd>
-</dl>
-
-<a name="Arbitrarily-large-floating-point-numbers"></a>
-<h2 class="chapter">4 Arbitrarily large floating point numbers</h2>
-
-<a name="Introduction-3"></a>
+<a name="Introduction-1"></a>
 <h3 class="section">4.1 Introduction</h3>
 
 <p>This extension adds the <code>BigFloat</code> primitive type. The
@@ -628,8 +213,8 @@ environment are also set according to the result of the operation.
 point environment is used.
 </p>
 <p>The rounding mode of the global floating point environment is always
-<code>RNDN</code> (&ldquo;round to nearest with ties to even&rdquo;)<a name="DOCF5" href="#FOOT5"><sup>5</sup></a>. The status flags of the global environment cannot be
-read<a name="DOCF6" href="#FOOT6"><sup>6</sup></a>. The precision of the global environment is
+<code>RNDN</code> (&ldquo;round to nearest with ties to even&rdquo;)<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>. The status flags of the global environment cannot be
+read<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>. The precision of the global environment is
 <code>BigFloatEnv.prec</code>. The number of exponent bits of the global
 environment is <code>BigFloatEnv.expBits</code>.  If <code>BigFloatEnv.expBits</code> is
 strictly smaller than the maximum allowed number of exponent bits
@@ -646,7 +231,7 @@ when calling a function (see <code>BigFloatEnv.setPrec</code>). Hence a
 function can change the global floating point environment for its
 callees but not for its caller.
 </p>
-<a name="Operators-1"></a>
+<a name="Operators"></a>
 <h3 class="section">4.3 Operators</h3>
 
 <p>The builtin operators are extended so that a BigFloat is returned if
@@ -669,9 +254,9 @@ equal when using the strict comparison operators (e.g. <code>0.0 ===
 <p>BigFloat literals are floating point numbers with a trailing <code>l</code>
 suffix. BigFloat literals have an infinite precision. They are rounded
 according to the global floating point environment when they are
-evaluated.<a name="DOCF7" href="#FOOT7"><sup>7</sup></a>
+evaluated.<a name="DOCF3" href="#FOOT3"><sup>3</sup></a>
 </p>
-<a name="Builtin-Object-changes-2"></a>
+<a name="Builtin-Object-changes"></a>
 <h3 class="section">4.5 Builtin Object changes</h3>
 
 <a name="BigFloat-function"></a>
@@ -739,6 +324,10 @@ point number <code>a</code> according to the floating point environment
 <dd><p>Round to an integer. No additional rounding is performed.
 </p>
 </dd>
+<dt><code>abs(x)</code></dt>
+<dd><p>Return the absolute value of x. No additional rounding is performed.
+</p>
+</dd>
 <dt><code>fmod(x, y[, e])</code></dt>
 <dt><code>remainder(x, y[, e])</code></dt>
 <dd><p>Floating point remainder. The quotient is truncated to zero (fmod) or
@@ -773,6 +362,10 @@ number. <code>e</code> is an optional floating point environment.
 <p>The following properties are modified:
 </p>
 <dl compact="compact">
+<dt><code>valueOf()</code></dt>
+<dd><p>Return the bigfloat primitive value corresponding to <code>this</code>.
+</p>
+</dd>
 <dt><code>toString(radix)</code></dt>
 <dd>
 <p>For floating point numbers:
@@ -787,14 +380,17 @@ the global precision and round to nearest gives the same number.
 
 </li></ul>
 
+<p>The exponent letter is <code>e</code> for base 10, <code>p</code> for bases 2, 8,
+16 with a binary exponent and <code>@</code> for the other bases.
+</p>
 </dd>
-<dt><code>toPrecision(p[, rnd_mode])</code></dt>
-<dt><code>toFixed(p[, rnd_mode])</code></dt>
-<dt><code>toExponential(p[, rnd_mode])</code></dt>
+<dt><code>toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
+<dt><code>toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
+<dt><code>toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
 <dd><p>Same semantics as the corresponding <code>Number</code> functions with
 BigFloats. There is no limit on the accepted precision <code>p</code>. The
-rounding mode can be optionally specified. It is set by default to
-<code>BigFloatEnv.RNDNA</code>.
+rounding mode and radix can be optionally specified. The radix must be
+between 2 and 36.
 </p>
 </dd>
 </dl>
@@ -832,14 +428,14 @@ subnormal flags is set to <code>false</code>. If <code>rndMode</code> is
 <dl compact="compact">
 <dt><code>prec</code></dt>
 <dd><p>Getter. Return the precision in bits of the global floating point
-environment. The initial value is <code>53</code>.
+environment. The initial value is <code>113</code>.
 </p>
 </dd>
 <dt><code>expBits</code></dt>
 <dd><p>Getter. Return the exponent size in bits of the global floating point
 environment assuming an IEEE 754 representation. If <code>expBits &lt;
 expBitsMax</code>, then subnormal numbers are supported. The initial value
-is <code>11</code>.
+is <code>15</code>.
 </p>
 </dd>
 <dt><code>setPrec(f, p[, e])</code></dt>
@@ -848,9 +444,7 @@ and the exponent size to <code>e</code> then call the function
 <code>f</code>. Then the Float precision and exponent size are reset to
 their precious value and the return value of <code>f</code> is returned (or
 an exception is raised if <code>f</code> raised an exception). If <code>e</code>
-is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>. <code>p</code>
-must be &gt;= 53 and <code>e</code> must be &gt;= 11 so that the global precision
-is at least equivalent to the IEEE 754 64 bit doubles.
+is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>.
 </p>
 </dd>
 <dt><code>precMin</code></dt>
@@ -858,7 +452,7 @@ is at least equivalent to the IEEE 754 64 bit doubles.
 </p>
 </dd>
 <dt><code>precMax</code></dt>
-<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 53.
+<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 113.
 </p>
 </dd>
 <dt><code>expBitsMin</code></dt>
@@ -868,7 +462,7 @@ bits. Must be at least 3.
 </dd>
 <dt><code>expBitsMax</code></dt>
 <dd><p>Read-only integer. Return the maximum allowed exponent size in
-bits. Must be at least 11.
+bits. Must be at least 15.
 </p>
 </dd>
 <dt><code>RNDN</code></dt>
@@ -891,11 +485,11 @@ bits. Must be at least 11.
 <dd><p>Read-only integer. Round to nearest, with ties away from zero rounding mode.
 </p>
 </dd>
-<dt><code>RNDNU</code></dt>
-<dd><p>Read-only integer. Round to nearest, with ties to +Infinity rounding mode.
+<dt><code>RNDA</code></dt>
+<dd><p>Read-only integer. Round away from zero rounding mode.
 </p>
 </dd>
-<dt><code>RNDF<a name="DOCF8" href="#FOOT8"><sup>8</sup></a></code></dt>
+<dt><code>RNDF<a name="DOCF4" href="#FOOT4"><sup>4</sup></a></code></dt>
 <dd><p>Read-only integer. Faithful rounding mode. The result is
 non-deterministically rounded to -Infinity or +Infinity. This rounding
 mode usually gives a faster and deterministic running time for the
@@ -939,102 +533,200 @@ assuming an IEEE 754 representation.
 </dd>
 </dl>
 
-<a name="Math-object-1"></a>
-<h4 class="subsection">4.5.4 <code>Math</code> object</h4>
+<a name="BigDecimal"></a>
+<h2 class="chapter">5 BigDecimal</h2>
 
-<p>The following properties are modified:
+<p>This extension adds the <code>BigDecimal</code> primitive type. The
+<code>BigDecimal</code> type represents floating point numbers in base
+10. It is inspired from the proposal available at
+<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>.
+</p>
+<p>The <code>BigDecimal</code> floating point numbers are always normalized and
+finite. There is no concept of <code>-0</code>, <code>Infinity</code> or
+<code>NaN</code>. By default, all the computations are done with infinite
+precision.
+</p>
+<a name="Operators-1"></a>
+<h3 class="section">5.1 Operators</h3>
+
+<p>The following builtin operators support BigDecimal:
 </p>
 <dl compact="compact">
-<dt><code>abs(x)</code></dt>
-<dd><p>Absolute value. If <code>x</code> is a BigFloat, its absolute value is
-returned as a BigFloat. No rounding is performed.
+<dt><code>+</code></dt>
+<dt><code>-</code></dt>
+<dt><code>*</code></dt>
+<dd><p>Both operands must be BigDecimal. The result is computed with infinite
+precision.
+</p></dd>
+<dt><code>%</code></dt>
+<dd><p>Both operands must be BigDecimal. The result is computed with infinite
+precision. A range error is throws in case of division by zero.
 </p>
 </dd>
-<dt><code>min(a, b)</code></dt>
-<dt><code>max(a, b)</code></dt>
-<dd><p>The returned type is the same one as the minimum (resp. maximum)
-value, so <code>BigFloat</code> values are accepted. When a <code>BigFloat</code>
-is returned, no rounding is performed.
+<dt><code>/</code></dt>
+<dd><p>Both operands must be BigDecimal. A range error is throws in case of
+division by zero or if the result cannot be represented with infinite
+precision (use <code>BigDecimal.div</code> to specify the rounding).
+</p>
+</dd>
+<dt><code>**</code></dt>
+<dd><p>Both operands must be BigDecimal. The exponent must be a positive
+integer. The result is computed with infinite precision.
+</p>
+</dd>
+<dt><code>===</code></dt>
+<dd><p>When one of the operand is a BigDecimal, return true if both operands
+are a BigDecimal and if they are equal.
+</p>
+</dd>
+<dt><code>==</code></dt>
+<dt><code>!=</code></dt>
+<dt><code>&lt;=</code></dt>
+<dt><code>&gt;=</code></dt>
+<dt><code>&lt;</code></dt>
+<dt><code>&gt;</code></dt>
+<dd>
+<p>Numerical comparison. When one of the operand is not a BigDecimal, it is
+converted to BigDecimal by using ToString(). Hence comparisons between
+Number and BigDecimal do not use the exact mathematical value of the
+Number value.
 </p>
 </dd>
 </dl>
 
-<a name="Math-mode"></a>
-<h2 class="chapter">5 Math mode</h2>
+<a name="BigDecimal-literals"></a>
+<h3 class="section">5.2 BigDecimal literals</h3>
 
-<a name="Introduction-4"></a>
-<h3 class="section">5.1 Introduction</h3>
+<p>BigDecimal literals are decimal floating point numbers with a trailing
+<code>m</code> suffix.
+</p>
+<a name="Builtin-Object-changes-1"></a>
+<h3 class="section">5.3 Builtin Object changes</h3>
 
-<p>A new <em>math mode</em> is enabled with the <code>&quot;use math&quot;</code>
-directive. <code>&quot;use bigint&quot;</code> is implied in math mode. With this
-mode, writing mathematical expressions is more intuitive, exact
-results (e.g. fractions) can be computed for all operators and floating
-point literals have the <code>BigFloat</code> type by default.
+<a name="The-BigDecimal-function_002e"></a>
+<h4 class="subsection">5.3.1 The <code>BigDecimal</code> function.</h4>
+
+<p>It returns <code>0m</code> if no parameter is provided. Otherwise the first
+parameter is converted to a bigdecimal by using ToString(). Hence
+Number value are not converted to their exact numerical value as
+BigDecimal.
 </p>
-<p>It propagates the same way as the <em>strict mode</em>. In
-this mode:
+<a name="Properties-of-the-BigDecimal-object"></a>
+<h4 class="subsection">5.3.2 Properties of the <code>BigDecimal</code> object</h4>
+
+<dl compact="compact">
+<dt><code>add(a, b[, e])</code></dt>
+<dt><code>sub(a, b[, e])</code></dt>
+<dt><code>mul(a, b[, e])</code></dt>
+<dt><code>div(a, b[, e])</code></dt>
+<dt><code>mod(a, b[, e])</code></dt>
+<dt><code>sqrt(a, e)</code></dt>
+<dt><code>round(a, e)</code></dt>
+<dd><p>Perform the specified floating point operation and round the floating
+point result according to the rounding object <code>e</code>. If the
+rounding object is not present, the operation is executed with
+infinite precision.
 </p>
-<ul>
-<li> The <code>^</code> operator is a similar to the power operator (<code>**</code>).
+<p>For <code>div</code>, a <code>RangeError</code> exception is thrown in case of
+division by zero or if the result cannot be represented with infinite
+precision if no rounding object is present.
+</p>
+<p>For <code>sqrt</code>, a range error is thrown if <code>a</code> is less than
+zero.
+</p>
+<p>The rounding object must contain the following properties:
+<code>roundingMode</code> is a string specifying the rounding mode
+(<code>&quot;floor&quot;</code>, <code>&quot;ceiling&quot;</code>, <code>&quot;down&quot;</code>, <code>&quot;up&quot;</code>,
+<code>&quot;half-even&quot;</code>, <code>&quot;half-up&quot;</code>). Either
+<code>maximumSignificantDigits</code> or <code>maximumFractionDigits</code> must
+be present to specify respectively the number of significant digits
+(must be &gt;= 1) or the number of digits after the decimal point (must
+be &gt;= 0).
+</p>
+</dd>
+</dl>
 
-</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>.
+<a name="Properties-of-the-BigDecimal_002eprototype-object"></a>
+<h4 class="subsection">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</h4>
 
-</li><li> The logical xor operator is still available with the <code>^^</code> operator.
+<dl compact="compact">
+<dt><code>valueOf()</code></dt>
+<dd><p>Return the bigdecimal primitive value corresponding to <code>this</code>.
+</p>
+</dd>
+<dt><code>toString()</code></dt>
+<dd><p>Convert <code>this</code> to a string with infinite precision in base 10.
+</p>
+</dd>
+<dt><code>toPrecision(p, rnd_mode = &quot;half-up&quot;)</code></dt>
+<dt><code>toFixed(p, rnd_mode = &quot;half-up&quot;)</code></dt>
+<dt><code>toExponential(p, rnd_mode = &quot;half-up&quot;)</code></dt>
+<dd><p>Convert the BigDecimal <code>this</code> to string with the specified
+precision <code>p</code>. There is no limit on the accepted precision
+<code>p</code>. The rounding mode can be optionally
+specified. <code>toPrecision</code> outputs either in decimal fixed notation
+or in decimal exponential notation with a <code>p</code> digits of
+precision.  <code>toExponential</code> outputs in decimal exponential
+notation with <code>p</code> digits after the decimal point. <code>toFixed</code>
+outputs in decimal notation with <code>p</code> digits after the decimal
+point.
+</p>
+</dd>
+</dl>
 
-</li><li> The division operator invokes <code>BigInt[Symbol.operatorDiv]</code> in case both operands are integers.
+<a name="Math-mode"></a>
+<h2 class="chapter">6 Math mode</h2>
 
-</li><li> The power operator invokes <code>BigInt[Symbol.operatorPow]</code> in case both operands are integers and the exponent is strictly negative.
+<p>A new <em>math mode</em> is enabled with the <code>&quot;use math&quot;</code>
+directive. It propagates the same way as the <em>strict mode</em>. It is
+designed so that arbitrarily large integers and floating point numbers
+are available by default. In order to minimize the number of changes
+in the Javascript semantics, integers are represented either as Number
+or BigInt depending on their magnitude. Floating point numbers are
+always represented as BigFloat.
+</p>
+<p>The following changes are made to the Javascript semantics:
+</p>
+<ul>
+<li> Floating point literals (i.e. number with a decimal point or an exponent) are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied). Hence <code>typeof 1.0 === &quot;bigfloat&quot;</code>.
 
-</li><li> The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder.
+</li><li> Integer literals (i.e. numbers without a decimal point or an exponent) with or without the <code>n</code> suffix are <code>BigInt</code> if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to <code>2**53-1</code>. Hence <code>typeof 1 === &quot;number &quot;</code>, <code>typeof 1n === &quot;number&quot;</code> but <code>typeof 9007199254740992 === &quot;bigint&quot; </code>.
 
-</li><li> Floating point literals are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied).
+</li><li> All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt.
 
-</li></ul>
+</li><li> The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result.
 
-<a name="Builtin-Object-changes-3"></a>
-<h3 class="section">5.2 Builtin Object changes</h3>
+</li><li> The <code>^</code> operator is an alias to the power operator (<code>**</code>).
 
-<a name="Symbol-constructor-1"></a>
-<h4 class="subsection">5.2.1 <code>Symbol</code> constructor</h4>
+</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>.
 
-<p>The following global symbol is added for the operator overloading:
-</p><dl compact="compact">
-<dt><code>operatorMathMod</code></dt>
-</dl>
+</li><li> The logical xor operator is still available with the <code>^^</code> operator.
 
-<a name="Remaining-issues"></a>
-<h3 class="section">5.3 Remaining issues</h3>
+</li><li> The integer division operator can be overloaded by modifying the corresponding operator in <code>BigInt.prototype.[[OperatorSet]]</code>.
 
-<ol>
-<li> A new floating point literal suffix could be added for <code>Number</code> literals.
+</li><li> The integer power operator with a non zero negative exponent can be overloaded by modifying the corresponding operator in <code>BigInt.prototype.[[OperatorSet]]</code>.
 
-</li></ol>
+</li><li> The modulo operator (<code>%</code>) returns the Euclidian remainder (always positive) instead of the truncated remainder.
+
+</li></ul>
 
 <div class="footnote">
 <hr>
 <h4 class="footnotes-heading">Footnotes</h4>
 
 <h3><a name="FOOT1" href="#DOCF1">(1)</a></h3>
-<p><a href="https://tc39.github.io/proposal-bigint/">https://tc39.github.io/proposal-bigint/</a></p>
-<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3>
-<p><a href="https://tc39.github.io/proposal-bigint/">https://tc39.github.io/proposal-bigint/</a></p>
-<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3>
-<p>Could be extended to 53 bits without changing the principle.</p>
-<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3>
-<p>The unsigned right right operator could be removed in bigint mode.</p>
-<h3><a name="FOOT5" href="#DOCF5">(5)</a></h3>
 <p>The
 rationale is that the rounding mode changes must always be
 explicit.</p>
-<h3><a name="FOOT6" href="#DOCF6">(6)</a></h3>
+<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3>
 <p>The rationale is to avoid side effects for the built-in
 operators.</p>
-<h3><a name="FOOT7" href="#DOCF7">(7)</a></h3>
+<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3>
 <p>Base 10 floating point literals cannot usually be
 exactly represented as base 2 floating point number. In order to
 ensure that the literal is represented accurately with the current
 precision, it must be evaluated at runtime.</p>
-<h3><a name="FOOT8" href="#DOCF8">(8)</a></h3>
+<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3>
 <p>Could be removed in case a deterministic behavior for floating point operations is required.</p>
 </div>
 <hr>
index 16a0a35d374493f79c148c97211c68f74343da10..09d414251b729c5b4cddbd3149a7f6fb4cd55e8d 100644 (file)
Binary files a/doc/jsbignum.pdf and b/doc/jsbignum.pdf differ
index 8b69eb2b377fec0b5e9346015d0f593ca92bbbd5..70455ec1c24abb3659d248f16950f01cc08cf2e3 100644 (file)
 @sp 7
 @center @titlefont{Javascript Bignum Extensions}
 @sp 3
-@center Version 2018-06-16
+@center Version 2020-01-11
 @sp 3
 @center Author: Fabrice Bellard
 @end titlepage
 
-@setfilename spec.info
+@setfilename jsbignum.info
 @settitle Javascript Bignum Extensions
 
 @contents
@@ -27,348 +27,52 @@ language while being 100% backward compatible:
 
 @itemize
 
-@item Overloading of the standard operators
-to support new types such as complex numbers, fractions or matrices.
-
-@item Bigint mode where arbitrarily large integers are available by default (no @code{n} suffix is necessary as in the TC39 BigInt proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}).
+@item Operator overloading with a dispatch logic inspired from the proposal available at @url{https://github.com/tc39/proposal-operator-overloading/}.
 
 @item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics.
 
-@item Optional @code{math} mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder.
+@item Arbitrarily large floating point numbers (@code{BigDecimal}) in base 10 based on the proposal available at
+@url{https://github.com/littledan/proposal-bigdecimal}.
+
+@item @code{math} mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (@code{%}) is defined as the Euclidian
+remainder. @code{^} is an alias to the power operator
+(@code{**}). @code{^^} is used as the exclusive or operator.
 
 @end itemize
 
 The extensions are independent from each other except the @code{math}
-mode which relies on the bigint mode and the operator overloading.
+mode which relies on BigFloat and operator overloading.
 
 @chapter Operator overloading
 
-@section Introduction
-
-If the operands of an operator have at least one object type, a custom
-operator method is searched before doing the legacy Javascript
-@code{ToNumber} conversion.
-
-For unary operators, the custom function is looked up in the object
-and has the following name:
-
-@table @code
-@item unary + 
-@code{Symbol.operatorPlus}
-
-@item unary -
-@code{Symbol.operatorNeg}
-
-@item ++
-@code{Symbol.operatorInc}
-
-@item --
-@code{Symbol.operatorDec}
-
-@item ~
-@code{Symbol.operatorNot}
-
-@end table
-
-For binary operators:
-
-@itemize
-
-@item
-If both operands have the same constructor function, then the operator
-is looked up in the constructor.
-
-@item
-Otherwise, the property @code{Symbol.operatorOrder} is looked up in both
-constructors and converted to @code{Int32}. The operator is then
-looked in the constructor with the larger @code{Symbol.operatorOrder}
-value. A @code{TypeError} is raised if both constructors have the same
-@code{Symbol.operatorOrder} value.
-
-@end itemize
-
-The operator is looked up with the following name:
-
-@table @code
-@item + 
-@code{Symbol.operatorAdd}
-
-@item -
-@code{Symbol.operatorSub}
-
-@item *
-@code{Symbol.operatorMul}
-
-@item /
-@code{Symbol.operatorDiv}
-
-@item %
-@code{Symbol.operatorMod}
-
-@item % (math mode)
-@code{Symbol.operatorMathMod}
-
-@item **
-@code{Symbol.operatorPow}
-
-@item |
-@code{Symbol.operatorOr}
-
-@item ^
-@code{Symbol.operatorXor}
-
-@item &
-@code{Symbol.operatorAnd}
-
-@item <<
-@code{Symbol.operatorShl}
-
-@item >>
-@code{Symbol.operatorShr}
-
-@item <
-@code{Symbol.operatorCmpLT}
-
-@item >
-@code{Symbol.operatorCmpLT}, operands swapped
-
-@item <=
-@code{Symbol.operatorCmpLE}
-
-@item >=
-@code{Symbol.operatorCmpLE}, operands swapped
-
-@item ==, !=
-@code{Symbol.operatorCmpEQ}
-
-@end table
-
-The return value of @code{Symbol.operatorCmpLT}, @code{Symbol.operatorCmpLE} and
-@code{Symbol.operatorCmpEQ} is converted to @code{Boolean}.
-
-@section Builtin Object changes
-
-@subsection @code{Symbol} constructor
-
-The following global symbols are added for the operator overloading:
-@table @code
-@item operatorOrder
-@item operatorAdd
-@item operatorSub
-@item operatorMul
-@item operatorDiv
-@item operatorMod
-@item operatorPow
-@item operatorShl
-@item operatorShr
-@item operatorAnd
-@item operatorOr
-@item operatorXor
-@item operatorCmpLT
-@item operatorCmpLE
-@item operatorCmpEQ
-@item operatorPlus
-@item operatorNeg
-@item operatorNot
-@item operatorInc
-@item operatorDec
-@end table
-
-
-@chapter The BigInt Mode
+Operator overloading is inspired from the proposal available at
+@url{https://github.com/tc39/proposal-operator-overloading/}. It
+implements the same dispatch logic but finds the operator sets by
+looking at the @code{Symbol.operatorSet} property in the objects. The
+changes were done in order to simplify the implementation.
 
-@section Introduction
-
-The bigint mode is enabled with the @code{"use bigint"} directive. It
-propagates the same way as the strict mode. In bigint mode, all
-integers are considered as @code{bigint} (arbitrarily large integer,
-similar to the TC39 BigInt
-proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}})
-instead of @code{number} (floating point number). In order to be able
-to exchange data between standard and bigint modes, numbers are
-internally represented as 3 different types:
+More precisely, the following modifications were made:
 
 @itemize
 
-@item Small integer (SmallInt): 32 bit integer@footnote{Could be extended to 53 bits without changing the principle.}.
-
-@item Big integer (BigInt): arbitrarily large integer.
-
-@item Floating point number (Float).
-
-@end itemize
-
-In standard mode, the semantics of each operation is modified so that
-when it returns a @code{number}, it is either of SmallInt or
-Float. But the difference between SmallInt and Float is not observable
-in standard mode.
-
-In bigint mode, each operation behaves differently whether its
-operands are integer or float. The difference between SmallInt and
-BigInt is not observable (i.e. they are both integers).
-
-The following table summarizes the observable types:
-
-@multitable @columnfractions .3 .3 .3
-@headitem Internal type @tab Observable type@* (standard mode) @tab Observable type@* (bigint mode)
-@item SmallInt @tab number @tab bigint
-@item BigInt @tab bigint @tab bigint
-@item Float @tab number @tab number
-@end multitable
-
-@section Changes that introduce incompatibilities with Javascript
-
-@subsection Standard mode
-
-There is no incompatibility with Javascript.
-
-@subsection Bigint mode
-
-The following changes are visible:
-
-@itemize
-
-@item Integer and Float are different types. Constants are typed. For example: @code{typeof 1.0 === "number"} and @code{typeof 1 === "bigint"}. Another consequence is that @code{1.0 === 1} is false.
-
-@item The range of integers is unlimited. In standard mode: @code{2**53 + 1 === 2**53}. This is no longer true with the bignum extensions.
-
-@item Binary bitwise operators do not truncate to 32 bits i.e. @code{0x800000000 | 1 === 0x800000001} while it gives @code{1} in standard mode.
+@item @code{with operators from} is not supported. Operator overloading is always enabled.
 
-@item Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with @code{0x1f} i.e. @code{1 << 32 === 4294967296} while it gives @code{1} in standard mode. However, the @code{>>>} operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior@footnote{The unsigned right right operator could be removed in bigint mode.}.
+@item The dispatch is not based on a static @code{[[OperatorSet]]} field in all instances. Instead, a dynamic lookup the of the @code{Symbol.operatorSet} property is done. This property is typically added in the prototype of each object.
 
-@item Operators with integer operands never return the minus zero floating point value as result. Hence @code{Object.is(0, -0) === true}. Use @code{-0.0} to create a minus zero floating point value.
+@item @code{Operators.create(...dictionaries)} is used to create a new OperatorSet object. The @code{Operators} function is supported as an helper to be closer to the TC39 proposal.
 
-@item The @code{ToPrimitive} abstract operation is called with the @code{"integer"} preferred type when an integer is required (e.g. for bitwise binary or shift operations).
+@item @code{[]} cannot be overloaded.
 
-@item The prototype of integers is no longer @code{Number.prototype}. Instead@* @code{Object.getPrototypeOf(1) === BigInt.prototype}. The prototype of floats remains Number.prototype.
+@item In math mode, the BigInt division and power operators can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}.
 
-@item If the TC39 BigInt proposal is supported, there is no observable difference between integers and @code{bigint}s.
-
-@end itemize
-
-@section Operators
-
-@subsection Arithmetic operators
-
-The operands are converted to number values as in normal
-Javascript. Then the general case is that an Integer is returned if
-both operands are Integer. Otherwise, a float is returned.
-
-The @code{+} operator also accepts strings as input and behaves like
-standard Javascript in this case.
-
-The binary operator @code{%} returns the truncated remainder of the
-division. When the result is an Integer type, a dividend of zero yields a
-RangeError exception.
-
-The binary operator @code{%} in math mode returns the Euclidian
-remainder of the division i.e. it is always positive. 
-
-The binary operator @code{/} returns a float.
-
-The binary operator @code{/} in math mode returns a float if one of
-the operands is float. Otherwise, @code{BigInt[Symbol.operatorDiv]} is
-invoked.
-
-The returned type of @code{a ** b} is Float if @math{a} or @math{b}
-are Float. If @math{a} and @math{b} are integers:
-@itemize
-@item @math{b < 0} returns a Float in bigint mode. In math mode, @code{BigInt[Symbol.operatorPow]} is invoked.
-
-@item @math{b >= 0} returns an integer.
 @end itemize
 
-The unary @code{-} and unary @code{+} return the same type as their
-operand. They performs no floating point rounding when the result is a
-float.
-
-The unary operators @code{++} and @code{--} return the same type as
-their operand.
-
-In standard mode:
-
-If the operator returns an Integer and that the result fits a
-SmallInt, it is converted to SmallInt. Otherwise, the Integer is
-converted to a Float.
-
-In bigint mode:
-
-If the operator returns an Integer and that the result fits a
-SmallInt, it is converted to SmallInt. Otherwise it is a BigInt.
-
-@subsection Logical operators
-
-In standard mode:
-
-The operands have their standard behavior. If the result fits a
-SmallInt it is converted to a SmallInt. Otherwise it is a Float.
-
-In bigint mode:
-
-The operands are converted to integer values. The floating point
-values are converted to integer by rounding them to zero.
-
-The logical operators are defined assuming the integers are
-represented in two complement notation.
-
-For @code{<<} and @code{<<}, the shift can be positive or negative. So
-@code{a << b} is defined as @math{\lfloor a/2^{-b} \rfloor} and
-@code{a >> b} is defined as @math{\lfloor a/2^{b} \rfloor}.
-
-The operator @code{>>>} is supported for backward compatibility and
-behaves the same way as Javascript i.e. implicit conversion to @code{Uint32}.
+@chapter BigInt extensions
 
-If the result fits a SmallInt it is converted to a SmallInt. Otherwise
-it is a BigInt.
-
-@subsection Relational operators
-
-The relational operators <, <=, >, >=, ==, != work as expected with
-integers and floating point numbers (e.g. @code{1.0 == 1} is true).
-
-The strict equality operators === and !== have the usual Javascript
-semantics. In particular, different types never equal, so @code{1.0
-=== 1} is false.
-
-@section Number literals
-
-Number literals in bigint mode have a slightly different behavior than
-in standard Javascript:
-
-@enumerate
-
-@item
-A number literal without a decimal point or an exponent is considered
-as an Integer. Otherwise it is a Float.
-
-@item
-Hexadecimal, octal or binary floating point literals are accepted with
-a decimal point or an exponent. The exponent is specified with the
-@code{p} letter assuming a base 2. The same convention is used by
-C99. Example: @code{0x1p3} is the same as @code{8.0}.
-
-@end enumerate
-
-@section Builtin Object changes
-
-@subsection @code{BigInt} function
-
-The @code{BigInt} function cannot be invoked as a constructor. When
-invoked as a function, it converts its first parameter to an
-integer. When a floating point number is given as parameter, it is
-truncated to an integer with infinite precision.
-
-@code{BigInt} properties:
+A few properties are added to the BigInt object:
 
 @table @code
 
-@item asIntN(bits, a)
-Set @math{b=a \pmod{2^{bits}}}. Return @math{b} if @math{b < 2^{bits-1}}
-otherwise @math{b-2^{bits}}.
-
-@item asUintN(bits, a)
-Return @math{a \pmod{2^{bits}}}.
-
 @item tdiv(a, b)
 Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError
 exception.
@@ -410,58 +114,7 @@ Return the number of trailing zeros in the two's complement binary representatio
 
 @end table
 
-@subsection @code{BigInt.prototype}
-
-It is a normal object.
-
-@subsection @code{Number} constructor
-
-The number constructor returns its argument rounded to a Float using
-the global floating point environment. In bigint mode, the Number
-constructor returns a Float. In standard mode, it returns a SmallInt
-if the value fits it, otherwise a Float.
-
-@subsection @code{Number.prototype}
-
-The following properties are modified:
-
-@table @code
-@item toString(radix)
-
-In bigint mode, integers are converted to the specified radix with
-infinite precision.
-
-@item toPrecision(p)
-@item toFixed(p)
-@item toExponential(p)
-
-In bigint mode, integers are accepted and converted to string with
-infinite precision.
-
-@item parseInt(string, radix)
-
-In bigint mode, an integer is returned and the conversion is done with
-infinite precision.
-
-@end table
-
-@subsection @code{Math} object
-
-The following properties are modified:
-
-@table @code
-@item abs(x)
-Absolute value. Return an integer if @code{x} is an Integer. Otherwise
-return a Float. No rounding is performed.
-
-@item min(a, b)
-@item max(a, b)
-No rounding is performed. The returned type is the same one as the
-minimum (resp. maximum) value.
-
-@end table
-
-@chapter Arbitrarily large floating point numbers
+@chapter BigFloat
 
 @section Introduction
 
@@ -590,6 +243,9 @@ point number @code{a} according to the floating point environment
 @item trunc(x)
 Round to an integer. No additional rounding is performed.
 
+@item abs(x)
+Return the absolute value of x. No additional rounding is performed.
+
 @item fmod(x, y[, e])
 @item remainder(x, y[, e])
 Floating point remainder. The quotient is truncated to zero (fmod) or
@@ -620,6 +276,9 @@ number. @code{e} is an optional floating point environment.
 The following properties are modified:
 
 @table @code
+@item valueOf()
+Return the bigfloat primitive value corresponding to @code{this}.
+
 @item toString(radix)
 
 For floating point numbers:
@@ -636,13 +295,16 @@ the global precision and round to nearest gives the same number.
 
 @end itemize
 
-@item toPrecision(p[, rnd_mode])
-@item toFixed(p[, rnd_mode])
-@item toExponential(p[, rnd_mode])
+The exponent letter is @code{e} for base 10, @code{p} for bases 2, 8,
+16 with a binary exponent and @code{@@} for the other bases.
+
+@item toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
+@item toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
+@item toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
 Same semantics as the corresponding @code{Number} functions with
 BigFloats. There is no limit on the accepted precision @code{p}. The
-rounding mode can be optionally specified. It is set by default to
-@code{BigFloatEnv.RNDNA}.
+rounding mode and radix can be optionally specified. The radix must be
+between 2 and 36.
 
 @end table
 
@@ -679,13 +341,13 @@ subnormal flags is set to @code{false}. If @code{rndMode} is
 
 @item prec
 Getter. Return the precision in bits of the global floating point
-environment. The initial value is @code{53}.
+environment. The initial value is @code{113}.
 
 @item expBits
 Getter. Return the exponent size in bits of the global floating point
 environment assuming an IEEE 754 representation. If @code{expBits <
 expBitsMax}, then subnormal numbers are supported. The initial value
-is @code{11}.
+is @code{15}.
 
 @item setPrec(f, p[, e])
 Set the precision of the global floating point environment to @code{p}
@@ -693,15 +355,13 @@ and the exponent size to @code{e} then call the function
 @code{f}. Then the Float precision and exponent size are reset to
 their precious value and the return value of @code{f} is returned (or
 an exception is raised if @code{f} raised an exception). If @code{e}
-is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @code{p}
-must be >= 53 and @code{e} must be >= 11 so that the global precision
-is at least equivalent to the IEEE 754 64 bit doubles.
+is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}.
 
 @item precMin
 Read-only integer. Return the minimum allowed precision. Must be at least 2.
 
 @item precMax
-Read-only integer. Return the maximum allowed precision. Must be at least 53.
+Read-only integer. Return the maximum allowed precision. Must be at least 113.
 
 @item expBitsMin
 Read-only integer. Return the minimum allowed exponent size in
@@ -709,7 +369,7 @@ bits. Must be at least 3.
 
 @item expBitsMax
 Read-only integer. Return the maximum allowed exponent size in
-bits. Must be at least 11.
+bits. Must be at least 15.
 
 @item RNDN
 Read-only integer. Round to nearest, with ties to even rounding mode.
@@ -726,8 +386,8 @@ Read-only integer. Round to +Infinity rounding mode.
 @item RNDNA
 Read-only integer. Round to nearest, with ties away from zero rounding mode.
 
-@item RNDNU
-Read-only integer. Round to nearest, with ties to +Infinity rounding mode.
+@item RNDA
+Read-only integer. Round away from zero rounding mode.
 
 @item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.}
 Read-only integer. Faithful rounding mode. The result is
@@ -767,69 +427,166 @@ Getter and setter (Boolean). Status flags.
 
 @end table
 
-@subsection @code{Math} object
+@chapter BigDecimal
 
-The following properties are modified:
+This extension adds the @code{BigDecimal} primitive type. The
+@code{BigDecimal} type represents floating point numbers in base
+10. It is inspired from the proposal available at
+@url{https://github.com/littledan/proposal-bigdecimal}.
+
+The @code{BigDecimal} floating point numbers are always normalized and
+finite. There is no concept of @code{-0}, @code{Infinity} or
+@code{NaN}. By default, all the computations are done with infinite
+precision.
+
+@section Operators
+
+The following builtin operators support BigDecimal:
 
 @table @code
-@item abs(x)
-Absolute value. If @code{x} is a BigFloat, its absolute value is
-returned as a BigFloat. No rounding is performed.
 
-@item min(a, b)
-@item max(a, b)
-The returned type is the same one as the minimum (resp. maximum)
-value, so @code{BigFloat} values are accepted. When a @code{BigFloat}
-is returned, no rounding is performed.
+@item +
+@item -
+@item *
+Both operands must be BigDecimal. The result is computed with infinite
+precision.
+@item %
+Both operands must be BigDecimal. The result is computed with infinite
+precision. A range error is throws in case of division by zero.
 
-@end table
+@item /
+Both operands must be BigDecimal. A range error is throws in case of
+division by zero or if the result cannot be represented with infinite
+precision (use @code{BigDecimal.div} to specify the rounding).
 
-@chapter Math mode
+@item **
+Both operands must be BigDecimal. The exponent must be a positive
+integer. The result is computed with infinite precision.
 
-@section Introduction
+@item ===
+When one of the operand is a BigDecimal, return true if both operands
+are a BigDecimal and if they are equal.
 
-A new @emph{math mode} is enabled with the @code{"use math"}
-directive. @code{"use bigint"} is implied in math mode. With this
-mode, writing mathematical expressions is more intuitive, exact
-results (e.g. fractions) can be computed for all operators and floating
-point literals have the @code{BigFloat} type by default.
+@item ==
+@item !=
+@item <=
+@item >=
+@item <
+@item >
 
-It propagates the same way as the @emph{strict mode}. In
-this mode:
+Numerical comparison. When one of the operand is not a BigDecimal, it is
+converted to BigDecimal by using ToString(). Hence comparisons between
+Number and BigDecimal do not use the exact mathematical value of the
+Number value.
 
-@itemize
+@end table
 
-@item The @code{^} operator is a similar to the power operator (@code{**}).
+@section BigDecimal literals
 
-@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}.
+BigDecimal literals are decimal floating point numbers with a trailing
+@code{m} suffix.
 
-@item The logical xor operator is still available with the @code{^^} operator.
+@section Builtin Object changes
 
-@item The division operator invokes @code{BigInt[Symbol.operatorDiv]} in case both operands are integers.
+@subsection The @code{BigDecimal} function.
 
-@item The power operator invokes @code{BigInt[Symbol.operatorPow]} in case both operands are integers and the exponent is strictly negative.
+It returns @code{0m} if no parameter is provided. Otherwise the first
+parameter is converted to a bigdecimal by using ToString(). Hence
+Number value are not converted to their exact numerical value as
+BigDecimal.
 
-@item The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder.
+@subsection Properties of the @code{BigDecimal} object
 
-@item Floating point literals are @code{BigFloat} by default (i.e. a @code{l} suffix is implied).
+@table @code
 
-@end itemize
+@item add(a, b[, e])
+@item sub(a, b[, e])
+@item mul(a, b[, e])
+@item div(a, b[, e])
+@item mod(a, b[, e])
+@item sqrt(a, e)
+@item round(a, e)
+Perform the specified floating point operation and round the floating
+point result according to the rounding object @code{e}. If the
+rounding object is not present, the operation is executed with
+infinite precision.
 
-@section Builtin Object changes
+For @code{div}, a @code{RangeError} exception is thrown in case of
+division by zero or if the result cannot be represented with infinite
+precision if no rounding object is present.
+
+For @code{sqrt}, a range error is thrown if @code{a} is less than
+zero.
+
+The rounding object must contain the following properties:
+@code{roundingMode} is a string specifying the rounding mode
+(@code{"floor"}, @code{"ceiling"}, @code{"down"}, @code{"up"},
+@code{"half-even"}, @code{"half-up"}). Either
+@code{maximumSignificantDigits} or @code{maximumFractionDigits} must
+be present to specify respectively the number of significant digits
+(must be >= 1) or the number of digits after the decimal point (must
+be >= 0).
 
-@subsection @code{Symbol} constructor
+@end table
+
+@subsection Properties of the @code{BigDecimal.prototype} object
 
-The following global symbol is added for the operator overloading:
 @table @code
-@item operatorMathMod
+@item valueOf()
+Return the bigdecimal primitive value corresponding to @code{this}.
+
+@item toString()
+Convert @code{this} to a string with infinite precision in base 10.
+
+@item toPrecision(p, rnd_mode = "half-up")
+@item toFixed(p, rnd_mode = "half-up")
+@item toExponential(p, rnd_mode = "half-up")
+Convert the BigDecimal @code{this} to string with the specified
+precision @code{p}. There is no limit on the accepted precision
+@code{p}. The rounding mode can be optionally
+specified. @code{toPrecision} outputs either in decimal fixed notation
+or in decimal exponential notation with a @code{p} digits of
+precision.  @code{toExponential} outputs in decimal exponential
+notation with @code{p} digits after the decimal point. @code{toFixed}
+outputs in decimal notation with @code{p} digits after the decimal
+point.
+
 @end table
 
-@section Remaining issues
+@chapter Math mode
 
-@enumerate
+A new @emph{math mode} is enabled with the @code{"use math"}
+directive. It propagates the same way as the @emph{strict mode}. It is
+designed so that arbitrarily large integers and floating point numbers
+are available by default. In order to minimize the number of changes
+in the Javascript semantics, integers are represented either as Number
+or BigInt depending on their magnitude. Floating point numbers are
+always represented as BigFloat.
+
+The following changes are made to the Javascript semantics:
 
-@item A new floating point literal suffix could be added for @code{Number} literals.
+@itemize
 
-@end enumerate
+@item Floating point literals (i.e. number with a decimal point or an exponent) are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). Hence @code{typeof 1.0 === "bigfloat"}.
+
+@item Integer literals (i.e. numbers without a decimal point or an exponent) with or without the @code{n} suffix are @code{BigInt} if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to @code{2**53-1}. Hence @code{typeof 1 === "number "}, @code{typeof 1n === "number"} but @code{typeof 9007199254740992 === "bigint" }.
+
+@item All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt.
+
+@item The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result.
+
+@item The @code{^} operator is an alias to the power operator (@code{**}).
+
+@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}.
+
+@item The logical xor operator is still available with the @code{^^} operator.
+
+@item The integer division operator can be overloaded by modifying the corresponding operator in @code{BigInt.prototype.[[OperatorSet]]}.
+
+@item The integer power operator with a non zero negative exponent can be overloaded by modifying the corresponding operator in @code{BigInt.prototype.[[OperatorSet]]}.
+
+@item The modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder.
+
+@end itemize
 
 @bye
index f3522d7734f3e72351540c7d80b0a6858dc398ec..11657a3b481ccde5f55490af823acc3d5c1eece1 100644 (file)
@@ -1,7 +1,8 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html>
-<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
+<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
 <head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <title>QuickJS Javascript Engine</title>
 
 <meta name="description" content="QuickJS Javascript Engine">
@@ -9,7 +10,6 @@
 <meta name="resource-type" content="document">
 <meta name="distribution" content="global">
 <meta name="Generator" content="makeinfo">
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link href="#SEC_Contents" rel="contents" title="Table of Contents">
 <style type="text/css">
 <!--
@@ -206,7 +206,7 @@ Javascript files and/or expressions as arguments to execute them:
 <a name="qjs-interpreter"></a>
 <h4 class="subsection">2.3.1 <code>qjs</code> interpreter</h4>
 
-<pre class="verbatim">usage: qjs [options] [files]
+<pre class="verbatim">usage: qjs [options] [file [args]]
 </pre>
 <p>Options are:
 </p><dl compact="compact">
@@ -241,6 +241,11 @@ source is <code>import</code>.
 the <code>&quot;use bigint&quot;</code> and <code>&quot;use math&quot;</code> directives.
 </p>
 </dd>
+<dt><code>-I file</code></dt>
+<dt><code>--include file</code></dt>
+<dd><p>Include an additional file.
+</p>
+</dd>
 </dl>
 
 <p>Advanced options are:
@@ -767,6 +772,10 @@ pathname of <code>path</code> and <code>err</code> the error code.
 and <code>err</code> the error code.
 </p>
 </dd>
+<dt><code>chdir(path)</code></dt>
+<dd><p>Change the current directory. Return the error code.
+</p>
+</dd>
 <dt><code>mkdir(path, mode = 0o777)</code></dt>
 <dd><p>Create a directory at <code>path</code>. Return the error code.
 </p>
index 68352cfc33d26e37707a87ba001545ec9255825e..99bebfc108d2eb2c026aa7c50c7b84ae4cacc255 100644 (file)
Binary files a/doc/quickjs.pdf and b/doc/quickjs.pdf differ
index 696a52dcc62270eafb3e247171a536d35c8aabad..aba1ab53e69165a1b8db0d87557f1202532bda7d 100644 (file)
@@ -92,7 +92,7 @@ generates a @code{hello} executable with no external dependency.
 @subsection @code{qjs} interpreter
 
 @verbatim
-usage: qjs [options] [files]
+usage: qjs [options] [file [args]]
 @end verbatim
 
 Options are:
@@ -122,6 +122,10 @@ Load as ES6 script (default=autodetect).
 Enable the bignum extensions: BigDecimal object, BigFloat object and
 the @code{"use bigint"} and @code{"use math"} directives.
 
+@item -I file
+@item --include file
+Include an additional file.
+
 @end table
 
 Advanced options are:
@@ -576,6 +580,9 @@ pathname of @code{path} and @code{err} the error code.
 Return @code{[str, err]} where @code{str} is the current working directory
 and @code{err} the error code.
 
+@item chdir(path)
+Change the current directory. Return the error code.
+
 @item mkdir(path, mode = 0o777)
 Create a directory at @code{path}. Return the error code.
 
index 7d5eccf4a1b4de06b8b7921e1a2a77545d9f03c0..6a416b7931b764a2a59cc37af0867029f17b03f0 100644 (file)
@@ -6,10 +6,10 @@
 
 /* compute PI with a precision of 'prec' digits */
 function calc_pi(prec) {
-    const CHUD_A = 13591409d;
-    const CHUD_B = 545140134d;
-    const CHUD_C = 640320d;
-    const CHUD_C3 = 10939058860032000d; /* C^3/24 */
+    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] */
@@ -17,7 +17,7 @@ function calc_pi(prec) {
         var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
         if (a == (b - 1n)) {
             b1 = BigDecimal(b);
-            G = (2d * b1 - 1d) * (6d * b1 - 1d) * (6d * b1 - 5d);
+            G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
             P = G * (CHUD_B * b1 + CHUD_A);
             if (b & 1n)
                 P = -P;
@@ -32,7 +32,7 @@ function calc_pi(prec) {
             if (need_G)
                 G = G1 * G2;
             else
-                G = 0d;
+                G = 0m;
         }
         return [P, Q, G];
     }
@@ -44,7 +44,7 @@ function calc_pi(prec) {
     Q = BigDecimal.div(Q, (P + Q * CHUD_A),
                        { roundingMode: "half-even",
                          maximumSignificantDigits: prec });
-    G = (CHUD_C / 12d) * BigDecimal.sqrt(CHUD_C,
+    G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
                                          { roundingMode: "half-even",
                                            maximumSignificantDigits: prec });
     return Q * G;
@@ -64,7 +64,5 @@ function calc_pi(prec) {
     /* we add more digits to reduce the probability of bad rounding for
        the last digits */
     r = calc_pi(n_digits + 20);
-    r = BigDecimal.round(r, { roundingMode: "down",
-                              maximumFractionDigits: n_digits })
-    print(r);
+    print(r.toFixed(n_digits, "down"));
 })();
diff --git a/libbf.c b/libbf.c
index 25e856d222d081f0200316b872a36f74a5153283..829d8031c1948bc3548f493c15aa33b7b9583cfd 100644 (file)
--- a/libbf.c
+++ b/libbf.c
@@ -86,7 +86,6 @@ static no_inline int fft_mul(bf_context_t *s,
 static void fft_clear_cache(bf_context_t *s);
 #endif
 #ifdef USE_BF_DEC
-static void mp_pow_init(void);
 static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos);
 #endif
 
@@ -154,6 +153,17 @@ static inline limb_t smod(slimb_t a, slimb_t 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;
+}
+
 #define malloc(s) malloc_is_forbidden(s)
 #define free(p) free_is_forbidden(p)
 #define realloc(p, s) realloc_is_forbidden(p, s)
@@ -164,9 +174,6 @@ void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
     memset(s, 0, sizeof(*s));
     s->realloc_func = realloc_func;
     s->realloc_opaque = realloc_opaque;
-#ifdef USE_BF_DEC
-    mp_pow_init();
-#endif
 }
 
 void bf_context_end(bf_context_t *s)
@@ -422,18 +429,13 @@ static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l,
         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;
-    case BF_RNDNU:
-        if (bit1) {
-            if (r->sign)
-                add_one = bit0;
-            else
-                add_one = 1;
-        }
-        break;
     default:
         abort();
     }
@@ -452,7 +454,7 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags)
     if (prec == BF_PREC_INF ||
         rnd_mode == BF_RNDN ||
         rnd_mode == BF_RNDNA ||
-        rnd_mode == BF_RNDNU ||
+        rnd_mode == BF_RNDA ||
         (rnd_mode == BF_RNDD && sign == 1) ||
         (rnd_mode == BF_RNDU && sign == 0)) {
         bf_set_inf(r, sign);
@@ -476,12 +478,14 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags)
 
 /* 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). Can fail with BF_ST_MEM_ERROR in case of
+   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)
+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, ret, rnd_mode;
+    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 */
@@ -506,7 +510,6 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
 
     /* round to prec bits */
     rnd_mode = flags & BF_RND_MASK;
-    ret = 0;
     add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode);
     
     if (prec <= 0) {
@@ -615,7 +618,7 @@ int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags)
             }
             r->expn -= shift;
         }
-        ret = __bf_round(r, prec1, flags, l);
+        ret = __bf_round(r, prec1, flags, l, 0);
     }
     //    bf_print_str("r_final", r);
     return ret;
@@ -638,8 +641,7 @@ int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k)
     }
     if (a->expn == BF_EXP_ZERO)
         return FALSE;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA ||
-               rnd_mode == BF_RNDNU);
+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
     if (k < (prec + 2))
         return FALSE;
     bit_pos = a->len * LIMB_BITS - 1 - prec;
@@ -666,7 +668,7 @@ 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);
+    return __bf_round(r, prec, flags, r->len, 0);
 }
 
 /* for debugging */
@@ -780,51 +782,26 @@ int bf_cmp_full(const bf_t *a, const bf_t *b)
     return res;
 }
 
-#define BF_CMP_EQ 1
-#define BF_CMP_LT 2
-#define BF_CMP_LE 3
-
-static int bf_cmp(const bf_t *a, const bf_t *b, int op)
+/* 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)
 {
-    BOOL is_both_zero;
     int res;
     
-    if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN)
-        return 0;
-    if (a->sign != b->sign) {
-        is_both_zero = (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO);
-        if (is_both_zero) {
-            return op & BF_CMP_EQ;
-        } else if (op & BF_CMP_LT) {
-            return a->sign;
-        } else {
-            return FALSE;
-        }
+    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 (res == 0) {
-            return op & BF_CMP_EQ;
-        } else if (op & BF_CMP_LT) {
-            return (res < 0) ^ a->sign;
-        } else {
-            return FALSE;
-        }
+        if (a->sign)
+            res = -res;
     }
-}
-
-int bf_cmp_eq(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b, BF_CMP_EQ);
-}
-
-int bf_cmp_le(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b, BF_CMP_LE);
-}
-
-int bf_cmp_lt(const bf_t *a, const bf_t *b)
-{
-    return bf_cmp(a, b, BF_CMP_LT);
+    return res;
 }
 
 /* Compute the number of bits 'n' matching the pattern:
@@ -1631,11 +1608,11 @@ 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_EXP_BITS_MAX) - 1;
+    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);
+    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,
@@ -1777,8 +1754,7 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
     }
 
     q_sign = a->sign ^ b->sign;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA ||
-               rnd_mode == BF_RNDNU);
+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
     switch(rnd_mode) {
     default:
     case BF_RNDZ:
@@ -1792,12 +1768,12 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
     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;
-    case BF_RNDNU:
-        /* XXX: unsupported yet */
-        abort();
     }
 
     a1->expn = a->expn;
@@ -1845,26 +1821,14 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
     return BF_ST_MEM_ERROR;
 }
 
-int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-            bf_flags_t flags)
+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, BF_RNDZ);
-    bf_delete(q);
-    return ret;
-}
-
-int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                 bf_flags_t flags)
-{
-    bf_t q_s, *q = &q_s;
-    int ret;
-    
-    bf_init(r->ctx, q);
-    ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN);
+    ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);
     bf_delete(q);
     return ret;
 }
@@ -1879,13 +1843,13 @@ static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags)
 }
 
 int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-              bf_flags_t flags)
+              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, BF_RNDN);
+    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;
@@ -2791,14 +2755,14 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
             /* 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);
+            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);
+                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);
+                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) &&
@@ -2807,6 +2771,8 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
                 /* 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;
             }
@@ -3062,7 +3028,7 @@ static int bf_atof_internal(bf_t *r, slimb_t *pexponent,
             c = to_digit(*p);
             if (c >= 10)
                 break;
-            if (unlikely(expn > ((EXP_MAX - 2 - 9) / 10))) {
+            if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
                 /* exponent overflow */
                 if (exp_is_neg) {
                     bf_set_zero(r, is_neg);
@@ -3501,11 +3467,14 @@ static int bf_convert_to_radix(bf_t *r, slimb_t *pE,
             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);
+            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);
+                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);
+                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
@@ -3605,7 +3574,15 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
         pos = a->len;
         pos_incr = 1;
         first_buf_pos = 0;
-    } else if ((radix & (radix - 1)) != 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];
@@ -3619,13 +3596,6 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
         pos = n;
         pos_incr = 1;
         first_buf_pos = pos * digits_per_limb - n_digits;
-    } else {
-        a = (bf_t *)a1;
-        radix_bits = ceil_log2(radix);
-        digits_per_limb = LIMB_BITS / radix_bits;
-        pos_incr = digits_per_limb * radix_bits;
-        pos = a->len * LIMB_BITS - a->expn + n_digits * radix_bits;
-        first_buf_pos = 0;
     }
     buf_pos = digits_per_limb;
     i = 0;
@@ -3668,12 +3638,13 @@ static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size)
 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, a2->ctx, bf_dbuf_realloc);
+    dbuf_init2(s, ctx, bf_dbuf_realloc);
     if (a2->expn == BF_EXP_NAN) {
         dbuf_putstr(s, "NaN");
     } else {
@@ -3687,77 +3658,160 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,
         } else {
             int fmt, ret;
             slimb_t n_digits, n, i, n_max, n1;
-            bf_t a1_s, *a1;
-            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;
-            
+            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;
-            a1 = &a1_s;
-            bf_init(a2->ctx, a1);
+            bf_init(ctx, a1);
             if (fmt == BF_FTOA_FORMAT_FRAC) {
-                size_t pos, start;
-                assert(!is_dec);
-                /* 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);
+                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 = a->len * LIMB_DIGITS;
-                        /* remove trailing zero digits */
+                        prec = n_digits = a1->len * LIMB_DIGITS;
+                        /* remove the trailing zero digits */
                         while (n_digits > 1 &&
-                               get_digit(a->tab, a->len, prec - n_digits) == 0) {
+                               get_digit(a1->tab, a1->len, prec - n_digits) == 0) {
                             n_digits--;
                         }
                         n_max = n_digits + 4;
                     }
-                    bf_init(a2->ctx, a1);
-                    bf_set(a1, a);
                     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;
                         
-                        if (prec == BF_PREC_INF) {
-                            assert(radix_bits != 0);
-                            /* XXX: could use the exact number of bits */
-                            prec = a->len * LIMB_BITS;
-                        }
+                        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
@@ -3771,7 +3825,7 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,
                             /* XXX: inefficient */
                             n_digits_max = n_digits;
                             n_digits_min = 1;
-                            bf_init(a2->ctx, b);
+                            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,
@@ -3869,7 +3923,7 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,
         *plen = s->size - 1;
     return (char *)s->buf;
  fail:
-    bf_free(a2->ctx, s->buf);
+    bf_free(ctx, s->buf);
     if (plen)
         *plen = 0;
     return NULL;
@@ -4035,7 +4089,7 @@ static void bf_const_pi_internal(bf_t *Q, limb_t prec)
 
 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))
+                        void (*func)(bf_t *res, limb_t prec), int sign)
 {
     limb_t ziv_extra_bits, prec1;
 
@@ -4051,6 +4105,7 @@ static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags,
             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);
@@ -4070,13 +4125,20 @@ static void bf_const_free(BFConstCache *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);
+    return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0);
 }
 
-int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags)
+/* 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);
+    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)
@@ -4126,9 +4188,29 @@ static int bf_ziv_rounding(bf_t *r, const bf_t *a,
                 break;
             }
             ziv_extra_bits = ziv_extra_bits * 2;
+            //            printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits);
         }
     }
-    return bf_round(r, prec, flags) | ret;
+    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'.
@@ -4193,18 +4275,70 @@ static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
     
     /* undo the range reduction */
     for(i = 0; i < K; i++) {
-        bf_mul(r, r, r, prec1, BF_RNDN);
+        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_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) {
@@ -4220,48 +4354,15 @@ int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
         return 0;
     }
 
-    /* crude overflow and underflow tests */
-    if (a->expn > 0) {
-        bf_t T_s, *T = &T_s;
-        bf_t log2_s, *log2 = &log2_s;
-        slimb_t e_min, e_max;
-        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 > e_max * log(2) implies exp(a) > e_max */
-        if (bf_cmp_lt(T, a) > 0) {
-            /* overflow */
-            bf_delete(T);
-            bf_delete(log2);
-            return bf_set_overflow(r, 0, prec, flags);
-        }
-        /* a < e_min * log(2) implies exp(a) < e_min */
-        bf_mul_si(T, log2, e_min, LIMB_BITS, BF_RNDD);
-        if (bf_cmp_lt(a, 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);
+    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);
 }
 
@@ -4404,7 +4505,6 @@ int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
 }
 
 /* x and y finite and x > 0 */
-/* XXX: overflow/underflow handling */
 static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
 {
     bf_context_t *s = r->ctx;
@@ -4415,15 +4515,17 @@ static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
     bf_init(s, T);
     /* XXX: proof for the added precision */
     prec1 = prec + 32;
-    bf_log(T, x, prec1, BF_RNDF);
-    bf_mul(T, T, y, prec1, BF_RNDF);
-    bf_exp(r, T, prec1, BF_RNDF);
+    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 */
-/* XXX: overflow/underflow handling */ 
 static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
 {
     bf_context_t *s = r->ctx;
@@ -4438,11 +4540,11 @@ static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
         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);
+    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);
+        ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
         bf_delete(T);
     }
     return ret;
@@ -4508,7 +4610,7 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags)
             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_QUICKS) &&
+            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 &&
@@ -4565,70 +4667,95 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags)
     if (bf_cmp_eq(T, r)) {
         /* abs(x) = 1: nothing more to do */
         ret = 0;
-    } else 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 {
+        /* 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 (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)
+                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);
             }
-            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 */
+        } 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=%ld\n", -y_emin);
-                bf_print_str("T", T);
-                bf_print_str("r", r);
+                    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;
+                    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);
         }
-    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;
@@ -4649,7 +4776,7 @@ static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1)
     bf_delete(T);
 }
 
-int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)
+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;
@@ -4659,27 +4786,6 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)
     int is_neg;
     
     assert(c != a && s != a);
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            if (c)
-                bf_set_nan(c);
-            if (s)
-                bf_set_nan(s);
-            return 0;
-        } else if (a->expn == BF_EXP_INF) {
-            if (c)
-                bf_set_nan(c);
-            if (s)
-                bf_set_nan(s);
-            return BF_ST_INVALID_OP;
-        } else {
-            if (c)
-                bf_set_ui(c, 1);
-            if (s)
-                bf_set_zero(s, a->sign);
-            return 0;
-        }
-    }
 
     bf_init(s1, T);
     bf_init(s1, U);
@@ -4699,10 +4805,10 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)
         slimb_t cancel;
         cancel = 0;
         for(;;) {
-            prec2 = prec1 + cancel;
+            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_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)))
@@ -4774,6 +4880,30 @@ static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
 
 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);
 }
 
@@ -4784,15 +4914,6 @@ static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
 
 int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t 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;
-    
     if (a->len == 0) {
         if (a->expn == BF_EXP_NAN) {
             bf_set_nan(r);
@@ -4806,6 +4927,26 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
         }
     }
 
+    /* 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);
@@ -4817,6 +4958,31 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
 
 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);
 }
 
@@ -4834,50 +5000,15 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec,
     int cmp_1;
     slimb_t prec1, i, K, l;
     
-    if (a->len == 0) {
-        if (a->expn == BF_EXP_NAN) {
-            bf_set_nan(r);
-            return 0;
-        } else {
-            if (a->expn == BF_EXP_INF)
-                i = 1 - 2 * a->sign;
-            else
-                i = 0;
-            i += add_pi2;
-            /* return i*(pi/2) with -1 <= i <= 2 */
-            if (i == 0) {
-                bf_set_zero(r, add_pi2 ? 0 : a->sign);
-                return 0;
-            } else {
-                /* PI or PI/2 */
-                bf_const_pi(r, prec, BF_RNDF);
-                if (i != 2)
-                    bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);
-                r->sign = (i < 0);
-                return BF_ST_INEXACT;
-            }
-        }
-    }
-    
-    bf_init(s, T);
-    bf_set_ui(T, 1);
-    cmp_1 = bf_cmpu(a, T);
-    if (cmp_1 == 0 && !add_pi2) {
-        /* short cut: abs(a) == 1 -> +/-pi/4 */
-        bf_const_pi(r, prec, BF_RNDF);
-        bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ);
-        r->sign = a->sign;
-        bf_delete(T);
-        return BF_ST_INEXACT;
-    }
-
     /* XXX: precision analysis */
     K = bf_isqrt((prec + 1) / 2);
     l = prec / (2 * K) + 1;
     prec1 = prec + K + 2 * l + 32;
-    //    printf("prec=%ld K=%ld l=%ld prec1=%ld\n", prec, K, l, prec1);
+    //    printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1);
     
-    if (cmp_1 > 0) {
+    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 {
@@ -4945,6 +5076,47 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec,
 
 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);
 }
 
@@ -4998,38 +5170,6 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
     BOOL is_acos = (BOOL)(intptr_t)opaque;
     bf_t T_s, *T = &T_s;
     limb_t prec1, prec2;
-    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 {
-            if (is_acos) {
-                bf_const_pi(r, prec, BF_RNDF);
-                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);
-    if (res > 0) {
-        bf_delete(T);
-        bf_set_nan(r);
-        return BF_ST_INVALID_OP;
-    } else if (res == 0 && a->sign == 0 && is_acos) {
-        bf_set_zero(r, 0);
-        bf_delete(T);
-        return 0;
-    }
     
     /* asin(x) = atan(x/sqrt(1-x^2)) 
        acos(x) = pi/2 - asin(x) */
@@ -5041,6 +5181,7 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
         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);
@@ -5056,11 +5197,76 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
 
 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);
 }
 
@@ -5124,14 +5330,14 @@ int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
 
 #endif /* LIMB_BITS != 64 */
 
-static inline limb_t shrd(limb_t low, limb_t high, long shift)
+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 limb_t shld(limb_t a1, limb_t a0, long shift)
+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));
@@ -5185,21 +5391,18 @@ do {\
 /* fast integer division by a fixed constant */
 
 typedef struct FastDivData {
-    limb_t d; /* divisor (only user visible field) */
     limb_t m1; /* multiplier */
-    int shift1;
-    int shift2;
+    int8_t shift1;
+    int8_t shift2;
 } FastDivData;
 
-#if 1
 /* From "Division by Invariant Integers using Multiplication" by
    Torborn Granlund and Peter L. Montgomery */
 /* d must be != 0 */
-static inline void fast_udiv_init(FastDivData *s, limb_t d)
+static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d)
 {
     int l;
     limb_t q, r, m1;
-    s->d = d;
     if (d == 1)
         l = 0;
     else
@@ -5225,52 +5428,78 @@ static inline limb_t fast_udiv(limb_t a, const FastDivData *s)
     return (t1 + t0) >> s->shift2;
 }
 
-static inline limb_t fast_urem(limb_t a, const FastDivData *s)
-{
-    limb_t q;
-    q = fast_udiv(a, s);
-    return a - q * s->d;
-}
+/* 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
+};
 
-#define fast_udivrem(q, r, a, s) q = fast_udiv(a, s), r = a - q * (s)->d
-    
+/* 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
-
-static inline void fast_udiv_init(FastDivData *s, limb_t d)
-{
-    s->d = d;
-}
-static inline limb_t fast_udiv(limb_t a, const FastDivData *s)
-{
-    return a / s->d;
-}
-static inline limb_t fast_urem(limb_t a, const FastDivData *s)
-{
-    return a % s->d;
-}
-
-#define fast_udivrem(q, r, a, s) q = a / (s)->d, r = a % (s)->d
-
+    { 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
+};
 
-/* contains 10^i */
-/* XXX: make it const */
-limb_t mp_pow_dec[LIMB_DIGITS + 1];
-static FastDivData mp_pow_div[LIMB_DIGITS + 1];
-
-static void mp_pow_init(void)
+/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */
+static inline limb_t fast_shr_dec(limb_t a, int shift)
 {
-    limb_t a;
-    int i;
-    
-    a = 1;
-    for(i = 0; i <= LIMB_DIGITS; i++) {
-        mp_pow_dec[i] = a;
-        fast_udiv_init(&mp_pow_div[i], a);
-        a = a * 10;
-    }
+    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)
 {
@@ -5653,7 +5882,7 @@ static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
     l = high;
     for(i = n - 1; i >= 0; i--) {
         a = tab[i];
-        fast_udivrem(q, r, a, &mp_pow_div[shift]);
+        fast_shr_rem_dec(q, r, a, shift);
         tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift];
         l = r;
     }
@@ -5671,7 +5900,7 @@ static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
     l = low;
     for(i = 0; i < n; i++) {
         a = tab[i];
-        fast_udivrem(q, r, a, &mp_pow_div[LIMB_DIGITS - shift]);
+        fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift);
         tab_r[i] = r * mp_pow_dec[shift] + l;
         l = q;
     }
@@ -6011,7 +6240,7 @@ static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos)
         return 0;
     pos = (limb_t)bit_pos / LIMB_DIGITS;
     shift = (limb_t)bit_pos % LIMB_DIGITS;
-    fast_udivrem(q, v, r->tab[pos], &mp_pow_div[shift + 1]);
+    fast_shr_rem_dec(q, v, r->tab[pos], shift + 1);
     (void)q;
     if (v != 0)
         return 1;
@@ -6032,7 +6261,7 @@ static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos)
     if (i < 0 || i >= len)
         return 0;
     shift = pos - i * LIMB_DIGITS;
-    return fast_udiv(tab[i], &mp_pow_div[shift]) % 10;
+    return fast_shr_dec(tab[i], shift) % 10;
 }
 
 #if 0
@@ -6056,7 +6285,7 @@ static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos)
             a1 = tab[i];
         else
             a1 = 0;
-        return fast_udiv(a0, &mp_pow_div[shift]) +
+        return fast_shr_dec(a0, shift) +
             fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) *
             mp_pow_dec[shift];
     }
@@ -6108,13 +6337,8 @@ static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l,
     case BF_RNDF:
         add_one = (digit1 >= 5);
         break;
-    case BF_RNDNU:
-        if (digit1 >= 5) {
-            if (r->sign)
-                add_one = (digit0 != 0);
-            else
-                add_one = 1;
-        }
+    case BF_RNDA:
+        add_one = inexact;
         break;
     default:
         abort();
@@ -6135,8 +6359,7 @@ 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;
 
-    /* e_min and e_max are computed to match the IEEE 754 conventions */
-    /* XXX: does not matter for decimal numbers */
+    /* 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;
@@ -6147,6 +6370,11 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
             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;
     }
@@ -6183,10 +6411,16 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
     
     /* check underflow */
     if (unlikely(r->expn < e_min)) {
-    underflow:
-        bfdec_set_zero(r, r->sign);
-        ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
-        return ret;
+        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 */
@@ -6202,7 +6436,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
     if (i >= 0) {
         shift = smod(bit_pos, LIMB_DIGITS);
         if (shift != 0) {
-            r->tab[i] = fast_udiv(r->tab[i], &mp_pow_div[shift]) *
+            r->tab[i] = fast_shr_dec(r->tab[i], shift) *
                 mp_pow_dec[shift];
         }
     } else {
@@ -6571,10 +6805,10 @@ static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
         /* 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 +
-                 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
+                 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;
+        precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
     }
     n = bf_max(a->len, precl);
     
@@ -6679,8 +6913,7 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
     }
 
     q_sign = a->sign ^ b->sign;
-    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA ||
-               rnd_mode == BF_RNDNU);
+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
     switch(rnd_mode) {
     default:
     case BF_RNDZ:
@@ -6694,12 +6927,12 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
     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;
-    case BF_RNDNU:
-        /* XXX: unsupported yet */
-        abort();
     }
 
     a1->expn = a->expn;
@@ -6756,14 +6989,14 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
     return BF_ST_MEM_ERROR;
 }
 
-int bfdec_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-               bf_flags_t flags)
+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, BF_RNDZ);
+    ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode);
     bfdec_delete(q);
     return ret;
 }
@@ -6779,7 +7012,7 @@ 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;
+    slimb_t n, n1, prec1;
     limb_t res;
 
     assert(r != a);
@@ -6798,9 +7031,14 @@ int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags)
         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 * (prec + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_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);
@@ -6869,7 +7107,7 @@ int bfdec_get_int32(int *pres, const bfdec_t *a)
         v = 0;
         ret = 0;
     } else if (a->expn <= 9) {
-        v = fast_udiv(a->tab[a->len - 1], &mp_pow_div[LIMB_DIGITS - a->expn]);
+        v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);
         if (a->sign)
             v = -v;
         ret = 0;
@@ -6877,7 +7115,7 @@ int bfdec_get_int32(int *pres, const bfdec_t *a)
         uint64_t v1;
         uint32_t v_max;
 #if LIMB_BITS == 64
-        v1 = fast_udiv(a->tab[a->len - 1], &mp_pow_div[LIMB_DIGITS - a->expn]);
+        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);
diff --git a/libbf.h b/libbf.h
index 500124c293e811b6d789d6e55d0e074e4b2cbfeb..60439dcac1b3342ca05ee30e3728fb8b9fbd6107 100644 (file)
--- a/libbf.h
+++ b/libbf.h
@@ -41,8 +41,8 @@ typedef unsigned __int128 uint128_t;
 typedef int64_t slimb_t;
 typedef uint64_t limb_t;
 typedef uint128_t dlimb_t;
-#define EXP_MIN INT64_MIN
-#define EXP_MAX INT64_MAX
+#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)
@@ -52,8 +52,8 @@ typedef uint128_t dlimb_t;
 typedef int32_t slimb_t;
 typedef uint32_t limb_t;
 typedef uint64_t dlimb_t;
-#define EXP_MIN INT32_MIN
-#define EXP_MAX INT32_MAX
+#define BF_RAW_EXP_MIN INT32_MIN
+#define BF_RAW_EXP_MAX INT32_MAX
 
 #define LIMB_DIGITS 9
 #define BF_DEC_BASE 1000000000U
@@ -61,10 +61,17 @@ typedef uint64_t dlimb_t;
 #endif
 
 /* in bits */
+/* minimum number of bits for the exponent */
 #define BF_EXP_BITS_MIN 3
-#define BF_EXP_BITS_MAX (LIMB_BITS - 2)
+/* 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
-#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 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
@@ -73,9 +80,9 @@ typedef uint64_t dlimb_t;
 #define BF_CHKSUM_MOD 975620677U
 #endif
 
-#define BF_EXP_ZERO EXP_MIN
-#define BF_EXP_INF (EXP_MAX - 1)
-#define BF_EXP_NAN EXP_MAX
+#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,
@@ -101,27 +108,29 @@ typedef struct {
 typedef enum {
     BF_RNDN, /* round to nearest, ties to even */
     BF_RNDZ, /* round to zero */
-    BF_RNDD, /* round to -inf */
+    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_RNDNU, /* round to nearest, ties to +inf */
+    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_MAX and prec != BF_PREC_INF. Not supported
-   for decimal floating point numbers. */
+   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(), bfdec_round() and
-   bfdev_div(). */
+   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;
 
@@ -142,12 +151,17 @@ typedef struct bf_context_t {
 
 static inline int bf_get_exp_bits(bf_flags_t flags)
 {
-    return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK);
+    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_SHIFT;
+    return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
 }
 
 /* returned status */
@@ -249,9 +263,22 @@ 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_eq(const bf_t *a, const bf_t *b);
-int bf_cmp_le(const bf_t *a, const bf_t *b);
-int bf_cmp_lt(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);
@@ -264,12 +291,10 @@ 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_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-            bf_flags_t flags);
-int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
-                 bf_flags_t flags);
+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);
+              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);
@@ -304,8 +329,8 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
 /* 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 the binary exponent and "@" for the
-   other bases. */
+   "p" for bases 2, 8, 16 with a binary exponent and "@" for the other
+   bases. */
 
 #define BF_FTOA_FORMAT_MASK (3 << 16)
 
@@ -316,17 +341,23 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
 /* fractional format: prec digits after the decimal point rounded with
    (flags & BF_RND_MASK) */
 #define BF_FTOA_FORMAT_FRAC  (1 << 16)
-/* free format: use as many digits as necessary so that bf_atof()
-   return the same number when using precision 'prec', rounding to
-   nearest and the subnormal+exponent configuration of 'flags'. The
-   result is meaningful only if 'a' is already rounded to the wanted
-   precision.
+/* 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.
    
-   Infinite precision (BF_PREC_INF) is supported when the radix is a
-   power of two. */
+   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). */
+   (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 */
@@ -370,7 +401,7 @@ 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_QUICKS (1 << 16)
+#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);
@@ -448,17 +479,21 @@ 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 bf_cmp_eq((const bf_t *)a, (const bf_t *)b);
+    return bfdec_cmp(a, b) == 0;
 }
 static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
 {
-    return bf_cmp_le((const bf_t *)a, (const bf_t *)b);
+    return bfdec_cmp(a, b) <= 0;
 }
 static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
 {
-    return bf_cmp_lt((const bf_t *)a, (const bf_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,
@@ -475,8 +510,8 @@ 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_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
-               bf_flags_t flags);
+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);
@@ -488,7 +523,7 @@ 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 limb_t mp_pow_dec[LIMB_DIGITS + 1];
+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)
 {
diff --git a/qjs.c b/qjs.c
index e04552a418f64d808fca319c6f6d685f56ed90b4..1c5974ff1eda6d8c0335a7bf45e09b8c8a341a10 100644 (file)
--- a/qjs.c
+++ b/qjs.c
@@ -41,9 +41,6 @@
 #include "cutils.h"
 #include "quickjs-libc.h"
 
-/* enable bignums */
-#define CONFIG_BIGNUM
-
 extern const uint8_t qjsc_repl[];
 extern const uint32_t qjsc_repl_size;
 #ifdef CONFIG_BIGNUM
@@ -258,12 +255,13 @@ static const JSMallocFunctions trace_mf = {
 void help(void)
 {
     printf("QuickJS version " CONFIG_VERSION "\n"
-           "usage: " PROG_NAME " [options] [file]\n"
+           "usage: " PROG_NAME " [options] [file [args]]\n"
            "-h  --help         list options\n"
            "-e  --eval EXPR    evaluate EXPR\n"
            "-i  --interactive  go to interactive mode\n"
            "-m  --module       load as ES6 module (default=autodetect)\n"
            "    --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"
@@ -292,6 +290,8 @@ int main(int argc, char **argv)
     int load_std = 0;
     int dump_unhandled_promise_rejection = 0;
     size_t memory_limit = 0;
+    char *include_list[32];
+    int i, include_count = 0;
 #ifdef CONFIG_BIGNUM
     int load_jscalc, bignum_ext = 0;
 #endif
@@ -345,6 +345,18 @@ int main(int argc, char **argv)
                 fprintf(stderr, "qjs: missing expression for -e\n");
                 exit(2);
             }
+            if (opt == 'I' || !strcmp(longopt, "include")) {
+                if (optind >= argc) {
+                    fprintf(stderr, "expecting filename");
+                    exit(1);
+                }
+                if (include_count >= countof(include_list)) {
+                    fprintf(stderr, "too many included files");
+                    exit(1);
+                }
+                include_list[include_count++] = argv[optind++];
+                continue;
+            }
             if (opt == 'i' || !strcmp(longopt, "interactive")) {
                 interactive++;
                 continue;
@@ -426,6 +438,7 @@ int main(int argc, char **argv)
     if (bignum_ext || load_jscalc) {
         JS_AddIntrinsicBigFloat(ctx);
         JS_AddIntrinsicBigDecimal(ctx);
+        JS_AddIntrinsicOperators(ctx);
         JS_EnableBignumExt(ctx, TRUE);
     }
 #endif
@@ -459,6 +472,11 @@ int main(int argc, char **argv)
             eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
         }
 
+        for(i = 0; i < include_count; i++) {
+            if (eval_file(ctx, include_list[i], module))
+                goto fail;
+        }
+
         if (expr) {
             if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
                 goto fail;
diff --git a/qjsc.c b/qjsc.c
index 0706887c93b32892021ba9d1b32f71ed3ea40b7f..e2aa6ad200232b83bf1743d50c35197657fd7403 100644 (file)
--- a/qjsc.c
+++ b/qjsc.c
@@ -36,9 +36,6 @@
 #include "cutils.h"
 #include "quickjs-libc.h"
 
-/* enable bignums */
-#define CONFIG_BIGNUM
-
 typedef struct {
     char *name;
     char *short_name;
@@ -79,7 +76,9 @@ static const FeatureEntry feature_list[] = {
     { "promise", "Promise" },
 #define FE_MODULE_LOADER 9
     { "module-loader", NULL },
+#ifdef CONFIG_BIGNUM
     { "bigint", "BigInt" },
+#endif
 };
 
 void namelist_add(namelist_t *lp, const char *name, const char *short_name,
@@ -485,9 +484,12 @@ int main(int argc, char **argv)
     FILE *fo;
     JSRuntime *rt;
     JSContext *ctx;
-    BOOL use_lto, bignum_ext;
+    BOOL use_lto;
     int module;
     OutputTypeEnum output_type;
+#ifdef CONFIG_BIGNUM
+    BOOL bignum_ext = FALSE;
+#endif
     
     out_filename = NULL;
     output_type = OUTPUT_EXECUTABLE;
@@ -497,7 +499,6 @@ int main(int argc, char **argv)
     byte_swap = FALSE;
     verbose = 0;
     use_lto = FALSE;
-    bignum_ext = FALSE;
     
     /* add system modules */
     namelist_add(&cmodule_list, "std", "std", 0);
@@ -538,9 +539,13 @@ int main(int argc, char **argv)
                     }
                     if (i == countof(feature_list))
                         goto bad_feature;
-                } else if (!strcmp(optarg, "bignum")) {
+                } else
+#ifdef CONFIG_BIGNUM
+                if (!strcmp(optarg, "bignum")) {
                     bignum_ext = TRUE;
-                } else {
+                } else
+#endif
+                {
                 bad_feature:
                     fprintf(stderr, "unsupported feature: %s\n", optarg);
                     exit(1);
@@ -615,6 +620,7 @@ int main(int argc, char **argv)
     if (bignum_ext) {
         JS_AddIntrinsicBigFloat(ctx);
         JS_AddIntrinsicBigDecimal(ctx);
+        JS_AddIntrinsicOperators(ctx);
         JS_EnableBignumExt(ctx, TRUE);
     }
 #endif
@@ -661,13 +667,15 @@ 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
         fprintf(fo, "  js_std_add_helpers(ctx, argc, argv);\n");
 
         for(i = 0; i < init_module_list.count; i++) {
index 87382ec50d821b3ba012342e9bb8ab0adfc4c77f..f94e33c0975f335c5574116d56cbcabd11fe7b25 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * QuickJS Javascript Calculator
  * 
- * Copyright (c) 2017-2018 Fabrice Bellard
+ * 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
 var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series, Matrix;
 
 (function(global) {
-    /* the types index are used to dispatch the operator functions */
-    var OT_INT = 0;
-    var OT_FRACTION = 10;
-    var OT_FLOAT64 = 19;
-    var OT_FLOAT = 20;
-    var OT_COMPLEX = 30;
-    var OT_MOD = 40;
-    var OT_POLY = 50;
-    var OT_POLYMOD = 55;
-    var OT_RFUNC = 60;
-    var OT_SERIES = 70;
-    var OT_ARRAY = 80;
-
     global.Integer = global.BigInt;
+    global.Float = global.BigFloat;
     global.algebraicMode = true;
     
     /* add non enumerable properties */
@@ -63,6 +51,42 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             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 */
 
@@ -104,11 +128,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         var d, r, s, i, j, a;
         d = n - 1;
         s = 0;
-        while (d & 1) {
+        while ((d & 1) == 0) {
             d >>= 1;
             s++;
         }
-        t = Math.min(t, small_primes.length);
+        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);
@@ -140,25 +165,31 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             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) {
-            return typeof a === "bigint";
-        },
-        [Symbol.operatorOrder]: OT_INT,
-        [Symbol.operatorDiv](a, b) {
-            if (algebraicMode) {
-                return Fraction.toFraction(a, b);
-            } else {
-                return Float(a) / Float(b);
-            }
-        },
-        [Symbol.operatorPow](a, b) {
-            if (algebraicMode) {
-                return generic_pow(a, b);
-            } else {
-                return Float(a) ** Float(b);
-            }
+            /* integers are represented either as bigint or as number */
+            return typeof a === "bigint" ||
+                (typeof a === "number" && Number.isSafeInteger(a));
         },
         gcd(a, b) {
             var r;
@@ -311,7 +342,10 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             return this * this;
         },
         abs() {
-            return Math.abs(this);
+            var v = this;
+            if (v < 0)
+                v = -v;
+            return v;
         },
         conj() {
             return this;
@@ -373,8 +407,119 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         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, {
-        [Symbol.operatorOrder]: OT_FRACTION,
         /* (internal use) simplify 'a' to an integer when possible */
         toFraction(a, b) {
             var r = Fraction(a, b);
@@ -383,72 +528,16 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             else
                 return r;
         },
-
-        [Symbol.operatorAdd](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den);
-        },
-        [Symbol.operatorSub](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den);
-        },
-        [Symbol.operatorMul](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return Fraction.toFraction(a.num * b.num, a.den * b.den);
-        },
-        [Symbol.operatorDiv](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return Fraction.toFraction(a.num * b.den, a.den * b.num);
-        },
-        [Symbol.operatorMathMod](a, b) {
-            var a1 = Fraction(a);
-            var b1 = Fraction(b);
-            return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b;
-        },
-        [Symbol.operatorMod](a, b) {
-            var a1 = Fraction(a);
-            var b1 = Fraction(b);
-            return a - Integer.tdiv(a1.num * b1.den, a1.den * b1.num) * b;
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            /* we assume the fractions are normalized */
-            return (a.num == b.num && a.den == b.den);
-        },
-        [Symbol.operatorCmpLT](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return (a.num * b.den < b.num * a.den);
-        },
-        [Symbol.operatorCmpLE](a, b) {
-            a = Fraction(a);
-            b = Fraction(b);
-            return (a.num * b.den <= b.num * a.den);
-        },
     });
 
     add_props(Fraction.prototype, {
         [Symbol.toPrimitive](hint) {
-            if (hint === "integer") {
-                return Integer.tdiv(this.num, this.den);
-            } else if (hint === "string") {
+            if (hint === "string") {
                 return this.toString();
             } else {
                 return Float(this.num) / this.den;
             }
         },
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            return Fraction(-this.num, this.den);
-        },
         inverse() {
             return Fraction(this.den, this.num);
         },
@@ -460,7 +549,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
         abs() {
             if (this.num < 0)
-                return this[Symbol.operatorNeg]();
+                return -this;
             else
                 return this;
         },
@@ -483,29 +572,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
 
     /* Number (Float64) */
 
-    add_props(Number, {
-        [Symbol.operatorOrder]: OT_FLOAT64,
-        /* operators are needed for fractions */
-        [Symbol.operatorAdd](a, b) {
-            return Number(a) + Number(b);
-        },
-        [Symbol.operatorSub](a, b) {
-            return Number(a) - Number(b);
-        },
-        [Symbol.operatorMul](a, b) {
-            return Number(a) * Number(b);
-        },
-        [Symbol.operatorDiv](a, b) {
-            return Number(a) / Number(b);
-        },
-        [Symbol.operatorPow](a, b) {
-            return Number(a) ** Number(b);
-        },
-    });
-              
     add_props(Number.prototype, {
         inverse() {
-            return 1.0 / this;
+            return 1 / this;
         },
         norm2() {
             return this * this;
@@ -536,8 +605,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
     
     /* Float */
 
-    global.Float = global.BigFloat;
-
     var const_tab = [];
     
     /* we cache the constants for small precisions */
@@ -603,26 +670,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         get SQRT2() { return get_const(7); },
     });
 
-    add_props(Float, {
-        [Symbol.operatorOrder]: OT_FLOAT,
-        /* operators are needed for fractions */
-        [Symbol.operatorAdd](a, b) {
-            return Float(a) + Float(b);
-        },
-        [Symbol.operatorSub](a, b) {
-            return Float(a) - Float(b);
-        },
-        [Symbol.operatorMul](a, b) {
-            return Float(a) * Float(b);
-        },
-        [Symbol.operatorDiv](a, b) {
-            return Float(a) / Float(b);
-        },
-        [Symbol.operatorPow](a, b) {
-            return Float(a) ** Float(b);
-        },
-    });
-              
     add_props(Float.prototype, {
         inverse() {
             return 1.0 / this;
@@ -631,7 +678,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             return this * this;
         },
         abs() {
-            return Math.abs(this);
+            return Float.abs(this);
         },
         conj() {
             return this;
@@ -672,8 +719,61 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         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, {
-        [Symbol.operatorOrder]: OT_COMPLEX,
         /* simplify to real number when possible */
         toComplex(re, im) {
             if (algebraicMode && im == 0)
@@ -681,43 +781,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             else
                 return Complex(re, im);
         },
-        
-        [Symbol.operatorAdd](a, b) {
-            a = Complex(a);
-            b = Complex(b);
-            return Complex.toComplex(a.re + b.re, a.im + b.im);
-        },
-        [Symbol.operatorSub](a, b) {
-            a = Complex(a);
-            b = Complex(b);
-            return Complex.toComplex(a.re - b.re, a.im - b.im);
-        },
-        [Symbol.operatorMul](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);
-        },
-        [Symbol.operatorDiv](a, b) {
-            a = Complex(a);
-            b = Complex(b);
-            return a * b.inverse();
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](a, b) {
-            a = Complex(a);
-            b = Complex(b);
-            return a.re == b.re && a.im == b.im;
-        }
     });
 
     add_props(Complex.prototype, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            return Complex(-this.re, -this.im);
-        },
         inverse() {
             var c = this.norm2();
             return Complex(this.re / c, -this.im / c);
@@ -785,14 +851,75 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         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);
+    }
 
-    add_props(Mod.prototype, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            return Mod(-this.res, this.mod);
+    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)) {
@@ -806,57 +933,21 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
     });
 
-    add_props(Mod, {
-        [Symbol.operatorOrder]: OT_MOD,
-        [Symbol.operatorAdd](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);
-            }
-        },
-        [Symbol.operatorSub](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);
-            }
-        },
-        [Symbol.operatorMul](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);
-            }
-        },
-        [Symbol.operatorDiv](a, b) {
-            if (!(b instanceof Mod))
-                b = Mod(b, a.mod);
-            return Mod[Symbol.operatorMul](a, b.inverse());
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](a, b) {
-            if (!(a instanceof Mod) ||
-                !(b instanceof Mod))
-                return false;
-            return (a.mod == b.mod && a.res == b.res);
-        }
-    });
-            
     /* 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)
@@ -868,7 +959,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
                 a = [ 0 ];
             Object.setPrototypeOf(a, Polynomial.prototype);
             return a.trim();
-        } else if (a.constructor[Symbol.operatorOrder] <= OT_MOD) {
+        } else if (polynomial_is_scalar(a)) {
             a = [a];
             Object.setPrototypeOf(a, Polynomial.prototype);
             return a;
@@ -1004,18 +1095,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             a.length = i;
             return a;
         },
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            var r, i, n, a;
-            a = this;
-            n = a.length;
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = -a[i];
-            return Polynomial(r);
-        },
         conj() {
             var r, i, n, a;
             a = this;
@@ -1097,74 +1176,106 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
     });
 
-    add_props(Polynomial, {
-        [Symbol.operatorOrder]: OT_POLY,
-        [Symbol.operatorAdd](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);
-        },
-        [Symbol.operatorSub](a, b) {
-            return Polynomial[Symbol.operatorAdd](a, -b);
-        },
-        [Symbol.operatorMul](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];
-                }
+
+    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);
-        },
-        [Symbol.operatorDiv](a, b) {
-            if (b.constructor[Symbol.operatorOrder] <= OT_COMPLEX)
-                return a * (1 / b);
-            else
-                return RationalFunction(Polynomial(a),
-                                        Polynomial(b));
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorMathMod](a, b) {
-            return Polynomial.divrem(a, b)[1];
-        },
-        [Symbol.operatorMod](a, b) {
-            return Polynomial.divrem(a, b)[1];
-        },
-        [Symbol.operatorCmpEQ](a, b) {
-            var n, i;
-            if (!(a instanceof Polynomial) ||
-                !(b instanceof Polynomial))
-                return false;
-            n = a.length;
-            if (n != b.length)
+        }
+        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;
-            for(i = 0; i < n; i++) {
-                if (a[i] != b[i])
-                    return false;
-            }
-            return true;
-        },
+        }
+        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)
@@ -1252,13 +1363,66 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         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, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            return PolyMod(-this.res, this.mod);
-        },
         inverse() {
             var a = this, m = a.mod;
             if (m instanceof Polynomial) {
@@ -1272,55 +1436,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
     });
 
-    add_props(PolyMod, {
-        [Symbol.operatorOrder]: OT_POLYMOD,
-        [Symbol.operatorAdd](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);
-            }
-        },
-        [Symbol.operatorSub](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);
-            }
-        },
-        [Symbol.operatorMul](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);
-            }
-        },
-        [Symbol.operatorDiv](a, b) {
-            if (!(b instanceof PolyMod))
-                b = PolyMod(b, a.mod);
-            return PolyMod[Symbol.operatorMul](a, b.inverse());
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](a, b) {
-            if (!(a instanceof PolyMod) ||
-                !(b instanceof PolyMod))
-                return false;
-            return (a.mod == b.mod && a.res == b.res);
-        }
-    });
-
     /* Rational function */
     
     RationalFunction = function RationalFunction(a, b)
@@ -1347,12 +1462,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
     }
 
     add_props(RationalFunction.prototype, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            return RationalFunction(-this.num, this.den);
-        },
         inverse() {
             return RationalFunction(this.den, this.num);
         },
@@ -1378,8 +1487,59 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
     });
 
+    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, {
-        [Symbol.operatorOrder]: OT_RFUNC,
         /* This function always return a RationalFunction object even
            if it could simplified to a polynomial, so it is not
            equivalent to RationalFunction(a) */
@@ -1394,33 +1554,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
                 return obj;
             }
         },
-        [Symbol.operatorAdd](a, b) {
-            a = RationalFunction.toRationalFunction(a);
-            b = RationalFunction.toRationalFunction(b);
-            return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den);
-        },
-        [Symbol.operatorSub](a, b) {
-            a = RationalFunction.toRationalFunction(a);
-            b = RationalFunction.toRationalFunction(b);
-            return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den);
-        },
-        [Symbol.operatorMul](a, b) {
-            a = RationalFunction.toRationalFunction(a);
-            b = RationalFunction.toRationalFunction(b);
-            return RationalFunction(a.num * b.num, a.den * b.den);
-        },
-        [Symbol.operatorDiv](a, b) {
-            a = RationalFunction.toRationalFunction(a);
-            b = RationalFunction.toRationalFunction(b);
-            return RationalFunction(a.num * b.den, a.den * b.num);
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](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);
-        },
     });
               
     /* Power series */
@@ -1435,6 +1568,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         }
         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) {
@@ -1442,7 +1581,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         
         if (a instanceof Series) {
             return a;
-        } else if (a.constructor[Symbol.operatorOrder] <= OT_POLY) {
+        } else if (series_is_scalar_or_polynomial(a)) {
             if (n <= 0) {
                 /* XXX: should still use the polynomial degree */
                 return Series.zero(0, 0);
@@ -1462,19 +1601,132 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         }
     };
 
-    add_props(Series.prototype, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            var obj, n, i;
-            n = this.length;
-            obj = Series.zero(this.length, this.emin);
-            for(i = 0; i < n; i++) {
-                obj[i] = -this[i];
+    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 obj;
-        },
+        }
+        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;
@@ -1610,7 +1862,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
     });
 
     add_props(Series, {
-        [Symbol.operatorOrder]: OT_SERIES,
         /* new series of length n and first exponent emin */
         zero(n, emin) {
             var r, i, obj;
@@ -1624,108 +1875,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
             Object.setPrototypeOf(r, obj);
             return r;
         },
-        [Symbol.operatorAdd](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 (v2.constructor[Symbol.operatorOrder] <= OT_POLY) {
-                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();
-        },
-        [Symbol.operatorSub](a, b) {
-            return Series[Symbol.operatorAdd](a, -b);
-        },
-        [Symbol.operatorMul](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();
-        },
-        [Symbol.operatorDiv](v1, v2) {
-            if (!(v2 instanceof Series))
-                v2 = Series(v2, v1.length);
-            return Series[Symbol.operatorMul](v1, v2.inverse());
-        },
-        [Symbol.operatorPow](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);
-            }
-        },
-        [Symbol.operatorCmpEQ](a, b) {
-            var n, i;
-            if (!(a instanceof Series) ||
-                !(b instanceof Series))
-                return false;
-            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;
-        },
         O(a) {
             function ErrorO() {
                 return TypeError("invalid O() argument");
             }
             var n;
-            if (a.constructor[Symbol.operatorOrder] <= OT_POLY) {
+            if (series_is_scalar_or_polynomial(a)) {
                 a = Polynomial(a);
                 n = a.deg();
                 if (n < 0)
@@ -2061,130 +2216,138 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
         },
     });
 
-    add_props(Array, {
-        [Symbol.operatorOrder]: OT_ARRAY,
-        [Symbol.operatorAdd](a, b) {
-            var r, i, n;
-            if (!Array.isArray(a) || !Array.isArray(b))
-                throw TypeError("array expected");
-            n = a.length;
-            if (n != b.length)
-                throw TypeError("incompatible array size");
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = a[i] + b[i];
-            return r;
-        },
-        [Symbol.operatorSub](a, b) {
-            var r, i, n;
-            n = a.length;
-            if (!Array.isArray(a) || !Array.isArray(b))
-                throw TypeError("array expected");
-            if (n != b.length)
-                throw TypeError("incompatible array size");
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = a[i] - b[i];
-            return r;
-        },
-        scalar_mul(a, b) {
-            var r, i, n;
-            n = a.length;
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = a[i] * b;
-            return r;
-        },
-        [Symbol.operatorMul](a, b) {
-            var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat, a_is_array, b_is_array;
-            a_is_array = Array.isArray(a);
-            b_is_array = Array.isArray(b);
-            if (!a_is_array && !b_is_array) {
-                throw TypeError("array expected");
-            } else if (!a_is_array && b_is_array) {
-                return Array.scalar_mul(b, a);
-            } else if (a_is_array && !b_is_array) {
-                return Array.scalar_mul(a, b);
-            }
-            h = a.length;
-            a_mat = Array.isArray(a[0]);
-            if (a_mat) {
-                l = a[0].length;
-            } else {
-                l = 1;
-            }
-            if (l != b.length)
-                throw RangeError("incompatible matrix size");
-            b_mat = Array.isArray(b[0]);
-            if (b_mat)
-                w = b[0].length;
-            else
-                w = 1;
-            r = [];
-            if (a_mat && b_mat) {
-                for(i = 0; i < h; i++) {
-                    rl = [];
-                    for(j = 0; j < w; j++) {
-                        sum = 0;
-                        for(k = 0; k < l; k++) {
-                            sum += a[i][k] * b[k][j];
-                        }
-                        rl[j] = sum;
-                    }
-                    r[i] = rl;
-                }
-            } else if (a_mat && !b_mat) {
-                for(i = 0; i < h; i++) {
+    function array_add(a, b) {
+        var r, i, n;
+        n = a.length;
+        if (n != b.length)
+            throw TypeError("incompatible array size");
+        r = [];
+        for(i = 0; i < n; i++)
+            r[i] = a[i] + b[i];
+        return r;
+    }
+    function array_sub(a, b) {
+        var r, i, n;
+        n = a.length;
+        if (n != b.length)
+            throw TypeError("incompatible array size");
+        r = [];
+        for(i = 0; i < n; i++)
+            r[i] = a[i] - b[i];
+        return r;
+    }
+    function array_scalar_mul(a, b) {
+        var r, i, n;
+        n = a.length;
+        r = [];
+        for(i = 0; i < n; i++)
+            r[i] = a[i] * b;
+        return r;
+    }
+    function array_mul(a, b) {
+        var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat;
+        h = a.length;
+        a_mat = Array.isArray(a[0]);
+        if (a_mat) {
+            l = a[0].length;
+        } else {
+            l = 1;
+        }
+        if (l != b.length)
+            throw RangeError("incompatible matrix size");
+        b_mat = Array.isArray(b[0]);
+        if (b_mat)
+            w = b[0].length;
+        else
+            w = 1;
+        r = [];
+        if (a_mat && b_mat) {
+            for(i = 0; i < h; i++) {
+                rl = [];
+                for(j = 0; j < w; j++) {
                     sum = 0;
                     for(k = 0; k < l; k++) {
-                        sum += a[i][k] * b[k];
+                        sum += a[i][k] * b[k][j];
                     }
-                    r[i] = sum;
+                    rl[j] = sum;
                 }
-            } else if (!a_mat && b_mat) {
-                for(i = 0; i < h; i++) {
-                    rl = [];
-                    for(j = 0; j < w; j++) {
-                        rl[j] = a[i] * b[0][j];
-                    }
-                    r[i] = rl;
+                r[i] = rl;
+            }
+        } else if (a_mat && !b_mat) {
+            for(i = 0; i < h; i++) {
+                sum = 0;
+                for(k = 0; k < l; k++) {
+                    sum += a[i][k] * b[k];
                 }
-            } else {
-                for(i = 0; i < h; i++) {
-                    r[i] = a[i] * b[0];
+                r[i] = sum;
+            }
+        } else if (!a_mat && b_mat) {
+            for(i = 0; i < h; i++) {
+                rl = [];
+                for(j = 0; j < w; j++) {
+                    rl[j] = a[i] * b[0][j];
                 }
+                r[i] = rl;
             }
-            return r;
-        },
-        [Symbol.operatorDiv](a, b) {
-            return Array[Symbol.operatorMul](a, b.inverse());
-        },
-        [Symbol.operatorPow]: generic_pow,
-        [Symbol.operatorCmpEQ](a, b) {
-            var n, i;
-            n = a.length;
-            if (n != b.length)
+        } else {
+            for(i = 0; i < h; i++) {
+                r[i] = a[i] * b[0];
+            }
+        }
+        return r;
+    }
+    function array_div(a, b) {
+        return array_mul(a, b.inverse());
+    }
+    function array_scalar_div(a, b) {
+        return a * b.inverse();
+    }
+    function array_eq(a, b) {
+        var n, i;
+        n = a.length;
+        if (n != b.length)
+            return false;
+        for(i = 0; i < n; i++) {
+            if (a[i] != b[i])
                 return false;
-            for(i = 0; i < n; i++) {
-                if (a[i] != b[i])
-                    return false;
+        }
+        return true;
+    }
+
+    operators_set(Array.prototype,
+        {
+            "+": array_add,
+            "-": array_sub,
+            "*": array_mul,
+            "/": array_div,
+            "==": array_eq,
+            "pos"(a) {
+                return a;
+            },
+            "neg"(a) {
+                var i, n, r;
+                n = a.length;
+                r = [];
+                for(i = 0; i < n; i++)
+                    r[i] = -a[i];
+                return r;
             }
-            return true;
         },
-    });
+        {
+            right: [Number, BigInt, Float, Fraction, Complex, Mod,
+                    Polynomial, PolyMod, RationalFunction, Series],
+            "*": array_scalar_mul,
+            "/": array_scalar_div,
+            "**": generic_pow, /* XXX: only for integer */
+        },
+        {
+            left: [Number, BigInt, Float, Fraction, Complex, Mod,
+                   Polynomial, PolyMod, RationalFunction, Series],
+            "*"(a, b) { return array_scalar_mul(b, a); },
+            "/"(a, b) { return array_scalar_div(b, a); },
+        });
 
     add_props(Array.prototype, {
-        [Symbol.operatorPlus]() {
-            return this;
-        },
-        [Symbol.operatorNeg]() {
-            var i, n, r;
-            n = this.length;
-            r = [];
-            for(i = 0; i < n; i++)
-                r[i] = -this[i];
-            return r;
-        },
         conj() {
             var i, n, r;
             n = this.length;
@@ -2426,6 +2589,42 @@ function fromdeg(a)
     return a * Float.PI / 180;
 }
 
+function sinh(a)
+{
+    var e = Float.exp(Float(a));
+    return (e - 1/e) * 0.5;
+}
+
+function cosh(a)
+{
+    var e = Float.exp(Float(a));
+    return (e + 1/e) * 0.5;
+}
+
+function tanh(a)
+{
+    var e = Float.exp(Float(a) * 2);
+    return (e - 1) / (e + 1);
+}
+
+function asinh(a)
+{
+    var x = Float(a);
+    return log(sqrt(x * x + 1) + x);
+}
+
+function acosh(a)
+{
+    var x = Float(a);
+    return log(sqrt(x * x - 1) + x);
+}
+
+function atanh(a)
+{
+    var x = Float(a);
+    return 0.5 * log((1 + x) / (1 - x));
+}
+
 var idn = Matrix.idn;
 var diag = Matrix.diag;
 var trans = Matrix.trans;
index 92042d2735b009caa0ca8ea87906614bd1095f02..501aa1f8db594ad14bcab497d2878dc6166ef17d 100644 (file)
@@ -219,6 +219,8 @@ DEF(BigInt, "BigInt")
 DEF(BigFloat, "BigFloat")
 DEF(BigFloatEnv, "BigFloatEnv")
 DEF(BigDecimal, "BigDecimal")
+DEF(OperatorSet, "OperatorSet")
+DEF(Operators, "Operators")
 #endif
 DEF(Map, "Map")
 DEF(Set, "Set") /* Map + 1 */
@@ -263,27 +265,7 @@ DEF(Symbol_species, "Symbol.species")
 DEF(Symbol_unscopables, "Symbol.unscopables")
 DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
 #ifdef CONFIG_BIGNUM
-DEF(Symbol_operatorOrder, "Symbol.operatorOrder")
-DEF(Symbol_operatorAdd, "Symbol.operatorAdd")
-DEF(Symbol_operatorSub, "Symbol.operatorSub")
-DEF(Symbol_operatorMul, "Symbol.operatorMul")
-DEF(Symbol_operatorDiv, "Symbol.operatorDiv")
-DEF(Symbol_operatorMod, "Symbol.operatorMod")
-DEF(Symbol_operatorPow, "Symbol.operatorPow")
-DEF(Symbol_operatorShl, "Symbol.operatorShl")
-DEF(Symbol_operatorShr, "Symbol.operatorShr")
-DEF(Symbol_operatorAnd, "Symbol.operatorAnd")
-DEF(Symbol_operatorOr, "Symbol.operatorOr")
-DEF(Symbol_operatorXor, "Symbol.operatorXor")
-DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT")
-DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE")
-DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ")
-DEF(Symbol_operatorPlus, "Symbol.operatorPlus")
-DEF(Symbol_operatorNeg, "Symbol.operatorNeg")
-DEF(Symbol_operatorNot, "Symbol.operatorNot")
-DEF(Symbol_operatorInc, "Symbol.operatorInc")
-DEF(Symbol_operatorDec, "Symbol.operatorDec")
-DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod")
+DEF(Symbol_operatorSet, "Symbol.operatorSet")
 #endif
-
+    
 #endif /* DEF */
index da53fefc182ffeeb4c8f61fa548ae2544b8145da..aff1b8911ac69e2c2fb460c18fc646b49e9945cc 100644 (file)
@@ -2027,6 +2027,20 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val,
 
 #if !defined(_WIN32)
 
+static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val,
+                           int argc, JSValueConst *argv)
+{
+    const char *target;
+    int err;
+
+    target = JS_ToCString(ctx, argv[0]);
+    if (!target)
+        return JS_EXCEPTION;
+    err = chdir(target);
+    JS_FreeCString(ctx, target);
+    return js_os_return(ctx, err);
+}
+
 /* return [path, errorcode] */
 static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
                               int argc, JSValueConst *argv)
@@ -2581,6 +2595,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
     JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
     JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
 #if !defined(_WIN32)
+    JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
     JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
     JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ),
     JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
index cc514e5502d46f3f8ac28dc72ae47ac41f1f50a8..57f6969b1eaa825aa04d9c439db1cc9e21820b3f 100644 (file)
@@ -260,9 +260,7 @@ DEF(             or, 1, 2, 1, none)
 DEF(is_undefined_or_null, 1, 1, 1, none)
 #ifdef CONFIG_BIGNUM
 DEF(      mul_pow10, 1, 2, 1, none)
-DEF(       math_div, 1, 2, 1, none)
 DEF(       math_mod, 1, 2, 1, none)
-DEF(       math_pow, 1, 2, 1, none)
 #endif
 /* must be the last non short and non temporary opcode */
 DEF(            nop, 1, 0, 0, none) 
@@ -271,7 +269,6 @@ DEF(            nop, 1, 0, 0, none)
 
 def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */
 
-def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */
 def(    enter_scope, 3, 0, 0, u16)  /* emitted in phase 1, removed in phase 2 */
 def(    leave_scope, 3, 0, 0, u16)  /* emitted in phase 1, removed in phase 2 */
 
index 646af8aacce3657c48d99371f3ccf7c5f84e27f0..b19a4d9fdb1ee1e64cbbe308a448b0d5332fa014 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
 #include "list.h"
 #include "quickjs.h"
 #include "libregexp.h"
-
-/* enable bignums */
-#define CONFIG_BIGNUM
-
 #ifdef CONFIG_BIGNUM
 #include "libbf.h"
 #endif
@@ -151,7 +147,8 @@ enum {
     JS_CLASS_BIG_INT,           /* u.object_data */
     JS_CLASS_BIG_FLOAT,         /* u.object_data */
     JS_CLASS_FLOAT_ENV,         /* u.float_env */
-    JS_CLASS_BIG_DECIMAL,         /* u.object_data */
+    JS_CLASS_BIG_DECIMAL,       /* u.object_data */
+    JS_CLASS_OPERATOR_SET,      /* u.operator_set */
 #endif
     JS_CLASS_MAP,               /* u.map_state */
     JS_CLASS_SET,               /* u.map_state */
@@ -285,6 +282,7 @@ struct JSRuntime {
     JSNumericOperations bigint_ops;
     JSNumericOperations bigfloat_ops;
     JSNumericOperations bigdecimal_ops;
+    uint32_t operator_count;
 #endif
 };
 
@@ -300,8 +298,7 @@ struct JSClass {
 
 #define JS_MODE_STRICT (1 << 0)
 #define JS_MODE_STRIP  (1 << 1)
-#define JS_MODE_BIGINT (1 << 2)
-#define JS_MODE_MATH   (1 << 3)
+#define JS_MODE_MATH   (1 << 2)
 
 typedef struct JSStackFrame {
     struct JSStackFrame *prev_frame; /* NULL if first stack frame */
@@ -312,7 +309,7 @@ typedef struct JSStackFrame {
     const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
                         instruction after the call */
     int arg_count;
-    int js_mode; /* 0 or JS_MODE_BIGINT for C functions */
+    int js_mode; /* 0 or JS_MODE_MATH for C functions */
     /* only used in generators. Current stack pointer value. NULL if
        the function is running. */ 
     JSValue *cur_sp;
@@ -419,8 +416,8 @@ struct JSContext {
 #ifdef CONFIG_BIGNUM
     bf_context_t *bf_ctx;   /* points to rt->bf_ctx, shared by all contexts */
     JSFloatEnv fp_env; /* global FP environment */
-    BOOL bignum_ext; /* enable bigint mode, math mode and operator
-                        overloading */
+    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;
@@ -653,6 +650,53 @@ typedef struct JSAsyncFunctionData {
     JSAsyncFunctionState func_state;
 } JSAsyncFunctionData;
 
+typedef enum {
+   /* binary operators */
+   JS_OVOP_ADD,
+   JS_OVOP_SUB,
+   JS_OVOP_MUL,
+   JS_OVOP_DIV,
+   JS_OVOP_MOD,
+   JS_OVOP_POW,
+   JS_OVOP_OR,
+   JS_OVOP_AND,
+   JS_OVOP_XOR,
+   JS_OVOP_SHL,
+   JS_OVOP_SAR,
+   JS_OVOP_SHR,
+   JS_OVOP_EQ,
+   JS_OVOP_LESS,
+
+   JS_OVOP_BINARY_COUNT,
+   /* unary operators */
+   JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
+   JS_OVOP_NEG,
+   JS_OVOP_INC,
+   JS_OVOP_DEC,
+   JS_OVOP_NOT,
+
+   JS_OVOP_COUNT,
+} JSOverloadableOperatorEnum;
+
+typedef struct {
+    uint32_t operator_index;
+    JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
+} JSBinaryOperatorDefEntry;
+
+typedef struct {
+    int count;
+    JSBinaryOperatorDefEntry *tab;
+} JSBinaryOperatorDef;
+
+typedef struct {
+    uint32_t operator_counter;
+    BOOL is_primitive; /* OperatorSet for a primitive type */
+    /* NULL if no operator is defined */
+    JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
+    JSBinaryOperatorDef left;
+    JSBinaryOperatorDef right;
+} JSOperatorSetData;
+
 typedef struct JSReqModuleEntry {
     JSAtom module_name;
     JSModuleDef *module; /* used using resolution */
@@ -808,6 +852,7 @@ struct JSObject {
         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 */
@@ -991,6 +1036,11 @@ 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
 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
 static int JS_ToBoolFree(JSContext *ctx, JSValue val);
 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
@@ -1021,10 +1071,27 @@ static JSProperty *add_property(JSContext *ctx,
                                 JSObject *p, JSAtom prop, int prop_flags);
 #ifdef CONFIG_BIGNUM
 static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
-static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a);
-static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a);
-static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint);
-static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a);
+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 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);
@@ -1123,19 +1190,6 @@ static const JSClassExoticMethods js_proxy_exotic_methods;
 static const JSClassExoticMethods js_module_ns_exotic_methods;
 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
 
-JS_BOOL JS_IsNumber(JSValueConst v)
-{
-#ifdef CONFIG_BIGNUM
-    /* XXX: check the bignum case. Should use a different function */
-    int tag = JS_VALUE_GET_TAG(v);
-    return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
-        tag == JS_TAG_BIG_FLOAT || JS_TAG_IS_FLOAT64(tag);
-#else
-    int tag = JS_VALUE_GET_TAG(v);
-    return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag);
-#endif
-}
-
 static void js_trigger_gc(JSRuntime *rt, size_t size)
 {
     BOOL force_gc;
@@ -1333,6 +1387,7 @@ static JSClassShortDef const js_std_class_def[] = {
     { 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 */
@@ -2136,10 +2191,10 @@ static inline BOOL is_strict_mode(JSContext *ctx)
 }
 
 #ifdef CONFIG_BIGNUM
-static inline BOOL is_bigint_mode(JSContext *ctx)
+static inline BOOL is_math_mode(JSContext *ctx)
 {
     JSStackFrame *sf = ctx->current_stack_frame;
-    return (sf && (sf->js_mode & JS_MODE_BIGINT));
+    return (sf && (sf->js_mode & JS_MODE_MATH));
 }
 #endif
 
@@ -2148,25 +2203,12 @@ JSValue JS_NewInt64(JSContext *ctx, int64_t v)
     if (v == (int32_t)v) {
         return JS_NewInt32(ctx, v);
     } else {
-#ifdef CONFIG_BIGNUM
-        if (is_bigint_mode(ctx)) {
-            bf_t a_s, *a = &a_s;
-            bf_init(ctx->bf_ctx, a);
-            bf_set_si(a, v);
-            return JS_NewBigInt(ctx, a);
-        } else
-#endif
-        {
-            return __JS_NewFloat64(ctx, (double)v);
-        }
+        return __JS_NewFloat64(ctx, (double)v);
     }
 }
 
 static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
 {
-#ifdef CONFIG_BIGNUM
-    return JS_NewInt64(ctx, val);
-#else
     JSValue v;
     if (val <= 0x7fffffff) {
         v = JS_MKVAL(JS_TAG_INT, val);
@@ -2174,7 +2216,6 @@ static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
         v = __JS_NewFloat64(ctx, val);
     }
     return v;
-#endif
 }
 
 /* JSAtom support */
@@ -5720,6 +5761,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
 #ifdef CONFIG_BIGNUM
         case JS_CLASS_BIG_INT:           /* u.object_data */
         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;
@@ -6515,28 +6557,17 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val)
     case JS_TAG_BIG_INT:
         val = ctx->class_proto[JS_CLASS_BIG_INT];
         break;
-    case JS_TAG_INT:
-        if (is_bigint_mode(ctx)) {
-            val = ctx->class_proto[JS_CLASS_BIG_INT];
-        } else {
-            val = ctx->class_proto[JS_CLASS_NUMBER];
-        }
-        break;
-    case JS_TAG_FLOAT64:
-        val = ctx->class_proto[JS_CLASS_NUMBER];
-        break;
     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;
-#else
+#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         val = ctx->class_proto[JS_CLASS_NUMBER];
         break;
-#endif
     case JS_TAG_BOOL:
         val = ctx->class_proto[JS_CLASS_BOOLEAN];
         break;
@@ -7750,11 +7781,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop,
             }
             p->u.array.count = len;
         }
-#ifdef CONFIG_BIGNUM
-        set_value(ctx, &prop->u.value, JS_NewUint32(ctx, len));
-#else
         prop->u.value = JS_NewUint32(ctx, len);
-#endif
     } else {
         /* Note: length is always a uint32 because the object is an
            array */
@@ -9352,9 +9379,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
 #define HINT_STRING  0
 #define HINT_NUMBER  1
 #define HINT_NONE    2
-#ifdef CONFIG_BIGNUM
-#define HINT_INTEGER 3
-#endif
 /* don't try Symbol.toPrimitive */
 #define HINT_FORCE_ORDINARY (1 << 4)
 
@@ -9389,11 +9413,6 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
             case HINT_NONE:
                 atom = JS_ATOM_default;
                 break;
-#ifdef CONFIG_BIGNUM
-            case HINT_INTEGER:
-                atom = JS_ATOM_integer;
-                break;
-#endif
             }
             arg = JS_AtomToString(ctx, atom);
             ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
@@ -9605,22 +9624,31 @@ static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
 {
     bf_t a_s, *a = &a_s;
     int ret;
-    bf_init(ctx->bf_ctx, a);
+    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) {
-        bf_delete(a);
+        JS_FreeValue(ctx, val);
         return JS_ThrowOutOfMemory(ctx);
     }
-    return JS_NewBigInt2(ctx, a, !(flags & ATOD_MODE_BIGINT));
+    val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
+    return val;
 }
 
 static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
                                      int radix, int flags, slimb_t *pexponent)
 {
-    bf_t a_s, *a = &a_s;
+    bf_t *a;
     int ret;
-
-    bf_init(ctx->bf_ctx, a);
+    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,
@@ -9630,25 +9658,30 @@ static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
                       ctx->fp_env.flags);
     }
     if (ret & BF_ST_MEM_ERROR) {
-        bf_delete(a);
+        JS_FreeValue(ctx, val);
         return JS_ThrowOutOfMemory(ctx);
     }
-    return JS_NewBigFloat(ctx, a);
+    return val;
 }
 
 static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
                                        int radix, int flags, slimb_t *pexponent)
 {
-    bfdec_t a_s, *a = &a_s;
+    bfdec_t *a;
     int ret;
-    bfdec_init(ctx->bf_ctx, a);
+    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) {
-        bfdec_delete(a);
+        JS_FreeValue(ctx, val);
         return JS_ThrowOutOfMemory(ctx);
     }
-    return JS_NewBigDecimal(ctx, a);
+    return val;
 }
 
 #endif
@@ -9730,10 +9763,12 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
             strstart(p, "Infinity", &p)) {
 #ifdef CONFIG_BIGNUM
             if (atod_type == ATOD_TYPE_BIG_FLOAT) {
-                bf_t a_s, *a = &a_s;
-                bf_init(ctx->bf_ctx, a);
+                bf_t *a;
+                val = JS_NewBigFloat(ctx);
+                if (JS_IsException(val))
+                    goto done;
+                a = JS_GetBigFloat(val);
                 bf_set_inf(a, is_neg);
-                val = JS_NewBigFloat(ctx, a);
             } else
 #endif
             {
@@ -9810,7 +9845,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
         } else if (*p == 'l') {
             p++;
             atod_type = ATOD_TYPE_BIG_FLOAT;
-        } else if (*p == 'd') {
+        } else if (*p == 'm') {
             p++;
             atod_type = ATOD_TYPE_BIG_DECIMAL;
         } else {
@@ -9901,7 +9936,6 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
 
 typedef enum JSToNumberHintEnum {
     TON_FLAG_NUMBER,
-    TON_FLAG_INTEGER,
     TON_FLAG_NUMERIC,
 } JSToNumberHintEnum;
 
@@ -9910,7 +9944,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
 {
     uint32_t tag;
     JSValue ret;
-    int hint;
 
  redo:
     tag = JS_VALUE_GET_NORM_TAG(val);
@@ -9924,12 +9957,19 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
         ret = val;
         break;
     case JS_TAG_BIG_INT:
-        if (flag == TON_FLAG_NUMBER && !is_bigint_mode(ctx)) {
+        if (flag != TON_FLAG_NUMERIC) {
             JS_FreeValue(ctx, val);
             return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
         }
-        /* fall thru */
+        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:
@@ -9944,12 +9984,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
         ret = JS_NAN;
         break;
     case JS_TAG_OBJECT:
-#ifdef CONFIG_BIGNUM
-        hint = flag == TON_FLAG_INTEGER ? HINT_INTEGER : HINT_NUMBER;
-#else
-        hint = HINT_NUMBER;
-#endif
-        val = JS_ToPrimitiveFree(ctx, val, hint);
+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
         if (JS_IsException(val))
             return JS_EXCEPTION;
         goto redo;
@@ -9969,10 +10004,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
                 ret = JS_NewInt32(ctx, 0);
             } else {
                 int flags = ATOD_ACCEPT_BIN_OCT;
-#ifdef CONFIG_BIGNUM
-                if (is_bigint_mode(ctx))
-                    flags |= ATOD_MODE_BIGINT;
-#endif
                 ret = js_atof(ctx, p, &p, 0, flags);
                 if (!JS_IsException(ret)) {
                     p += skip_spaces(p);
@@ -10001,7 +10032,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
 }
 
-#ifdef CONFIG_BIGNUM
 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
 {
     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
@@ -10011,7 +10041,6 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
 {
     return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
 }
-#endif
 
 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
                                           JSValue val)
@@ -10078,6 +10107,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
     return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
 }
 
+/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
 {
     uint32_t tag;
@@ -10106,11 +10136,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
         }
         break;
 #ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_INT:
-        if (!is_bigint_mode(ctx))
-            goto to_number;
-        ret = val;
-        break;
     case JS_TAG_BIG_FLOAT:
         {
             bf_t a_s, *a, r_s, *r = &r_s;
@@ -10124,10 +10149,13 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
                 else
                     ret = JS_DupValue(ctx, val);
             } else {
-                bf_init(ctx->bf_ctx, r);
-                bf_set(r, a);
-                bf_rint(r, BF_RNDZ);
-                ret = JS_NewBigInt(ctx, r);
+                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);
@@ -10136,9 +10164,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
         break;
 #endif
     default:
-#ifdef CONFIG_BIGNUM
-    to_number:
-#endif
         val = JS_ToNumberFree(ctx, val);
         if (JS_IsException(val))
             return val;
@@ -10182,17 +10207,12 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
         break;
 #ifdef CONFIG_BIGNUM
     case JS_TAG_BIG_FLOAT:
-    to_bf:
         {
             JSBigFloat *p = JS_VALUE_GET_PTR(val);
             bf_get_int32(&ret, &p->num, 0);
             JS_FreeValue(ctx, val);
         }
         break;
-    case JS_TAG_BIG_INT:
-        if (is_bigint_mode(ctx))
-            goto to_bf;
-        /* fall thru */
 #endif
     default:
         val = JS_ToNumberFree(ctx, val);
@@ -10261,17 +10281,12 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
         return 0;
 #ifdef CONFIG_BIGNUM
     case JS_TAG_BIG_FLOAT:
-    to_bf:
         {
             JSBigFloat *p = JS_VALUE_GET_PTR(val);
             bf_get_int64(pres, &p->num, 0);
             JS_FreeValue(ctx, val);
         }
         return 0;
-    case JS_TAG_BIG_INT:
-        if (is_bigint_mode(ctx))
-            goto to_bf;
-        /* fall thru */
 #endif
     default:
         val = JS_ToNumberFree(ctx, val);
@@ -10346,17 +10361,12 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
         break;
 #ifdef CONFIG_BIGNUM
     case JS_TAG_BIG_FLOAT:
-    to_bf:
         {
             JSBigFloat *p = JS_VALUE_GET_PTR(val);
             bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
             JS_FreeValue(ctx, val);
         }
         break;
-    case JS_TAG_BIG_INT:
-        if (is_bigint_mode(ctx))
-            goto to_bf;
-        /* fall thru */
 #endif
     default:
         val = JS_ToNumberFree(ctx, val);
@@ -10418,17 +10428,12 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
         break;
 #ifdef CONFIG_BIGNUM
     case JS_TAG_BIG_FLOAT:
-    to_bf:
         {
             JSBigFloat *p = JS_VALUE_GET_PTR(val);
             bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
             JS_FreeValue(ctx, val);
         }
         break;
-    case JS_TAG_BIG_INT:
-        if (is_bigint_mode(ctx))
-            goto to_bf;
-        /* fall thru */
 #endif
     default:
         val = JS_ToNumberFree(ctx, val);
@@ -10486,15 +10491,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
         }
         break;
 #ifdef CONFIG_BIGNUM
-    case JS_TAG_BIG_INT:
-        {
-            JSBigFloat *p = JS_VALUE_GET_PTR(val);
-            if (!is_bigint_mode(ctx))
-                goto to_number;
-            bf_get_int32(&res, &p->num, 0);
-            JS_FreeValue(ctx, val);
-        }
-        goto int_clamp;
     case JS_TAG_BIG_FLOAT:
         {
             JSBigFloat *p = JS_VALUE_GET_PTR(val);
@@ -10509,9 +10505,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
         goto int_clamp;
 #endif
     default:
-#ifdef CONFIG_BIGNUM
-    to_number:
-#endif
         val = JS_ToNumberFree(ctx, val);
         if (JS_IsException(val)) {
             *pres = 0;
@@ -10584,6 +10577,12 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
 
 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
 
+static BOOL is_safe_integer(double d)
+{
+    return isfinite(d) && floor(d) == d &&
+        fabs(d) <= (double)MAX_SAFE_INTEGER;
+}
+
 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
 {
     int64_t v;
@@ -10609,7 +10608,6 @@ static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
 }
 
 /* Note: can return an exception */
-/* XXX: bignum case */
 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
 {
     double d;
@@ -10753,7 +10751,8 @@ 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_string(JSContext *ctx, JSValueConst val)
+static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
+                                        limb_t prec, int flags)
 {
     JSValue ret;
     bfdec_t *a;
@@ -10764,8 +10763,7 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
     saved_sign = a->sign;
     if (a->expn == BF_EXP_ZERO)
         a->sign = 0;
-    str = bfdec_ftoa(NULL, a, 0, BF_RNDZ | BF_FTOA_FORMAT_FREE |
-                     BF_FTOA_JS_QUIRKS);
+    str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
     a->sign = saved_sign;
     if (!str)
         return JS_ThrowOutOfMemory(ctx);
@@ -10774,6 +10772,12 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
     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 */
@@ -11478,30 +11482,48 @@ static double js_pow(double a, double b)
 
 #ifdef CONFIG_BIGNUM
 
+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)
 {
-    BOOL is_bignum = is_bigint_mode(ctx);
-    if (is_bignum && v == (int32_t)v) {
-        return JS_NewInt32(ctx, v);
+    if (is_math_mode(ctx) &&
+        v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
+        return JS_NewInt64(ctx, v);
     } else {
-        bf_t a_s, *a = &a_s;
-        bf_init(ctx->bf_ctx, a);
-        bf_set_si(a, v);
-        return JS_NewBigInt2(ctx, a, TRUE);
+        return JS_NewBigInt64_1(ctx, v);
     }
 }
 
 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
 {
-    BOOL is_bignum = is_bigint_mode(ctx);
-    if (is_bignum && v == (int32_t)v) {
-        return JS_NewInt32(ctx, v);
+    JSValue val;
+    if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
+        val = JS_NewInt64(ctx, v);
     } else {
-        bf_t a_s, *a = &a_s;
-        bf_init(ctx->bf_ctx, a);
-        bf_set_ui(a, v);
-        return JS_NewBigInt2(ctx, a, TRUE);
+        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);
+        }
     }
+    return val;
 }
 
 /* if the returned bigfloat is allocated it is equal to
@@ -11582,13 +11604,10 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
     p = str;
     p += skip_spaces(p);
     if ((p - str) == len) {
-        bf_t a_s, *a = &a_s;
-        bf_init(ctx->bf_ctx, a);
-        bf_set_si(a, 0);
-        val = JS_NewBigInt(ctx, a);
+        val = JS_NewBigInt64(ctx, 0);
     } else {
         flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
-        if (is_bigint_mode(ctx))
+        if (is_math_mode(ctx))
             flags |= ATOD_MODE_BIGINT;
         val = js_atof(ctx, p, &p, 0, flags);
         p += skip_spaces(p);
@@ -11625,7 +11644,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
     case JS_TAG_INT:
     case JS_TAG_NULL:
     case JS_TAG_UNDEFINED:
-        if (!is_bigint_mode(ctx))
+        if (!is_math_mode(ctx))
             goto fail;
         /* fall tru */
     case JS_TAG_BOOL:
@@ -11636,7 +11655,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
     case JS_TAG_FLOAT64:
         {
             double d = JS_VALUE_GET_FLOAT64(val);
-            if (!is_bigint_mode(ctx))
+            if (!is_math_mode(ctx))
                 goto fail;
             if (!isfinite(d))
                 goto fail;
@@ -11651,7 +11670,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
         r = &p->num;
         break;
     case JS_TAG_BIG_FLOAT:
-        if (!is_bigint_mode(ctx))
+        if (!is_math_mode(ctx))
             goto fail;
         p = JS_VALUE_GET_PTR(val);
         if (!bf_is_finite(&p->num))
@@ -11668,8 +11687,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
             return NULL;
         goto redo;
     case JS_TAG_OBJECT:
-        val = JS_ToPrimitiveFree(ctx, val,
-                                 is_bigint_mode(ctx) ? HINT_INTEGER : HINT_NUMBER);
+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
         if (JS_IsException(val))
             return NULL;
         goto redo;
@@ -11692,21 +11710,26 @@ 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, b_s, *b;
+        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)
+        if (!a) {
+            JS_FreeValue(ctx, res);
             return JS_EXCEPTION;
-        b = &b_s;
-        bf_init(ctx->bf_ctx, b);
-        ret = bf_set(b, a);
+        }
+        r = JS_GetBigInt(res);
+        ret = bf_set(r, a);
         JS_FreeBigInt(ctx, a, &a_s);
         if (ret) {
-            bf_delete(b);
+            JS_FreeValue(ctx, res);
             return JS_ThrowOutOfMemory(ctx);
         }
-        return JS_NewBigInt2(ctx, b, TRUE);
+        return JS_CompactBigInt(ctx, res);
     }
 }
 
@@ -11745,7 +11768,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
 static JSBigFloat *js_new_bf(JSContext *ctx)
 {
     JSBigFloat *p;
-    p = js_mallocz(ctx, sizeof(*p));
+    p = js_malloc(ctx, sizeof(*p));
     if (!p)
         return NULL;
     p->header.ref_count = 1;
@@ -11753,224 +11776,387 @@ static JSBigFloat *js_new_bf(JSContext *ctx)
     return p;
 }
 
-/* WARNING: 'a' is freed */
-static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a)
+static JSValue JS_NewBigFloat(JSContext *ctx)
 {
-    JSValue ret;
     JSBigFloat *p;
-
-    p = js_new_bf(ctx);
-    bf_memcpy(&p->num, a);
-    ret = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
-    return ret;
+    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);
 }
 
-/* WARNING: 'a' is freed */
-static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a)
+static JSValue JS_NewBigDecimal(JSContext *ctx)
 {
-    JSValue ret;
     JSBigDecimal *p;
-
-    p = js_mallocz(ctx, sizeof(*p));
+    p = js_malloc(ctx, sizeof(*p));
     if (!p)
         return JS_EXCEPTION;
     p->header.ref_count = 1;
     bfdec_init(ctx->bf_ctx, &p->num);
-    bfdec_memcpy(&p->num, a);
-    ret = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
-    return ret;
+    return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
 }
 
-/* WARNING: 'a' is freed */
-static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint)
+static JSValue JS_NewBigInt(JSContext *ctx)
 {
-    JSValue ret;
     JSBigFloat *p;
-    int32_t v;
-
-    if (!force_bigint && bf_get_int32(&v, a, 0) == 0) {
-        /* can fit in an int32 */
-        ret = JS_NewInt32(ctx, v);
-        bf_delete(a);
-    } else {
-        p = js_new_bf(ctx);
-        bf_memcpy(&p->num, a);
-        /* normalize the zero representation */
-        if (bf_is_zero(&p->num))
-            p->num.sign = 0;
-        ret = JS_MKPTR(JS_TAG_BIG_INT, p);
-    }
-    return ret;
+    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_NewBigInt(JSContext *ctx, bf_t *a)
+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
+                                 BOOL convert_to_safe_integer)
 {
-    return JS_NewBigInt2(ctx, a, !is_bigint_mode(ctx));
+    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;
 }
 
-/* return < 0 if exception, 0 if overloading method, 1 if overloading
-   operator called */
-static __exception int js_call_binary_op_fallback(JSContext *ctx,
-                                                   JSValue *pret,
-                                                   JSValueConst op1,
-                                                   JSValueConst op2,
-                                                   OPCodeEnum op)
-{
-    JSAtom op_name;
-    JSValue method, ret, c1, c2;
-    BOOL bool_result, swap_op;
-    JSValueConst args[2];
+/* 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));
+}
+
+/* must be kept in sync with JSOverloadableOperatorEnum */
+static const char *js_overloadable_operator_names[JS_OVOP_COUNT] = {
+    "+",
+    "-",
+    "*",
+    "/",
+    "%",
+    "**",
+    "|",
+    "&",
+    "^",
+    "<<",
+    ">>",
+    ">>>",
+    "==",
+    "<",
+    "pos",
+    "neg",
+    "++",
+    "--",
+    "~",
+};
 
-    bool_result = FALSE;
-    swap_op = FALSE;
-    c1 = JS_UNDEFINED;
-    c2 = JS_UNDEFINED;
+static int get_ovop_from_opcode(OPCodeEnum op)
+{
     switch(op) {
     case OP_add:
-        op_name = JS_ATOM_Symbol_operatorAdd;
-        break;
+        return JS_OVOP_ADD;
     case OP_sub:
-        op_name = JS_ATOM_Symbol_operatorSub;
-        break;
+        return JS_OVOP_SUB;
     case OP_mul:
-        op_name = JS_ATOM_Symbol_operatorMul;
-        break;
+        return JS_OVOP_MUL;
     case OP_div:
-    case OP_math_div:
-        op_name = JS_ATOM_Symbol_operatorDiv;
-        break;
+        return JS_OVOP_DIV;
     case OP_mod:
-        op_name = JS_ATOM_Symbol_operatorMod;
-        break;
-    case OP_pow:
-    case OP_math_pow:
-        op_name = JS_ATOM_Symbol_operatorPow;
-        break;
     case OP_math_mod:
-        op_name = JS_ATOM_Symbol_operatorMathMod;
-        break;
-    case OP_shl:
-        op_name = JS_ATOM_Symbol_operatorShl;
-        break;
-    case OP_sar:
-        op_name = JS_ATOM_Symbol_operatorShr;
-        break;
-    case OP_and:
-        op_name = JS_ATOM_Symbol_operatorAnd;
-        break;
+        return JS_OVOP_MOD;
+    case OP_pow:
+        return JS_OVOP_POW;
     case OP_or:
-        op_name = JS_ATOM_Symbol_operatorOr;
-        break;
+        return JS_OVOP_OR;
+    case OP_and:
+        return JS_OVOP_AND;
     case OP_xor:
-        op_name = JS_ATOM_Symbol_operatorXor;
-        break;
+        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:
-        op_name = JS_ATOM_Symbol_operatorCmpLT;
-        bool_result = TRUE;
-        break;
     case OP_lte:
-        op_name = JS_ATOM_Symbol_operatorCmpLE;
-        bool_result = TRUE;
-        break;
     case OP_gt:
-        op_name = JS_ATOM_Symbol_operatorCmpLT;
-        bool_result = TRUE;
-        swap_op = TRUE;
-        break;
     case OP_gte:
-        op_name = JS_ATOM_Symbol_operatorCmpLE;
-        bool_result = TRUE;
-        swap_op = TRUE;
-        break;
-    case OP_eq:
-    case OP_neq:
-        op_name = JS_ATOM_Symbol_operatorCmpEQ;
-        bool_result = TRUE;
-        break;
+        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:
-        goto fail;
+        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];
     }
-    c1 = JS_GetProperty(ctx, op1, JS_ATOM_constructor);
-    if (JS_IsException(c1))
+    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;
-    c2 = JS_GetProperty(ctx, op2, JS_ATOM_constructor);
-    if (JS_IsException(c2))
+    if (JS_IsUndefined(opset1_obj))
+        return 0;
+    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+    if (!opset1)
         goto exception;
-    if (JS_VALUE_GET_TAG(c1) != JS_TAG_OBJECT ||
-        JS_VALUE_GET_TAG(c2) != JS_TAG_OBJECT)
-        goto fail;
-    if (JS_VALUE_GET_OBJ(c1) == JS_VALUE_GET_OBJ(c2)) {
-        /* if same constructor, there is no ambiguity */
-        method = JS_GetProperty(ctx, c1, op_name);
+
+    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 {
-        JSValue val;
-        int order1, order2;
+        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;
+    }
 
-        /* different constructors: we use a user-defined ordering */
-        val = JS_GetProperty(ctx, c1, JS_ATOM_Symbol_operatorOrder);
-        if (JS_IsException(val))
-            goto exception;
-        if (JS_IsUndefined(val))
-            goto undef_order;
-        if (JS_ToInt32Free(ctx, &order1, val))
-            goto exception;
-        val = JS_GetProperty(ctx, c2, JS_ATOM_Symbol_operatorOrder);
-        if (JS_IsException(val))
-            goto exception;
-        if (JS_IsUndefined(val)) {
-        undef_order:
-            JS_FreeValue(ctx, c1);
-            JS_FreeValue(ctx, c2);
-            *pret = JS_UNDEFINED;
-            return 0;
+    if (opset1->is_primitive) {
+        if (is_numeric) {
+            new_op1 = JS_ToNumeric(ctx, op1);
+        } else {
+            new_op1 = JS_ToPrimitive(ctx, op1, hint);
         }
-        if (JS_ToInt32Free(ctx, &order2, val))
+        if (JS_IsException(new_op1))
             goto exception;
-        /* ambiguous priority: error */
-        if (order1 == order2) {
-            JS_ThrowTypeError(ctx, "operator_order is identical in both constructors");
-            goto exception;
-        }
-        if (order1 > order2) {
-            val = c1;
+    } else {
+        new_op1 = JS_DupValue(ctx, op1);
+    }
+    
+    if (opset2->is_primitive) {
+        if (is_numeric) {
+            new_op2 = JS_ToNumeric(ctx, op2);
         } else {
-            val = c2;
+            new_op2 = JS_ToPrimitive(ctx, op2, hint);
         }
-        method = JS_GetProperty(ctx, val, op_name);
-    }
-    JS_FreeValue(ctx, c1);
-    JS_FreeValue(ctx, c2);
-    c1 = JS_UNDEFINED;
-    c2 = JS_UNDEFINED;
-    if (JS_IsException(method))
-        goto exception;
-    if (JS_IsUndefined(method) || JS_IsNull(method)) {
-        *pret = JS_UNDEFINED;
-        return 0;
+        if (JS_IsException(new_op2)) {
+            JS_FreeValue(ctx, new_op1);
+            goto exception;
+        }
+    } else {
+        new_op2 = JS_DupValue(ctx, op2);
     }
-    if (swap_op) {
-        args[0] = op2;
-        args[1] = op1;
+
+    /* 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] = op1;
-        args[1] = op2;
+        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 (bool_result) {
+    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;
- fail:
-    JS_ThrowTypeError(ctx, "invalid types for binary operator");
  exception:
-    JS_FreeValue(ctx, c1);
-    JS_FreeValue(ctx, c2);
+    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;
 }
@@ -11993,16 +12179,22 @@ static JSValue throw_bf_exception(JSContext *ctx, int status)
 static int js_unary_arith_bigint(JSContext *ctx,
                                  JSValue *pres, OPCodeEnum op, JSValue op1)
 {
-    bf_t a_s, r_s, *r = &r_s, *a;
+    bf_t a_s, *r, *a;
     int ret, v;
+    JSValue res;
     
-    if (op == OP_plus && !is_bigint_mode(ctx)) {
+    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);
-    bf_init(ctx->bf_ctx, r);
     ret = 0;
     switch(op) {
     case OP_inc:
@@ -12027,28 +12219,35 @@ static int js_unary_arith_bigint(JSContext *ctx,
     JS_FreeBigInt(ctx, a, &a_s);
     JS_FreeValue(ctx, op1);
     if (unlikely(ret)) {
-        bf_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigInt(ctx, r);
+    res = JS_CompactBigInt(ctx, res);
+    *pres = res;
     return 0;
 }
 
 static int js_unary_arith_bigfloat(JSContext *ctx,
                                    JSValue *pres, OPCodeEnum op, JSValue op1)
 {
-    bf_t a_s, r_s, *r = &r_s, *a;
+    bf_t a_s, *r, *a;
     int ret, v;
+    JSValue res;
     
-    if (op == OP_plus && !is_bigint_mode(ctx)) {
+    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);
-    bf_init(ctx->bf_ctx, r);
     ret = 0;
     switch(op) {
     case OP_inc:
@@ -12070,28 +12269,34 @@ static int js_unary_arith_bigfloat(JSContext *ctx,
         bf_delete(a);
     JS_FreeValue(ctx, op1);
     if (unlikely(ret & BF_ST_MEM_ERROR)) {
-        bf_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigFloat(ctx, r);
+    *pres = res;
     return 0;
 }
 
 static int js_unary_arith_bigdecimal(JSContext *ctx,
                                      JSValue *pres, OPCodeEnum op, JSValue op1)
 {
-    bfdec_t r_s, *r = &r_s, *a;
+    bfdec_t *r, *a;
     int ret, v;
-
-    if (op == OP_plus && !is_bigint_mode(ctx)) {
+    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);
-    bfdec_init(ctx->bf_ctx, r);
     ret = 0;
     switch(op) {
     case OP_inc:
@@ -12111,11 +12316,11 @@ static int js_unary_arith_bigdecimal(JSContext *ctx,
     }
     JS_FreeValue(ctx, op1);
     if (unlikely(ret)) {
-        bfdec_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigDecimal(ctx, r);
+    *pres = res;
     return 0;
 }
 
@@ -12123,112 +12328,93 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
                                                      JSValue *sp,
                                                      OPCodeEnum op)
 {
-    JSValue op1, val, method;
-    BOOL is_legacy;
-    JSAtom op_name;
-    int v;
+    JSValue op1, val;
+    int v, ret;
     uint32_t tag;
 
     op1 = sp[-1];
     /* fast path for float64 */
     if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
         goto handle_float64;
-    if (JS_IsObject(op1) && ctx->bignum_ext) {
-        switch(op) {
-        case OP_plus:
-            op_name = JS_ATOM_Symbol_operatorPlus;
-            break;
-        case OP_neg:
-            op_name = JS_ATOM_Symbol_operatorNeg;
-            break;
-        case OP_inc:
-            op_name = JS_ATOM_Symbol_operatorInc;
-            break;
-        case OP_dec:
-            op_name = JS_ATOM_Symbol_operatorDec;
-            break;
-        default:
-            abort();
-        }
-        method = JS_GetProperty(ctx, op1, op_name);
-        if (JS_IsException(method))
-            return -1;
-        if (JS_IsUndefined(method) || JS_IsNull(method))
-            goto to_number;
-        val = JS_CallFree(ctx, method, op1, 0, NULL);
-        if (JS_IsException(val))
+    if (JS_IsObject(op1)) {
+        ret = js_call_unary_op_fallback(ctx, &val, op1, op);
+        if (ret < 0)
             return -1;
-        JS_FreeValue(ctx, op1);
-        sp[-1] = val;
-    } else {
-    to_number:
-        op1 = JS_ToNumericFree(ctx, op1);
-        if (JS_IsException(op1))
-            goto exception;
-        is_legacy = is_bigint_mode(ctx) ^ 1;
-        tag = JS_VALUE_GET_TAG(op1);
-        switch(tag) {
-        case JS_TAG_INT:
-            {
-                int64_t v64;
-                v64 = JS_VALUE_GET_INT(op1);
-                switch(op) {
-                case OP_inc:
-                case OP_dec:
-                    v = 2 * (op - OP_dec) - 1;
-                    v64 += v;
-                    break;
-                case OP_plus:
-                    break;
-                case OP_neg:
-                    if (v64 == 0 && is_legacy) {
-                        sp[-1] = __JS_NewFloat64(ctx, -0.0);
-                        return 0;
-                    } else {
-                        v64 = -v64;
-                    }
-                    break;
-                default:
-                    abort();
+        if (ret) {
+            JS_FreeValue(ctx, op1);
+            sp[-1] = val;
+            return 0;
+        }
+    }
+
+    op1 = JS_ToNumericFree(ctx, op1);
+    if (JS_IsException(op1))
+        goto exception;
+    tag = JS_VALUE_GET_TAG(op1);
+    switch(tag) {
+    case JS_TAG_INT:
+        {
+            int64_t v64;
+            v64 = JS_VALUE_GET_INT(op1);
+            switch(op) {
+            case OP_inc:
+            case OP_dec:
+                v = 2 * (op - OP_dec) - 1;
+                v64 += v;
+                break;
+            case OP_plus:
+                break;
+            case OP_neg:
+                if (v64 == 0) {
+                    sp[-1] = __JS_NewFloat64(ctx, -0.0);
+                    return 0;
+                } else {
+                    v64 = -v64;
                 }
-                sp[-1] = JS_NewInt64(ctx, v64);
+                break;
+            default:
+                abort();
             }
-            break;
-        case JS_TAG_BIG_INT:
-            if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
-                goto exception;
-            break;
-        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;
-        default:
-        handle_float64:
-            {
-                double d;
-                d = JS_VALUE_GET_FLOAT64(op1);
-                switch(op) {
-                case OP_inc:
-                case OP_dec:
-                    v = 2 * (op - OP_dec) - 1;
-                    d += v;
-                    break;
-                case OP_plus:
-                    break;
-                case OP_neg:
-                    d = -d;
-                    break;
-                default:
-                    abort();
-                }
-                sp[-1] = __JS_NewFloat64(ctx, d);
+            sp[-1] = JS_NewInt64(ctx, v64);
+        }
+        break;
+    case JS_TAG_BIG_INT:
+    handle_bigint:
+        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
+            goto exception;
+        break;
+    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;
+    default:
+    handle_float64:
+        {
+            double d;
+            if (is_math_mode(ctx))
+                goto handle_bigint;
+            d = JS_VALUE_GET_FLOAT64(op1);
+            switch(op) {
+            case OP_inc:
+            case OP_dec:
+                v = 2 * (op - OP_dec) - 1;
+                d += v;
+                break;
+            case OP_plus:
+                break;
+            case OP_neg:
+                d = -d;
+                break;
+            default:
+                abort();
             }
-            break;
+            sp[-1] = __JS_NewFloat64(ctx, d);
         }
+        break;
     }
     return 0;
  exception:
@@ -12255,39 +12441,33 @@ static __exception int js_post_inc_slow(JSContext *ctx,
 
 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
 {
-    JSValue op1, method, val;
-
+    JSValue op1, val;
+    int ret;
+    
     op1 = sp[-1];
     if (JS_IsObject(op1)) {
-        if (!ctx->bignum_ext)
-            goto to_number;
-        method = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorNot);
-        if (JS_IsException(method))
-            return -1;
-        if (JS_IsUndefined(method) || JS_IsNull(method))
-            goto to_number;
-        val = JS_CallFree(ctx, method, op1, 0, NULL);
-        if (JS_IsException(val))
+        ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
+        if (ret < 0)
             return -1;
-        JS_FreeValue(ctx, op1);
-        sp[-1] = val;
-    } else {
-        if (JS_IsString(op1)) {
-        to_number:
-            op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER);
-            if (JS_IsException(op1))
-                goto exception;
-        }
-        if (is_bigint_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
-            if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
-                goto exception;
-        } else {
-            int32_t v1;
-            if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
-                goto exception;
-            sp[-1] = JS_NewInt32(ctx, ~v1);
+        if (ret) {
+            JS_FreeValue(ctx, op1);
+            sp[-1] = val;
+            return 0;
         }
     }
+
+    op1 = JS_ToNumericFree(ctx, op1);
+    if (JS_IsException(op1))
+        goto exception;
+    if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
+        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
+            goto exception;
+    } else {
+        int32_t v1;
+        if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
+            goto exception;
+        sp[-1] = JS_NewInt32(ctx, ~v1);
+    }
     return 0;
  exception:
     sp[-1] = JS_UNDEFINED;
@@ -12297,12 +12477,19 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
 static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
                                     JSValue *pres, JSValue op1, JSValue op2)
 {
-    bf_t a_s, b_s, r_s, *r, *a, *b;
-    int ret, rnd_mode;
+    bf_t a_s, b_s, *r, *a, *b;
+    int ret;
+    JSValue res;
     
+    res = JS_NewBigFloat(ctx);
+    if (JS_IsException(res)) {
+        JS_FreeValue(ctx, op1);
+        JS_FreeValue(ctx, op2);
+        return -1;
+    }
+    r = JS_GetBigFloat(res);
     a = JS_ToBigFloat(ctx, &a_s, op1);
     b = JS_ToBigFloat(ctx, &b_s, op2);
-    r = &r_s;
     bf_init(ctx->bf_ctx, r);
     switch(op) {
     case OP_add:
@@ -12314,29 +12501,21 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
     case OP_mul:
         ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
         break;
-    case OP_math_div:
     case OP_div:
         ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
         break;
     case OP_math_mod:
         /* Euclidian remainder */
-        rnd_mode = BF_DIVREM_EUCLIDIAN;
-        goto do_mod;
+        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+                     BF_DIVREM_EUCLIDIAN);
+        break;
     case OP_mod:
-        rnd_mode = BF_RNDZ;
-    do_mod:
-        {
-            bf_t q_s, *q = &q_s;
-            bf_init(ctx->bf_ctx, q);
-            ret = bf_divrem(q, r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
-                            rnd_mode);
-            bf_delete(q);
-        }
+        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+                     BF_RNDZ);
         break;
     case OP_pow:
-    case OP_math_pow:
         ret = bf_pow(r, a, b, ctx->fp_env.prec,
-                     ctx->fp_env.flags | BF_POW_JS_QUICKS);
+                     ctx->fp_env.flags | BF_POW_JS_QUIRKS);
         break;
     default:
         abort();
@@ -12348,21 +12527,24 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
     JS_FreeValue(ctx, op1);
     JS_FreeValue(ctx, op2);
     if (unlikely(ret & BF_ST_MEM_ERROR)) {
-        bf_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigFloat(ctx, r);
+    *pres = res;
     return 0;
 }
 
 static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
                                   JSValue *pres, JSValue op1, JSValue op2)
 {
-    bf_t a_s, b_s, r_s, *r, *a, *b;
-    int ret, rnd_mode;
+    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;
@@ -12371,8 +12553,7 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
         JS_FreeBigInt(ctx, a, &a_s);
         goto fail;
     }
-    r = &r_s;
-    bf_init(ctx->bf_ctx, r);
+    r = JS_GetBigInt(res);
     ret = 0;
     switch(op) {
     case OP_add:
@@ -12384,73 +12565,75 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
     case OP_mul:
         ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
         break;
-    case OP_math_div:
-        goto op_fallback;
     case OP_div:
-        if (!is_bigint_mode(ctx)) {
+        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 {
-            bf_div(r, a, b, 53, bf_set_exp_bits(11) |
-                   BF_RNDN | BF_FLAG_SUBNORMAL);
-            goto float64_result;
+            goto math_mode_div_pow;
         }
         break;
     case OP_math_mod:
         /* Euclidian remainder */
-        rnd_mode = BF_DIVREM_EUCLIDIAN;
-        goto do_int_mod;
+        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+                     BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
+        break;
     case OP_mod:
-        rnd_mode = BF_RNDZ;
-    do_int_mod:
-        {
-            bf_t q_s, *q = &q_s;
-            bf_init(ctx->bf_ctx, q);
-            ret = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ,
-                            rnd_mode) & BF_ST_INVALID_OP;
-            bf_delete(q);
-        }
+        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+                     BF_RNDZ) & BF_ST_INVALID_OP;
         break;
     case OP_pow:
-    case OP_math_pow:
         if (b->sign) {
-            if (!is_bigint_mode(ctx)) {
+            if (!is_math_mode(ctx)) {
                 ret = BF_ST_INVALID_OP;
-            } else if (op == OP_math_pow) {
-            op_fallback:
-                bf_delete(r);
+            } else {
+            math_mode_div_pow:
+                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);
-                ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op);
                 JS_FreeValue(ctx, op1);
                 JS_FreeValue(ctx, op2);
-                if (ret < 0) {
-                    return -1;
-                } else if (ret == 0) {
-                    JS_ThrowTypeError(ctx, "operator must be defined for exact division or power");
+                if (unlikely(ret)) {
+                    JS_FreeValue(ctx, res);
+                    throw_bf_exception(ctx, ret);
                     return -1;
                 }
                 *pres = res;
                 return 0;
-            } else {
-                double dr;
-                bf_pow(r, a, b, 53, bf_set_exp_bits(11) |
-                       BF_RNDN | BF_FLAG_SUBNORMAL);
-            float64_result:
-                bf_get_float64(r, &dr, BF_RNDN);
-                bf_delete(r);
-                JS_FreeBigInt(ctx, a, &a_s);
-                JS_FreeBigInt(ctx, b, &b_s);
-                JS_FreeValue(ctx, op1);
-                JS_FreeValue(ctx, op2);
-                *pres = __JS_NewFloat64(ctx, dr);
-                return 0;
             }
         } else {
-            ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUICKS);
+            ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
         }
         break;
 
@@ -12494,13 +12677,14 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
     JS_FreeValue(ctx, op1);
     JS_FreeValue(ctx, op2);
     if (unlikely(ret)) {
-        bf_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigInt(ctx, r);
+    *pres = JS_CompactBigInt(ctx, res);
     return 0;
  fail:
+    JS_FreeValue(ctx, res);
     JS_FreeValue(ctx, op1);
     JS_FreeValue(ctx, op2);
     return -1;
@@ -12536,18 +12720,21 @@ static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
 static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
                                       JSValue *pres, JSValue op1, JSValue op2)
 {
-    bfdec_t r_s, *r, *a, *b;
-    int ret, rnd_mode;
-    /* big decimal result */
+    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;
-    r = &r_s;
-    bfdec_init(ctx->bf_ctx, r);
     switch(op) {
     case OP_add:
         ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
@@ -12558,26 +12745,17 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
     case OP_mul:
         ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
         break;
-    case OP_math_div:
     case OP_div:
         ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
         break;
     case OP_math_mod:
         /* Euclidian remainder */
-        rnd_mode = BF_DIVREM_EUCLIDIAN;
-        goto do_mod_dec;
+        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
+        break;
     case OP_mod:
-        rnd_mode = BF_RNDZ;
-    do_mod_dec:
-        {
-            bfdec_t q_s, *q = &q_s;
-            bfdec_init(ctx->bf_ctx, q);
-            ret = bfdec_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, rnd_mode);
-            bfdec_delete(q);
-        }
+        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
         break;
     case OP_pow:
-    case OP_math_pow:
         ret = js_bfdec_pow(r, a, b);
         break;
     default:
@@ -12586,13 +12764,14 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
     JS_FreeValue(ctx, op1);
     JS_FreeValue(ctx, op2);
     if (unlikely(ret)) {
-        bfdec_delete(r);
+        JS_FreeValue(ctx, res);
         throw_bf_exception(ctx, ret);
         return -1;
     }
-    *pres = JS_NewBigDecimal(ctx, r);
+    *pres = res;
     return 0;
  fail:
+    JS_FreeValue(ctx, res);
     JS_FreeValue(ctx, op1);
     JS_FreeValue(ctx, op2);
     return -1;
@@ -12602,7 +12781,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
                                                       OPCodeEnum op)
 {
     JSValue op1, op2, res;
-    BOOL is_legacy;
     uint32_t tag1, tag2;
     int ret;
     double d1, d2;
@@ -12619,12 +12797,11 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
     }
 
     /* 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))) &&
-        ctx->bignum_ext) {
-        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op);
+    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))) {
+        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
         if (ret != 0) {
             JS_FreeValue(ctx, op1);
             JS_FreeValue(ctx, op2);
@@ -12655,23 +12832,23 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         int64_t v;
         v1 = JS_VALUE_GET_INT(op1);
         v2 = JS_VALUE_GET_INT(op2);
-        is_legacy = is_bigint_mode(ctx) ^ 1;
         switch(op) {
         case OP_sub:
             v = (int64_t)v1 - (int64_t)v2;
             break;
         case OP_mul:
             v = (int64_t)v1 * (int64_t)v2;
-            if (is_legacy && v == 0 && (v1 | v2) < 0) {
+            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_math_div:
-            if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-                goto exception;
-            return 0;
         case OP_div:
+            if (is_math_mode(ctx))
+                goto handle_bigint;
             sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
             return 0;
         case OP_math_mod:
@@ -12688,26 +12865,19 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
             }
             break;
         case OP_mod:
-            if (is_legacy && (v1 < 0 || v2 <= 0)) {
+            if (v1 < 0 || v2 <= 0) {
                 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
                 return 0;
             } else {
-                if (unlikely(v2 == 0)) {
-                    throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
-                    goto exception;
-                }
                 v = (int64_t)v1 % (int64_t)v2;
             }
             break;
         case OP_pow:
-        case OP_math_pow:
-            if (is_legacy) {
+            if (!is_math_mode(ctx)) {
                 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
                 return 0;
             } else {
-                if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-                    goto exception;
-                return 0;
+                goto handle_bigint;
             }
             break;
         default:
@@ -12720,6 +12890,10 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
     } 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 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))
+            goto exception;
     } else if (tag1 == JS_TAG_FLOAT64 || tag2 == JS_TAG_FLOAT64) {
         double dr;
         /* float64 result */
@@ -12730,6 +12904,8 @@ 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;
@@ -12738,7 +12914,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
             dr = d1 * d2;
             break;
         case OP_div:
-        case OP_math_div:
             dr = d1 / d2;
             break;
         case OP_mod:
@@ -12752,16 +12927,12 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
                 dr += d2;
             break;
         case OP_pow:
-        case OP_math_pow:
             dr = js_pow(d1, d2);
             break;
         default:
             abort();
         }
         sp[-2] = __JS_NewFloat64(ctx, dr);
-    } else {
-        if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
-            goto exception;
     }
     return 0;
  exception:
@@ -12792,12 +12963,14 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
 
     if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
         /* 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))) &&
-            ctx->bignum_ext) {
-            ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add);
+        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))) {
+            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);
@@ -12886,6 +13059,7 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
     JSValue op1, op2, res;
     int ret;
     uint32_t tag1, tag2;
+    uint32_t v1, v2, r;
 
     op1 = sp[-2];
     op2 = sp[-1];
@@ -12893,12 +13067,11 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
     tag2 = JS_VALUE_GET_NORM_TAG(op2);
 
     /* 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))) &&
-        ctx->bignum_ext) {
-        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op);
+    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))) {
+        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
         if (ret != 0) {
             JS_FreeValue(ctx, op1);
             JS_FreeValue(ctx, op2);
@@ -12911,63 +13084,60 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
         }
     }
 
-    op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER);
+    op1 = JS_ToNumericFree(ctx, op1);
     if (JS_IsException(op1)) {
         JS_FreeValue(ctx, op2);
         goto exception;
     }
-    op2 = JS_ToNumberHintFree(ctx, op2, TON_FLAG_INTEGER);
+    op2 = JS_ToNumericFree(ctx, op2);
     if (JS_IsException(op2)) {
         JS_FreeValue(ctx, op1);
         goto exception;
     }
 
-    if (!is_bigint_mode(ctx)) {
-        uint32_t v1, v2, r;
+    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 {
-                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 {
-            if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
-                JS_FreeValue(ctx, op2);
-                goto exception;
-            }
-            if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
+        bigint_op:
+            if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
                 goto exception;
-            switch(op) {
-            case OP_shl:
-                r = v1 << (v2 & 0x1f);
-                break;
-            case OP_sar:
-                r = (int)v1 >> (v2 & 0x1f);
-                break;
-            case OP_and:
-                r = v1 & v2;
-                break;
-            case OP_or:
-                r = v1 | v2;
-                break;
-            case OP_xor:
-                r = v1 ^ v2;
-                break;
-            default:
-                abort();
-            }
-            sp[-2] = JS_NewInt32(ctx, r);
         }
     } else {
-    bigint_op:
-        if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+        if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
+            JS_FreeValue(ctx, op2);
+            goto exception;
+        }
+        if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
             goto exception;
+        switch(op) {
+        case OP_shl:
+            r = v1 << (v2 & 0x1f);
+            break;
+        case OP_sar:
+            r = (int)v1 >> (v2 & 0x1f);
+            break;
+        case OP_and:
+            r = v1 & v2;
+            break;
+        case OP_or:
+            r = v1 | v2;
+            break;
+        case OP_xor:
+            r = v1 ^ v2;
+            break;
+        default:
+            abort();
+        }
+        sp[-2] = JS_NewInt32(ctx, r);
     }
     return 0;
  exception:
@@ -13081,12 +13251,12 @@ 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);
     /* 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))) &&
-        ctx->bignum_ext) {
-        res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op);
+    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))) {
+        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);
@@ -13165,7 +13335,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
     } else {
         if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
              (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
-            !is_bigint_mode(ctx)) {
+            !is_math_mode(ctx)) {
             if (tag1 == JS_TAG_STRING) {
                 op1 = JS_StringToBigInt(ctx, op1);
                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
@@ -13271,10 +13441,11 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
                 goto exception;
         }
     } else if (tag1 == tag2) {
-        if (tag1 == JS_TAG_OBJECT && ctx->bignum_ext) {
+        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);
+                                             is_neq ? OP_neq : OP_eq,
+                                             FALSE, HINT_NONE);
             if (res != 0) {
                 JS_FreeValue(ctx, op1);
                 JS_FreeValue(ctx, op2);
@@ -13294,7 +13465,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
                (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
 
         if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
-            !is_bigint_mode(ctx)) {
+            !is_math_mode(ctx)) {
             if (tag1 == JS_TAG_STRING) {
                 op1 = JS_StringToBigInt(ctx, op1);
                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
@@ -13336,7 +13507,8 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
 
         /* try the fallback operator */
         res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
-                                          is_neq ? OP_neq : OP_eq);
+                                         is_neq ? OP_neq : OP_eq,
+                                         FALSE, HINT_NONE);
         if (res != 0) {
             JS_FreeValue(ctx, op1);
             JS_FreeValue(ctx, op2);
@@ -13391,7 +13563,7 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
         goto exception;
     }
     /* XXX: could forbid >>> in bignum mode */
-    if (!is_bigint_mode(ctx) &&
+    if (!is_math_mode(ctx) &&
         (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
          JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
         JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
@@ -13433,34 +13605,38 @@ static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
 
 static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
 {
-    bf_t a_s, *a, r_s, *r = &r_s;
-    JSValue op1, op2;
-    slimb_t e;
+    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)
         return -1;
-#if LIMB_BITS == 32
-    ret = JS_ToInt32(ctx, &e, op2);
-#else
-    ret = JS_ToInt64(ctx, &e, op2);
-#endif
+    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_init(ctx->bf_ctx, r);
     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] = JS_NewBigFloat(ctx, r);
+    sp[-2] = res;
     return 0;
 }
 
@@ -13882,22 +14058,9 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
             d2 = JS_VALUE_GET_INT(op2);
             goto number_test;
         } else if (tag2 == JS_TAG_FLOAT64) {
-#ifdef CONFIG_BIGNUM
-            if (is_bigint_mode(ctx)) {
-                res = FALSE;
-            } else
-#endif
-            {
-                d2 = JS_VALUE_GET_FLOAT64(op2);
-                goto number_test;
-            }
-        } else
-#ifdef CONFIG_BIGNUM
-        if (tag2 == JS_TAG_BIG_INT && is_bigint_mode(ctx)) {
-            goto bigint_test;
-        } else
-#endif
-        {
+            d2 = JS_VALUE_GET_FLOAT64(op2);
+            goto number_test;
+        } else {
             res = FALSE;
         }
         break;
@@ -13905,11 +14068,7 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
         d1 = JS_VALUE_GET_FLOAT64(op1);
         if (tag2 == JS_TAG_FLOAT64) {
             d2 = JS_VALUE_GET_FLOAT64(op2);
-        } else if (tag2 == JS_TAG_INT
-#ifdef CONFIG_BIGNUM
-                   && !is_bigint_mode(ctx)
-#endif
-                   ) {
+        } else if (tag2 == JS_TAG_INT) {
             d2 = JS_VALUE_GET_INT(op2);
         } else {
             res = FALSE;
@@ -13936,15 +14095,10 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
     case JS_TAG_BIG_INT:
         {
             bf_t a_s, *a, b_s, *b;
-            if (tag1 == tag2) {
-                /* OK */
-            } else if (tag2 == JS_TAG_INT && is_bigint_mode(ctx)) {
-                /* OK */
-            } else {
+            if (tag1 != tag2) {
                 res = FALSE;
                 break;
             }
-        bigint_test:
             a = JS_ToBigFloat(ctx, &a_s, op1);
             b = JS_ToBigFloat(ctx, &b_s, op2);
             res = bf_cmp_eq(a, b);
@@ -14100,30 +14254,20 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1)
     tag = JS_VALUE_GET_NORM_TAG(op1);
     switch(tag) {
 #ifdef CONFIG_BIGNUM
-    case JS_TAG_INT:
-        if (is_bigint_mode(ctx))
-            atom = JS_ATOM_bigint;
-        else
-            atom = JS_ATOM_number;
-        break;
     case JS_TAG_BIG_INT:
         atom = JS_ATOM_bigint;
         break;
-    case JS_TAG_FLOAT64:
-        atom = JS_ATOM_number;
-        break;
     case JS_TAG_BIG_FLOAT:
         atom = JS_ATOM_bigfloat;
         break;
     case JS_TAG_BIG_DECIMAL:
         atom = JS_ATOM_bigdecimal;
         break;
-#else
+#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         atom = JS_ATOM_number;
         break;
-#endif
     case JS_TAG_UNDEFINED:
         atom = JS_ATOM_undefined;
         break;
@@ -15288,7 +15432,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
     /* we only propagate the bignum mode as some runtime functions
        test it */
     if (prev_sf)
-        sf->js_mode = prev_sf->js_mode & JS_MODE_BIGINT;
+        sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
     else
         sf->js_mode = 0;
 #else
@@ -17315,11 +17459,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                     v1 = JS_VALUE_GET_INT(op1);
                     v2 = JS_VALUE_GET_INT(op2);
                     r = (int64_t)v1 * v2;
-#ifdef CONFIG_BIGNUM
-                    if (unlikely((int)r != r) || (r == 0 && !is_bigint_mode(ctx)))
-                        goto binary_arith_slow;
-#else
                     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;
                     }
@@ -17328,14 +17473,15 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                         d = -0.0;
                         goto mul_fp_res;
                     }
-#endif
                     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);
-#ifndef CONFIG_BIGNUM
                 mul_fp_res:
-#endif
                     sp[-2] = __JS_NewFloat64(ctx, d);
                     sp--;
                 } else {
@@ -17343,11 +17489,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                 }
             }
             BREAK;
-#ifdef CONFIG_BIGNUM
-        CASE(OP_math_div):
-        CASE(OP_div):
-            goto binary_arith_slow;
-#else
         CASE(OP_div):
             {
                 JSValue op1, op2;
@@ -17355,6 +17496,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                 op2 = sp[-1];
                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
                     int v1, v2;
+                    if (unlikely(sf->js_mode & JS_MODE_MATH))
+                        goto binary_arith_slow;
                     v1 = JS_VALUE_GET_INT(op1);
                     v2 = JS_VALUE_GET_INT(op2);
                     sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
@@ -17364,7 +17507,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                 }
             }
             BREAK;
-#endif
         CASE(OP_mod):
 #ifdef CONFIG_BIGNUM
         CASE(OP_math_mod):
@@ -17390,9 +17532,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
             }
             BREAK;
         CASE(OP_pow):
-#ifdef CONFIG_BIGNUM
-        CASE(OP_math_pow):
-#endif
         binary_arith_slow:
             if (js_binary_arith_slow(ctx, sp, opcode))
                 goto exception;
@@ -17422,11 +17561,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                 tag = JS_VALUE_GET_TAG(op1);
                 if (tag == JS_TAG_INT) {
                     val = JS_VALUE_GET_INT(op1);
-#ifdef CONFIG_BIGNUM
-                    if (unlikely(val == INT32_MIN) ||
-                        (val == 0 && !is_bigint_mode(ctx)))
-                        goto slow_neg;
-#else
                     /* Note: -0 cannot be expressed as integer */
                     if (unlikely(val == 0)) {
                         d = -0.0;
@@ -17436,18 +17570,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                         d = -(double)val;
                         goto neg_fp_res;
                     }
-#endif
                     sp[-1] = JS_NewInt32(ctx, -val);
                 } else if (JS_TAG_IS_FLOAT64(tag)) {
                     d = -JS_VALUE_GET_FLOAT64(op1);
-#ifndef CONFIG_BIGNUM
                 neg_fp_res:
-#endif
                     sp[-1] = __JS_NewFloat64(ctx, d);
                 } else {
-#ifdef CONFIG_BIGNUM
-                slow_neg:
-#endif
                     if (js_unary_arith_slow(ctx, sp, opcode))
                         goto exception;
                 }
@@ -17560,7 +17688,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
 #ifdef CONFIG_BIGNUM
                     {
                         int64_t r;
-                        if (is_bigint_mode(ctx)) {
+                        if (unlikely(sf->js_mode & JS_MODE_MATH)) {
                             if (v2 > 0x1f)
                                 goto shl_slow;
                             r = (int64_t)v1 << v2;
@@ -17616,7 +17744,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                     v2 = JS_VALUE_GET_INT(op2);
 #ifdef CONFIG_BIGNUM
                     if (unlikely(v2 > 0x1f)) {
-                        if (is_bigint_mode(ctx))
+                        if (unlikely(sf->js_mode & JS_MODE_MATH))
                             goto sar_slow;
                         else
                             v2 &= 0x1f;
@@ -20182,7 +20310,7 @@ static __exception int next_token(JSParseState *s)
                     ATOD_ACCEPT_UNDERSCORES;
 #ifdef CONFIG_BIGNUM
                 flags |= ATOD_ACCEPT_SUFFIX;
-                if (s->cur_func->js_mode & JS_MODE_BIGINT) {
+                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;
@@ -23312,7 +23440,6 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen
             } else
 #ifdef CONFIG_BIGNUM
             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
-                bf_t r_s, *r = &r_s;
                 slimb_t e;
                 int ret;
 
@@ -23326,9 +23453,9 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen
                     emit_op(s, OP_push_i32);
                     emit_u32(s, e);
                 } else {
-                    bf_init(s->ctx->bf_ctx, r);
-                    bf_set_si(r, e);
-                    val = JS_NewBigInt(s->ctx, r);
+                    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)
@@ -24105,10 +24232,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag)
                 return -1;
             if (js_parse_unary(s, 1))
                 return -1;
-            if (s->cur_func->js_mode & JS_MODE_MATH)
-                emit_op(s, OP_math_pow);
-            else
-                emit_op(s, OP_pow);
+            emit_op(s, OP_pow);
         }
 #else
         if (s->token.val == TOK_POW) {
@@ -24151,12 +24275,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
                 opcode = OP_mul;
                 break;
             case '/':
-#ifdef CONFIG_BIGNUM
-                if (s->cur_func->js_mode & JS_MODE_MATH)
-                    opcode = OP_math_div;
-                else
-#endif
-                    opcode = OP_div;
+                opcode = OP_div;
                 break;
             case '%':
 #ifdef CONFIG_BIGNUM
@@ -24549,12 +24668,8 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted)
             op = assign_opcodes[op - TOK_MUL_ASSIGN];
 #ifdef CONFIG_BIGNUM
             if (s->cur_func->js_mode & JS_MODE_MATH) {
-                if (op == OP_div)
-                    op = OP_math_div;
-                else if (op == OP_mod)
+                if (op == OP_mod)
                     op = OP_math_mod;
-                else if (op == OP_pow)
-                    op = OP_math_pow;
             }
 #endif
             emit_op(s, op);
@@ -30149,20 +30264,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
         len = opcode_info[op].size;
         pos_next = pos + len;
         switch(op) {
-        case OP_close_var_object:
-            {
-                if (s->var_object_idx >= 0) {
-                    /* close the var object and create a new one */
-                    add_pc2line_info(s, bc_out.size, line_num);
-                    dbuf_putc(&bc_out, OP_close_loc);
-                    dbuf_put_u16(&bc_out, s->var_object_idx);
-                    dbuf_putc(&bc_out, OP_special_object);
-                    dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
-                    put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
-                }
-            }
-            break;
-
         case OP_line_num:
             /* line number info (for debug). We put it in a separate
                compressed table to reduce memory usage and get better
@@ -31508,11 +31609,8 @@ static __exception int js_parse_directives(JSParseState *s)
         }
 #endif
 #ifdef CONFIG_BIGNUM
-        else if (s->ctx->bignum_ext && !strcmp(str, "use bigint")) {
-            s->cur_func->js_mode |= JS_MODE_BIGINT;
-        }
         else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
-            s->cur_func->js_mode |= JS_MODE_BIGINT | JS_MODE_MATH;
+            s->cur_func->js_mode |= JS_MODE_MATH;
         }
 #endif
     }
@@ -31804,13 +31902,6 @@ static __exception int js_parse_function_decl2(JSParseState *s,
                 }
                 if (js_parse_destructing_element(s, TOK_VAR, 1, TRUE, -1, TRUE))
                     goto fail;
-                /* Close var object: necessary if direct eval call
-                   occurred in the assignment expression or if any
-                   variable was captured and a later direct eval call
-                   may instantiate it in the var object.
-                   The next pass will generate the capture if required.
-                 */
-                emit_op(s, OP_close_var_object);
             } else if (s->token.val == TOK_IDENT) {
                 if (s->token.u.ident.is_reserved) {
                     js_parse_error_reserved_identifier(s);
@@ -31889,8 +31980,6 @@ static __exception int js_parse_function_decl2(JSParseState *s,
                         set_object_name(s, name);
                         emit_op(s, OP_put_arg);
                         emit_u16(s, idx);
-                        /* Close var object: see above comment. */
-                        emit_op(s, OP_close_var_object);
                         emit_label(s, label);
                     }
                 } else if (!has_opt_arg) {
@@ -34399,30 +34488,20 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
     case JS_TAG_EXCEPTION:
         return JS_DupValue(ctx, val);
 #ifdef CONFIG_BIGNUM
-    case JS_TAG_INT:
-        if (is_bigint_mode(ctx))
-            obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
-        else
-            obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
-        goto set_value;
     case JS_TAG_BIG_INT:
         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
         goto set_value;
-    case JS_TAG_FLOAT64:
-        obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
-        goto set_value;
     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;
-#else
+#endif
     case JS_TAG_INT:
     case JS_TAG_FLOAT64:
         obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
         goto set_value;
-#endif
     case JS_TAG_STRING:
         /* XXX: should call the string constructor */
         {
@@ -34995,10 +35074,13 @@ static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val
     ret = JS_PreventExtensions(ctx, obj);
     if (ret < 0)
         return JS_EXCEPTION;
-    if (reflect)
+    if (reflect) {
         return JS_NewBool(ctx, ret);
-    else
+    } else {
+        if (!ret)
+            return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
         return JS_DupValue(ctx, obj);
+    }
 }
 
 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
@@ -35166,7 +35248,7 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
             goto exception;
     }
     js_free_prop_enum(ctx, props, len);
-    return JS_TRUE;
+    return JS_DupValue(ctx, obj);
 
  exception:
     js_free_prop_enum(ctx, props, len);
@@ -37690,17 +37772,14 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
                                      int argc, JSValueConst *argv)
 {
     JSValue val, obj;
-#ifdef CONFIG_BIGNUM
     if (argc == 0) {
-        if (is_bigint_mode(ctx))
-            val = __JS_NewFloat64(ctx, 0);
-        else
-            val = JS_NewInt32(ctx, 0);
+        val = JS_NewInt32(ctx, 0);
     } else {
         val = JS_ToNumeric(ctx, argv[0]);
         if (JS_IsException(val))
             return val;
         switch(JS_VALUE_GET_TAG(val)) {
+#ifdef CONFIG_BIGNUM
         case JS_TAG_BIG_INT:
         case JS_TAG_BIG_FLOAT:
             {
@@ -37711,25 +37790,19 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
                 val = __JS_NewFloat64(ctx, d);
             }
             break;
-        case JS_TAG_INT:
-            if (is_bigint_mode(ctx)) {
-                /* always return a number in bignum mode */
-                val = __JS_NewFloat64(ctx, JS_VALUE_GET_INT(val));
-            }
+        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;
         }
     }
-#else
-    if (argc == 0) {
-        val = JS_NewInt32(ctx, 0);
-    } else {
-        val = JS_ToNumber(ctx, argv[0]);
-        if (JS_IsException(val))
-            return val;
-    }
-#endif
     if (!JS_IsUndefined(new_target)) {
         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
         if (!JS_IsException(obj))
@@ -37760,7 +37833,6 @@ static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
                                int argc, JSValueConst *argv)
 {
-    /* XXX: should just check for float and big_float */
     if (!JS_IsNumber(argv[0]))
         return JS_FALSE;
     return js_global_isNaN(ctx, this_val, argc, argv);
@@ -37793,8 +37865,7 @@ static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
         return JS_FALSE;
     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
         return JS_EXCEPTION;
-    return JS_NewBool(ctx, isfinite(d) && floor(d) == d &&
-                      fabs(d) <= (double)MAX_SAFE_INTEGER);
+    return JS_NewBool(ctx, is_safe_integer(d));
 }
 
 static const JSCFunctionListEntry js_number_funcs[] = {
@@ -37838,6 +37909,18 @@ static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
     return js_thisNumberValue(ctx, this_val);
 }
 
+static int js_get_radix(JSContext *ctx, JSValueConst val)
+{
+    int radix;
+    if (JS_ToInt32Sat(ctx, &radix, val))
+        return -1;
+    if (radix < 2 || radix > 36) {
+        JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+        return -1;
+    }
+    return radix;
+}
+
 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
                                   int argc, JSValueConst *argv, int magic)
 {
@@ -37851,12 +37934,9 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
     if (magic || JS_IsUndefined(argv[0])) {
         base = 10;
     } else {
-        if (JS_ToInt32Sat(ctx, &base, argv[0]))
-            goto fail;
-        if (base < 2 || base > 36) {
-            JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+        base = js_get_radix(ctx, argv[0]);
+        if (base < 0)
             goto fail;
-        }
     }
     if (JS_ToFloat64Free(ctx, &d, val))
         return JS_EXCEPTION;
@@ -37972,10 +38052,6 @@ static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
         p = str;
         p += skip_spaces(p);
         flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
-#ifdef CONFIG_BIGNUM
-        if (is_bigint_mode(ctx))
-            flags |= ATOD_MODE_BIGINT;
-#endif
         ret = js_atof(ctx, p, NULL, radix, flags);
     }
     JS_FreeCString(ctx, str);
@@ -39738,348 +39814,6 @@ static double js_fmax(double a, double b)
     }
 }
 
-#ifdef CONFIG_BIGNUM
-
-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_s, *r = &r_s;
-    JSFloatEnv *fe;
-    int rnd_mode;
-    JSValue op1;
-
-    op1 = JS_ToNumber(ctx, argv[0]);
-    if (JS_IsException(op1))
-        return op1;
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    fe = &ctx->fp_env;
-    if (argc > 1) {
-        fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
-        if (!fe) {
-            if (a == &a_s)
-                bf_delete(a);
-            JS_FreeValue(ctx, op1);
-            return JS_EXCEPTION;
-        }
-    }
-
-    bf_init(ctx->bf_ctx, r);
-    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_RNDNU;
-        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 JS_NewBigFloat(ctx, r);
-}
-
-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;
-
-    op1 = JS_ToNumber(ctx, argv[0]);
-    if (JS_IsException(op1))
-        return op1;
-    op2 = JS_ToNumber(ctx, argv[1]);
-    if (JS_IsException(op2)) {
-        JS_FreeValue(ctx, op1);
-        return op2;
-    }
-    a = JS_ToBigFloat(ctx, &a_s, op1);
-    b = JS_ToBigFloat(ctx, &b_s, op2);
-    fe = &ctx->fp_env;
-    if (argc > 2) {
-        fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
-        if (!fe) {
-            if (a == &a_s)
-                bf_delete(a);
-            if (b == &b_s)
-                bf_delete(b);
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            return JS_EXCEPTION;
-        }
-    }
-
-    bf_init(ctx->bf_ctx, r);
-    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_QUICKS);
-        break;
-    case MATH_OP_FMOD:
-        fe->status |= bf_fmod(r, a, b, fe->prec, fe->flags);
-        break;
-    case MATH_OP_REM:
-        fe->status |= bf_remainder(r, a, b, fe->prec, fe->flags);
-        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 JS_NewBigFloat(ctx, r);
-}
-
-static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
-                               int argc, JSValueConst *argv, int magic)
-{
-    BOOL is_max = magic;
-    JSValue val, ret;
-    int i;
-    uint32_t tag;
-
-    if (unlikely(argc == 0)) {
-        return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
-    }
-
-    tag = JS_VALUE_GET_TAG(argv[0]);
-    if (tag == JS_TAG_INT) {
-        int a1, r1 = JS_VALUE_GET_INT(argv[0]);
-        for(i = 1; i < argc; i++) {
-            tag = JS_VALUE_GET_TAG(argv[i]);
-            if (tag != JS_TAG_INT) {
-                ret = JS_NewInt32(ctx, r1);
-                goto generic_case;
-            }
-            a1 = JS_VALUE_GET_INT(argv[i]);
-            if (is_max)
-                r1 = max_int(r1, a1);
-            else
-                r1 = min_int(r1, a1);
-        }
-        ret = JS_NewInt32(ctx, r1);
-    } else {
-        ret = JS_ToNumber(ctx, argv[0]);
-        if (JS_IsException(ret))
-            return ret;
-        i = 1;
-    generic_case:
-        for(; i < argc; i++) {
-            val = JS_ToNumber(ctx, argv[i]);
-            if (JS_IsException(val)) {
-                JS_FreeValue(ctx, ret);
-                return val;
-            }
-            if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(ret)) &&
-                JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(val))) {
-                double r, a;
-                r = JS_VALUE_GET_FLOAT64(ret);
-                a = JS_VALUE_GET_FLOAT64(val);
-                if (!isnan(r)) {
-                    if (isnan(a)) {
-                        r = a;
-                    } else {
-                        if (is_max)
-                            r = js_fmax(r, a);
-                        else
-                            r = js_fmin(r, a);
-                    }
-                    ret = __JS_NewFloat64(ctx, r);
-                }
-            } else {
-                bf_t a_s, *a, r_s, *r;
-                int res;
-
-                r = JS_ToBigFloat(ctx, &r_s, ret);
-                if (!bf_is_nan(r)) {
-                    a = JS_ToBigFloat(ctx, &a_s, val);
-                    res = bf_cmp_full(a, r);
-                    if (is_max)
-                        res = -res;
-                    if (bf_is_nan(a) || res < 0) {
-                        JS_FreeValue(ctx, ret);
-                        ret = JS_DupValue(ctx, val);
-                    }
-                    if (a == &a_s)
-                        bf_delete(a);
-                }
-                if (r == &r_s)
-                    bf_delete(r);
-                JS_FreeValue(ctx, val);
-            }
-        }
-    }
-    return ret;
-}
-
-static JSValue js_math_abs(JSContext *ctx, JSValueConst this_val,
-                           int argc, JSValueConst *argv)
-{
-    JSValue val;
-    uint32_t tag;
-
-    val = JS_ToNumeric(ctx, argv[0]);
-    tag = JS_VALUE_GET_NORM_TAG(val);
-    switch(tag) {
-    case JS_TAG_INT:
-        if (JS_VALUE_GET_INT(val) < 0)
-            val = JS_NewInt64(ctx, -(int64_t)JS_VALUE_GET_INT(val));
-        break;
-    case JS_TAG_FLOAT64:
-        val = __JS_NewFloat64(ctx, fabs(JS_VALUE_GET_FLOAT64(val)));
-        break;
-    case JS_TAG_BIG_FLOAT:
-    case JS_TAG_BIG_INT:
-        {
-            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);
-            r->sign = 0;
-            JS_FreeValue(ctx, val);
-            if (tag == JS_TAG_BIG_FLOAT)
-                val = JS_NewBigFloat(ctx, r);
-            else
-                val = JS_NewBigInt2(ctx, r, TRUE);
-        }
-        break;
-    default:
-        break;
-    }
-    return val;
-}
-
-#if 0
-/* XXX: should give exact rounding */
-/* XXX: correct NaN/Infinity handling */
-static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
-                             int argc, JSValueConst *argv)
-{
-    bf_t a_s, *a, r_s, *r = &r_s, r2_s, *r2 = &r2_s;
-    JSValue val;
-    int i;
-    BOOL is_float;
-
-    bf_init(ctx->bf_ctx, r);
-    bf_set_si(r, 0);
-    for(i = 0; i < argc; i++) {
-        val = JS_ToNumber(ctx, argv[i]);
-        if (JS_IsException(val)) {
-            bf_delete(r);
-            return val;
-        }
-        a = JS_ToBigFloat(ctx, &is_float, &a_s, val);
-        bf_add(r, r, a, ctx->fp_env.prec, ctx->fp_env.flags);
-        if (a == &a_s)
-            bf_delete(a);
-    }
-    bf_init(ctx->bf_ctx, r2);
-    bf_sqrt(r2, r, ctx->fp_env.prec, ctx->fp_env.flags);
-    bf_delete(r);
-    return JS_NewBigFloat(ctx, r2);
-}
-#endif
-
-#else
-
 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
                                int argc, JSValueConst *argv, int magic)
 {
@@ -40133,8 +39867,6 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
     }
 }
 
-#endif /* !CONFIG_BIGNUM */
-
 static double js_math_sign(double a)
 {
     if (isnan(a) || a == 0.0)
@@ -40267,11 +39999,7 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
 static const JSCFunctionListEntry js_math_funcs[] = {
     JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
     JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
-#ifdef CONFIG_BIGNUM
-    JS_CFUNC_DEF("abs", 1, js_math_abs ),
-#else
     JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
-#endif
     JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
     JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
     JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
@@ -41959,46 +41687,12 @@ static JSValue json_parse_value(JSParseState *s)
         }
         is_neg = 1;
     number:
-#ifdef CONFIG_BIGNUM
-        val = JS_DupValue(ctx, s->token.u.num.val);
-        if (is_neg) {
-            switch(JS_VALUE_GET_NORM_TAG(val)) {
-            case JS_TAG_BIG_INT:
-            case JS_TAG_BIG_FLOAT:
-                {
-                    JSBigFloat *p;
-                    p = JS_VALUE_GET_PTR(val);
-                    bf_neg(&p->num);
-                }
-                break;
-            case JS_TAG_FLOAT64:
-                {
-                    double d;
-                    d = JS_VALUE_GET_FLOAT64(val);
-                    val = __JS_NewFloat64(ctx, -d);
-                }
-                break;
-            default:
-            case JS_TAG_INT:
-                {
-                    int v;
-                    v = JS_VALUE_GET_INT(val);
-                    if (v == 0 && !is_bigint_mode(s->ctx))
-                        val = __JS_NewFloat64(ctx, -0.0);
-                    else
-                        val = JS_NewInt64(ctx, -(int64_t)v);
-                }
-                break;
-            }
-        }
-#else
         val = s->token.u.num.val;
         if (is_neg) {
             double d;
             JS_ToFloat64(ctx, &d, val);  /* no exception possible */
             val = JS_NewFloat64(ctx, -d);
         }
-#endif
         if (next_token(s))
             goto fail;
         break;
@@ -42168,10 +41862,6 @@ static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
     return r;
 }
 
-#ifdef CONFIG_BIGNUM
-static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v);
-#endif
-
 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
                              JSValueConst holder, JSValue val, JSValueConst key)
 {
@@ -46764,7 +46454,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
     double d;
     int p, i, c, sgn;
     JSString *sp;
-
+    BOOL is_local;
+    
     rv = JS_NAN;
 
     s = JS_ToString(ctx, argv[0]);
@@ -46780,11 +46471,15 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
         if (string_get_signed_field(sp, &p, &fields[0]))
             goto done;
 
+        is_local = TRUE;
         for (i = 1; i < 6; i++) {
             if (string_get_field(sp, &p, &fields[i]))
                 break;
         }
-        if (i == 6 && p < sp->len && string_get(sp, p) == '.') {
+        if (i <= 3) {
+            /* no time: UTC by default */
+            is_local = FALSE;
+        } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') {
             /* parse milliseconds as a fractional part, round to nearest */
             /* XXX: the spec does not indicate which rounding should be used */
             int mul = 1000, ms = 0;
@@ -46802,14 +46497,20 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
 
         /* parse the time zone offset if present: [+-]HH:mm */
         tz = 0;
-        if (p < sp->len && ((sgn = string_get(sp, p)) == '+' || sgn == '-')) {
-            if (string_get_field(sp, &p, &hh))
-                goto done;
-            if (string_get_field(sp, &p, &mm))
-                goto done;
-            tz = hh * 60 + mm;
-            if (sgn == '-')
-                tz = -tz;
+        if (p < sp->len) {
+            sgn = string_get(sp, p);
+            if (sgn == '+' || sgn == '-') {
+                if (string_get_field(sp, &p, &hh))
+                    goto done;
+                if (string_get_field(sp, &p, &mm))
+                    goto done;
+                tz = hh * 60 + mm;
+                if (sgn == '-')
+                    tz = -tz;
+                is_local = FALSE;
+            } else if (sgn == 'Z') {
+                is_local = FALSE;
+            }
         }
     } else {
         /* toString or toUTCString format */
@@ -46844,6 +46545,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
         // XXX: parse optional milliseconds?
 
         /* parse the time zone offset if present: [+-]HHmm */
+        is_local = FALSE;
         tz = 0;
         for (tz = 0; p < sp->len; p++) {
             sgn = string_get(sp, p);
@@ -46860,7 +46562,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
             }
         }
     }
-    d = set_date_fields(fields, 0) - tz * 60000;
+    d = set_date_fields(fields, is_local) - tz * 60000;
     rv = __JS_NewFloat64(ctx, d);
 
 done:
@@ -47080,61 +46782,364 @@ void JS_AddIntrinsicEval(JSContext *ctx)
 
 #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]);
+}
+
 /* BigInt */
 
 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
 {
     uint32_t tag;
-    BOOL is_legacy;
-    int ret;
 
-    is_legacy = is_bigint_mode(ctx) ^ 1;
  redo:
     tag = JS_VALUE_GET_NORM_TAG(val);
     switch(tag) {
     case JS_TAG_INT:
     case JS_TAG_BOOL:
-        if (is_legacy) {
-            bf_t r_s, *r = &r_s;
-            bf_init(ctx->bf_ctx, r);
-            bf_set_si(r, JS_VALUE_GET_INT(val));
-            val = JS_NewBigInt2(ctx, r, TRUE);
-        } else {
-            val = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
-        }
+        val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
         break;
     case JS_TAG_BIG_INT:
         break;
     case JS_TAG_FLOAT64:
     case JS_TAG_BIG_FLOAT:
         {
-            bf_t *a, a_s, r_s, *r = &r_s;
+            bf_t *a, a_s;
+            
             a = JS_ToBigFloat(ctx, &a_s, val);
-            bf_init(ctx->bf_ctx, r);
             if (!bf_is_finite(a)) {
                 JS_FreeValue(ctx, val);
                 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
             } else {
-                bf_set(r, a);
-                ret = bf_rint(r, BF_RNDZ);
+                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 (is_legacy && (ret & BF_ST_INEXACT)) {
-                    bf_delete(r);
+                if (ret & BF_ST_MEM_ERROR) {
+                    JS_FreeValue(ctx, val1);
+                    val = JS_ThrowOutOfMemory(ctx);
+                } else if (ret & BF_ST_INEXACT) {
+                    JS_FreeValue(ctx, val1);
                     val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
                 } else {
-                    val = JS_NewBigInt2(ctx, r, is_legacy);
+                    val = JS_CompactBigInt(ctx, val1);
                 }
             }
             if (a == &a_s)
                 bf_delete(a);
         }
         break;
+    case JS_TAG_BIG_DECIMAL:
+        val = JS_ToStringFree(ctx, val);
+         if (JS_IsException(val))
+            break;
+        goto redo;
     case JS_TAG_STRING:
         val = JS_StringToBigIntErr(ctx, val);
         break;
     case JS_TAG_OBJECT:
-        val = JS_ToPrimitiveFree(ctx, val,
-                                 is_legacy ? HINT_NUMBER : HINT_INTEGER);
+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
         if (JS_IsException(val))
             break;
         goto redo;
@@ -47154,17 +47159,6 @@ static JSValue js_bigint_constructor(JSContext *ctx,
     return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
 }
 
-static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
-{
-    int tag = JS_VALUE_GET_TAG(v);
-    if (tag == JS_TAG_BIG_INT)
-        return TRUE;
-    if (is_bigint_mode(ctx))
-        return tag == JS_TAG_INT;
-    else
-        return FALSE;
-}
-
 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
 {
     if (JS_IsBigInt(ctx, this_val))
@@ -47173,8 +47167,6 @@ static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
         JSObject *p = JS_VALUE_GET_OBJ(this_val);
         if (p->class_id == JS_CLASS_BIG_INT) {
-            /* XXX: may relax the check in case the object comes from
-               bignum mode */
             if (JS_IsBigInt(ctx, p->u.object_data))
                 return JS_DupValue(ctx, p->u.object_data);
         }
@@ -47195,12 +47187,9 @@ static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
     if (argc == 0 || JS_IsUndefined(argv[0])) {
         base = 10;
     } else {
-        if (JS_ToInt32Sat(ctx, &base, argv[0]))
+        base = js_get_radix(ctx, argv[0]);
+        if (base < 0)
             goto fail;
-        if (base < 2 || base > 36) {
-            JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
-            goto fail;
-        }
     }
     ret = js_bigint_to_string1(ctx, val, base);
     JS_FreeValue(ctx, val);
@@ -47220,72 +47209,96 @@ 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_s, *r = &r_s, q_s, *q = &q_s;
+    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)
-        return JS_EXCEPTION;
+        goto fail;
     b = JS_ToBigInt(ctx, &b_s, argv[1]);
     if (!b) {
         JS_FreeBigInt(ctx, a, &a_s);
-        return JS_EXCEPTION;
+        goto fail;
     }
-    bf_init(ctx->bf_ctx, q);
-    bf_init(ctx->bf_ctx, r);
+    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)) {
-        bf_delete(q);
-        bf_delete(r);
         throw_bf_exception(ctx, status);
-        return JS_EXCEPTION;
+        goto fail;
     }
+    q_val = JS_CompactBigInt(ctx, q_val);
     if (magic & 0x10) {
         JSValue ret;
-        /* XXX: handle exceptions */
         ret = JS_NewArray(ctx);
-        JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, q));
-        JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, r));
+        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 {
-        bf_delete(r);
-        return JS_NewBigInt(ctx, q);
+        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_s, *r = &r_s, rem_s, *rem = &rem_s;
+    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)
-        return JS_EXCEPTION;
-    bf_init(ctx->bf_ctx, r);
-    bf_init(ctx->bf_ctx, rem);
+        goto fail;
     status = bf_sqrtrem(r, rem, a);
     JS_FreeBigInt(ctx, a, &a_s);
     if (unlikely(status & ~BF_ST_INEXACT)) {
-        bf_delete(r);
-        bf_delete(rem);
-        return throw_bf_exception(ctx, status);
+        throw_bf_exception(ctx, status);
+        goto fail;
     }
+    r_val = JS_CompactBigInt(ctx, r_val);
     if (magic) {
         JSValue ret;
-        /* XXX: handle exceptions */
         ret = JS_NewArray(ctx);
-        JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, r));
-        JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, rem));
+        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 {
-        bf_delete(rem);
-        return JS_NewBigInt(ctx, r);
+        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,
@@ -47326,15 +47339,22 @@ static JSValue js_bigint_asUintN(JSContext *ctx,
                                   int argc, JSValueConst *argv, int asIntN)
 {
     uint64_t bits;
-    bf_t a_s, *a = &a_s, r_s, *r = &r_s, mask_s, *mask = &mask_s;
-
+    bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
+    JSValue res;
+    
     if (JS_ToIndex(ctx, &bits, argv[0]))
         return JS_EXCEPTION;
+    res = JS_NewBigInt(ctx);
+    if (JS_IsException(res))
+        return JS_EXCEPTION;
+    r = JS_GetBigInt(res);
     a = JS_ToBigInt(ctx, &a_s, argv[1]);
-    if (!a)
+    if (!a) {
+        JS_FreeValue(ctx, res);
         return JS_EXCEPTION;
+    }
     /* XXX: optimize */
-    bf_init(ctx->bf_ctx, r);
+    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);
@@ -47351,7 +47371,7 @@ static JSValue js_bigint_asUintN(JSContext *ctx,
     }
     bf_delete(mask);
     JS_FreeBigInt(ctx, a, &a_s);
-    return JS_NewBigInt(ctx, r);
+    return JS_CompactBigInt(ctx, res);
 }
 
 static const JSCFunctionListEntry js_bigint_funcs[] = {
@@ -47430,12 +47450,9 @@ static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
     if (argc == 0 || JS_IsUndefined(argv[0])) {
         base = 10;
     } else {
-        if (JS_ToInt32Sat(ctx, &base, argv[0]))
-            goto fail;
-        if (base < 2 || base > 36) {
-            JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+        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);
@@ -47451,7 +47468,7 @@ static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
     return js_thisBigFloatValue(ctx, this_val);
 }
 
-static int get_rnd_mode(JSContext *ctx, JSValueConst val)
+static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
 {
     int rnd_mode;
     if (JS_ToInt32Sat(ctx, &rnd_mode, val))
@@ -47468,8 +47485,7 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
 {
     JSValue val, ret;
     int64_t f;
-    int res, rnd_mode;
-    bf_t a_s, *a, b;
+    int rnd_mode, radix;
 
     val = js_thisBigFloatValue(ctx, this_val);
     if (JS_IsException(val))
@@ -47481,26 +47497,19 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
         goto fail;
     }
     rnd_mode = BF_RNDNA;
+    radix = 10;
+    /* XXX: swap parameter order for rounding mode and radix */
     if (argc > 1) {
-        rnd_mode = get_rnd_mode(ctx, argv[1]);
+        rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
         if (rnd_mode < 0)
             goto fail;
     }
-
-    a = JS_ToBigFloat(ctx, &a_s, val);
-    if (!a)
-        goto fail;
-    bf_init(ctx->bf_ctx, &b);
-    bf_set_float64(&b, 1e21);
-    res = bf_cmpu(a, &b);
-    bf_delete(&b);
-    if (a == &a_s)
-        bf_delete(a);
-    if (res >= 0) {
-        ret = JS_ToString(ctx, val);
-    } else {
-        ret = js_ftoa(ctx, val, 10, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+    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:
@@ -47533,7 +47542,7 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
 {
     JSValue val, ret;
     int64_t f;
-    int rnd_mode;
+    int rnd_mode, radix;
 
     val = js_thisBigFloatValue(ctx, this_val);
     if (JS_IsException(val))
@@ -47551,12 +47560,18 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
             goto fail;
         }
         rnd_mode = BF_RNDNA;
+        radix = 10;
         if (argc > 1) {
-            rnd_mode = get_rnd_mode(ctx, argv[1]);
+            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
             if (rnd_mode < 0)
                 goto fail;
         }
-        ret = js_ftoa(ctx, val, 10, f + 1,
+        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);
@@ -47571,7 +47586,7 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
 {
     JSValue val, ret;
     int64_t p;
-    int rnd_mode;
+    int rnd_mode, radix;
 
     val = js_thisBigFloatValue(ctx, this_val);
     if (JS_IsException(val))
@@ -47589,12 +47604,18 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
             goto fail;
         }
         rnd_mode = BF_RNDNA;
+        radix = 10;
         if (argc > 1) {
-            rnd_mode = get_rnd_mode(ctx, argv[1]);
+            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
             if (rnd_mode < 0)
                 goto fail;
         }
-        ret = js_ftoa(ctx, val, 10, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
+        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;
@@ -47617,10 +47638,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx,
 {
     JSValue val;
     if (argc == 0) {
-        bf_t r_s, *r = &r_s;
-        bf_init(ctx->bf_ctx, r);
+        bf_t *r;
+        val = JS_NewBigFloat(ctx);
+        if (JS_IsException(val))
+            return val;
+        r = JS_GetBigFloat(val);
         bf_set_zero(r, 0);
-        val = JS_NewBigFloat(ctx, r);
     } else {
         val = JS_DupValue(ctx, argv[0]);
     redo:
@@ -47629,18 +47652,26 @@ static JSValue js_bigfloat_constructor(JSContext *ctx,
             break;
         case JS_TAG_FLOAT64:
             {
-                bf_t r_s, *r = &r_s;
-                bf_init(ctx->bf_ctx, r);
-                bf_set_float64(r, JS_VALUE_GET_FLOAT64(val));
-                val = JS_NewBigFloat(ctx, r);
+                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_s, *r = &r_s;
-                bf_init(ctx->bf_ctx, r);
-                bf_set_si(r, JS_VALUE_GET_INT(val));
-                val = JS_NewBigFloat(ctx, r);
+                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:
@@ -47650,6 +47681,11 @@ static JSValue js_bigfloat_constructor(JSContext *ctx,
                 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;
@@ -47663,10 +47699,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx,
                 p = str;
                 p += skip_spaces(p);
                 if ((p - str) == len) {
-                    bf_t r_s, *r = &r_s;
-                    bf_init(ctx->bf_ctx, r);
-                    bf_set_si(r, 0);
-                    val = JS_NewBigFloat(ctx, r);
+                    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 |
@@ -47699,13 +47737,20 @@ static JSValue js_bigfloat_constructor(JSContext *ctx,
         }
     }
     return val;
+ fail:
+    JS_FreeValue(ctx, val);
+    return JS_EXCEPTION;
 }
 
 static JSValue js_bigfloat_get_const(JSContext *ctx,
-                                  JSValueConst this_val, int magic)
+                                     JSValueConst this_val, int magic)
 {
-    bf_t r_s, *r = &r_s;
-    bf_init(ctx->bf_ctx, r);
+    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);
@@ -47741,13 +47786,13 @@ static JSValue js_bigfloat_get_const(JSContext *ctx,
     default:
         abort();
     }
-    return JS_NewBigFloat(ctx, r);
+    return val;
 }
 
 static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
                                       int argc, JSValueConst *argv)
 {
-    bf_t a_s, *a = &a_s;
+    bf_t *a;
     const char *str;
     JSValue ret;
     int radix;
@@ -47771,10 +47816,13 @@ static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
         if (!fe)
             goto fail;
     }
-    bf_init(ctx->bf_ctx, a);
+    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);
-    ret = JS_NewBigFloat(ctx, a);
+ done:
     JS_FreeCString(ctx, str);
     return ret;
 }
@@ -47800,7 +47848,201 @@ static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
     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));
+    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);
+    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);
+    b = JS_ToBigFloat(ctx, &b_s, op2);
+    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 (a == &a_s)
+            bf_delete(a);
+        if (b == &b_s)
+            bf_delete(b);
+        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[] = {
@@ -47895,9 +48137,6 @@ static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
     return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
 }
 
-/* temporary fix for string conversion overflows */
-#define BF_EXP_BITS_MAX1 (BF_EXP_BITS_MAX - 1)
-
 static JSValue js_float_env_setPrec(JSContext *ctx,
                                     JSValueConst this_val,
                                     int argc, JSValueConst *argv)
@@ -47913,18 +48152,16 @@ static JSValue js_float_env_setPrec(JSContext *ctx,
         return JS_EXCEPTION;
     if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
         return JS_ThrowRangeError(ctx, "invalid precision");
-    exp_bits = BF_EXP_BITS_MAX1;
+    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_MAX1)
+        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_set_exp_bits(exp_bits);
-    if (exp_bits != BF_EXP_BITS_MAX1)
-        flags |= BF_FLAG_SUBNORMAL;
+    flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
 
     saved_prec = ctx->fp_env.prec;
     saved_flags = ctx->fp_env.flags;
@@ -47984,24 +48221,20 @@ static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_v
     case FE_EXP:
         if (JS_ToInt32Sat(ctx, &b, val))
             return JS_EXCEPTION;
-        if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX1)
+        if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
-        if (b == BF_EXP_BITS_MAX1)
-            fe->flags &= ~BF_FLAG_SUBNORMAL;
         fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
             bf_set_exp_bits(b);
         break;
     case FE_RNDMODE:
-        b = get_rnd_mode(ctx, val);
+        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);
-        if (bf_get_exp_bits(fe->flags) != BF_EXP_BITS_MAX1) {
-            fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
-        }
+        fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
         break;
     default:
         b = JS_ToBool(ctx, val);
@@ -48031,12 +48264,12 @@ static const JSCFunctionListEntry js_float_env_funcs[] = {
     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("RNDNU", BF_RNDNU, 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_MAX1, 0 ),
+    JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
 };
 
 static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
@@ -48112,10 +48345,18 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
     case JS_TAG_BOOL:
     case JS_TAG_INT:
         {
-            bfdec_t r_s, *r = &r_s;
-            bfdec_init(ctx->bf_ctx, r);
-            bfdec_set_si(r, JS_VALUE_GET_INT(val));
-            val = JS_NewBigDecimal(ctx, r);
+            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:
@@ -48138,10 +48379,12 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
             p = str;
             p += skip_spaces(p);
             if ((p - str) == len) {
-                bfdec_t r_s, *r = &r_s;
-                bfdec_init(ctx->bf_ctx, r);
+                bfdec_t *r;
+                val = JS_NewBigDecimal(ctx);
+                if (JS_IsException(val))
+                    break;
+                r = JS_GetBigDecimal(val);
                 bfdec_set_zero(r, 0);
-                val = JS_NewBigDecimal(ctx, r);
                 err = 0;
             } else {
                 val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
@@ -48166,12 +48409,14 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
         goto redo;
     case JS_TAG_UNDEFINED:
         {
-            bfdec_t r_s, *r = &r_s;
+            bfdec_t *r;
             if (!allow_null_or_undefined)
                 goto fail;
-            bfdec_init(ctx->bf_ctx, r);
+            val = JS_NewBigDecimal(ctx);
+            if (JS_IsException(val))
+                break;
+            r = JS_GetBigDecimal(val);
             bfdec_set_nan(r);
-            val = JS_NewBigDecimal(ctx, r);
         }
         break;
     default:
@@ -48188,10 +48433,12 @@ static JSValue js_bigdecimal_constructor(JSContext *ctx,
 {
     JSValue val;
     if (argc == 0) {
-        bfdec_t r_s, *r = &r_s;
-        bfdec_init(ctx->bf_ctx, r);
+        bfdec_t *r;
+        val = JS_NewBigDecimal(ctx);
+        if (JS_IsException(val))
+            return val;
+        r = JS_GetBigDecimal(val);
         bfdec_set_zero(r, 0);
-        val = JS_NewBigDecimal(ctx, r);
     } else {
         val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
     }
@@ -48230,6 +48477,39 @@ static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
     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;
@@ -48239,10 +48519,9 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
                                  JSValueConst obj)
 {
     JSValue prop;
-    const char *str;
-    size_t size;
     int64_t val;
     BOOL has_prec;
+    int rnd_mode;
     
     if (!JS_IsObject(obj)) {
         JS_ThrowTypeErrorNotAnObject(ctx);
@@ -48251,29 +48530,11 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
     prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
     if (JS_IsException(prop))
         return -1;
-    str = JS_ToCStringLen(ctx, &size, prop);
+    rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
     JS_FreeValue(ctx, prop);
-    if (!str)
-        return -1;
-    if (strlen(str) != size)
-        goto invalid_rounding_mode;
-    if (!strcmp(str, "floor")) {
-        fe->flags = BF_RNDD;
-    } else if (!strcmp(str, "ceiling")) {
-        fe->flags = BF_RNDU;
-    } else if (!strcmp(str, "down")) {
-        fe->flags = BF_RNDZ;
-    } else if (!strcmp(str, "half-even")) {
-        fe->flags = BF_RNDN;
-    } else if (!strcmp(str, "half-up")) {
-        fe->flags = BF_RNDNA;
-    } else {
-    invalid_rounding_mode:
-        JS_FreeCString(ctx, str);
-        JS_ThrowTypeError(ctx, "invalid rounding mode");
+    if (rnd_mode < 0)
         return -1;
-    }
-    JS_FreeCString(ctx, str);
+    fe->flags = rnd_mode;
     
     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
     if (JS_IsException(prop))
@@ -48282,7 +48543,7 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
     if (!JS_IsUndefined(prop)) {
         if (JS_ToInt64SatFree(ctx, &val, prop))
             return -1;
-        if (val < 0 || val > BF_PREC_MAX)
+        if (val < 1 || val > BF_PREC_MAX)
             goto invalid_precision;
         fe->prec = val;
         has_prec = TRUE;
@@ -48320,7 +48581,7 @@ 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;
+    JSValue op1, op2, res;
     BigDecimalEnv fe_s, *fe = &fe_s;
     int op_count, ret;
 
@@ -48354,21 +48615,18 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
     fe->flags = BF_RNDZ;
     fe->prec = BF_PREC_INF;
     if (op_count < argc) {
-        if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) {
-        fail:
-            JS_FreeValue(ctx, op1);
-            JS_FreeValue(ctx, op2);
-            return JS_EXCEPTION;
-        }
-        if ((fe->flags & BF_FLAG_RADPNT_PREC) &&
-            magic != MATH_OP_DIV &&
-            magic != MATH_OP_ROUND) {
-            JS_ThrowTypeError(ctx, "maximumFractionDigits is not supported for this operation");
+        if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
             goto fail;
-        }
     }
 
-    bfdec_init(ctx->bf_ctx, r);
+    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);
@@ -48382,6 +48640,9 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
     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;
@@ -48398,16 +48659,119 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
     ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
         BF_ST_OVERFLOW;
     if (ret != 0) {
-        bfdec_delete(r);
+        JS_FreeValue(ctx, res);
         return throw_bf_exception(ctx, ret);
     } else {
-        return JS_NewBigDecimal(ctx, r);
+        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[] = {
@@ -48415,6 +48779,7 @@ static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
     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 ),
 };
@@ -49512,6 +49877,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
     count = min_int(final - from, len - to);
     if (count > 0) {
         p = JS_VALUE_GET_OBJ(this_val);
+        if (typed_array_is_detached(ctx, p))
+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
         shift = typed_array_size_log2(p->class_id);
         memmove(p->u.array.u.uint8_ptr + (to << shift),
                 p->u.array.u.uint8_ptr + (from << shift),
@@ -49579,6 +49946,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
             return JS_EXCEPTION;
     }
 
+    if (typed_array_is_detached(ctx, p))
+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+    
     shift = typed_array_size_log2(p->class_id);
     switch(shift) {
     case 0:
index 8fcd7e5c7f09ba9073f550e35db5164427768b9d..57eded89f8adffc43d5bd8e3662811b1049e0efa 100644 (file)
--- a/quickjs.h
+++ b/quickjs.h
@@ -53,7 +53,7 @@ typedef struct JSClass JSClass;
 typedef uint32_t JSClassID;
 typedef uint32_t JSAtom;
 
-#if defined(__x86_64__) || defined(__aarch64__)
+#if INTPTR_MAX >= INT64_MAX
 #define JS_PTR64
 #define JS_PTR64_DEF(a) a
 #else
@@ -366,7 +366,9 @@ void JS_AddIntrinsicPromise(JSContext *ctx);
 void JS_AddIntrinsicBigInt(JSContext *ctx);
 void JS_AddIntrinsicBigFloat(JSContext *ctx);
 void JS_AddIntrinsicBigDecimal(JSContext *ctx);
-/* enable "use bigint", "use math" and operator overloading */
+/* enable operator overloading */
+void JS_AddIntrinsicOperators(JSContext *ctx);
+/* enable "use math" */
 void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
 
 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
@@ -531,12 +533,16 @@ static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
     return v;
 }
 
-JS_BOOL JS_IsNumber(JSValueConst v);
+static inline JS_BOOL JS_IsNumber(JSValueConst v)
+{
+    int tag = JS_VALUE_GET_TAG(v);
+    return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag);
+}
 
-static inline JS_BOOL JS_IsInteger(JSValueConst v)
+static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
 {
     int tag = JS_VALUE_GET_TAG(v);
-    return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT;
+    return tag == JS_TAG_BIG_INT;
 }
 
 static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
diff --git a/repl.js b/repl.js
index 47c8636b908f312f8466965808c85f28163cb530..a5f95f580004216703b6da2e340867cdc78a2e59 100644 (file)
--- a/repl.js
+++ b/repl.js
@@ -1,8 +1,8 @@
 /*
  * QuickJS Read Eval Print Loop
  * 
- * Copyright (c) 2017-2019 Fabrice Bellard
- * Copyright (c) 2017-2019 Charlie Gordon
+ * Copyright (c) 2017-2020 Fabrice Bellard
+ * Copyright (c) 2017-2020 Charlie Gordon
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -860,18 +860,6 @@ import * as os from "os";
     var hex_mode = false;
     var eval_mode = "std";
 
-    function bignum_typeof(a) {
-        "use bigint";
-        return typeof a;
-    }
-
-    function eval_mode_typeof(a) {
-        if (eval_mode === "std")
-            return typeof a;
-        else
-            return bignum_typeof(a);
-    }
-    
     function number_to_string(a, radix) {
         var s;
         if (!isFinite(a)) {
@@ -897,13 +885,6 @@ import * as os from "os";
                     s = a.toString();
                 }
             }
-            if (eval_mode !== "std" && s.indexOf(".") < 0 &&
-                ((radix == 16 && s.indexOf("p") < 0) ||
-                 (radix == 10 && s.indexOf("e") < 0))) {
-                /* add a decimal point so that the floating point type
-                   is visible */
-                s += ".0";
-            }
             return s;
         }
     }
@@ -975,7 +956,7 @@ import * as os from "os";
         function print_rec(a) {
             var n, i, keys, key, type, s;
             
-            type = eval_mode_typeof(a);
+            type = typeof(a);
             if (type === "object") {
                 if (a === null) {
                     std.puts(a);
@@ -1037,7 +1018,7 @@ import * as os from "os";
             } else if (type === "bigfloat") {
                 std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10));
             } else if (type === "bigdecimal") {
-                std.puts(a.toString() + "d");
+                std.puts(a.toString() + "m");
             } else if (type === "symbol") {
                 std.puts(String(a));
             } else if (type === "function") {
@@ -1133,8 +1114,7 @@ import * as os from "os";
             param = expr.substring(cmd.length + 1).trim();
             if (param === "") {
                 std.puts("Running mode=" + eval_mode + "\n");
-            } else if (param === "std" || param === "math" ||
-                       param === "bigint") {
+            } else if (param === "std" || param === "math") {
                 eval_mode = param;
             } else {
                 std.puts("Invalid mode\n");
@@ -1192,7 +1172,7 @@ import * as os from "os";
             std.puts("\\p [m [e]]  set the BigFloat precision to 'm' bits\n" +
                      "\\digits n   set the BigFloat precision to 'ceil(n*log2(10))' bits\n");
             if (!has_jscalc) {
-                std.puts("\\mode [std|bigint|math] change the running mode (current = " + eval_mode + ")\n");
+                std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n");
             }
         }
         if (!config_numcalc) {
@@ -1206,8 +1186,6 @@ import * as os from "os";
         try {
             if (eval_mode === "math")
                 expr = '"use math"; void 0;' + expr;
-            else if (eval_mode === "bigint")
-                expr = '"use bigint"; void 0;' + expr;
             var now = (new Date).getTime();
             /* eval as a script */
             result = std.evalScript(expr, { backtrace_barrier: true });
index 90adb53d63d83cbdafa210b6df1275345741b058..4b20792d6ccfbf714d6137d17262d809ff127941 100644 (file)
@@ -1,4 +1,17 @@
+test262/test/language/expressions/arrow-function/eval-var-scope-syntax-err.js:47: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/async-generator/eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/async-generator/named-eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/class/super-evaluation-order.js:26: Test262Error: via ArgumentListEvaluation Expected SameValue(«0», «123») to be true
+test262/test/language/expressions/class/super-evaluation-order.js:26: strict mode: Test262Error: via ArgumentListEvaluation Expected SameValue(«0», «123») to be true
 test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
 test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
+test262/test/language/expressions/function/eval-var-scope-syntax-err.js:48: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/object/method-definition/async-gen-meth-eval-var-scope-syntax-err.js:32: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/object/method-definition/gen-meth-eval-var-scope-syntax-err.js:54: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/expressions/object/method-definition/meth-eval-var-scope-syntax-err.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
 test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: value has no property
 test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: value has no property
+test262/test/language/statements/async-generator/eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/statements/function/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
+test262/test/language/statements/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
index 074592909d696fa61a88835cbe1bb847b2a76d9b..f4f72a0a670b3f47fc3cd0e0b04b89fa60083c62 100644 (file)
@@ -171,67 +171,152 @@ function test_bigfloat()
     e = new BigFloatEnv(128);
     assert(e.prec == 128);
     a = BigFloat.sqrt(2l, e);
-    assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, 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(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(1d === 1d);
-    assert(1d !== 2d);
-    test_less(1d, 2d);
-    test_eq(2d, 2d);
+    assert(1m === 1m);
+    assert(1m !== 2m);
+    test_less(1m, 2m);
+    test_eq(2m, 2m);
     
-    test_less(1, 2d);
-    test_eq(2, 2d);
+    test_less(1, 2m);
+    test_eq(2, 2m);
 
-    test_less(1.1, 2d);
-    test_eq(Math.sqrt(4), 2d);
+    test_less(1.1, 2m);
+    test_eq(Math.sqrt(4), 2m);
     
-    test_less(2n, 3d);
-    test_eq(3n, 3d);
+    test_less(2n, 3m);
+    test_eq(3n, 3m);
     
-    assert(BigDecimal("1234.1") === 1234.1d);
-    assert(BigDecimal("    1234.1") === 1234.1d);
-    assert(BigDecimal("    1234.1  ") === 1234.1d);
-
-    assert(BigDecimal(0.1) === 0.1d);
-    assert(BigDecimal(123) === 123d);
-    assert(BigDecimal(true) === 1d);
+    assert(BigDecimal("1234.1") === 1234.1m);
+    assert(BigDecimal("    1234.1") === 1234.1m);
+    assert(BigDecimal("    1234.1  ") === 1234.1m);
 
-    assert(123d + 1d === 124d);
-    assert(123d - 1d === 122d);
+    assert(BigDecimal(0.1) === 0.1m);
+    assert(BigDecimal(123) === 123m);
+    assert(BigDecimal(true) === 1m);
 
-    assert(3.2d * 3d === 9.6d);
-    assert(10d / 2d === 5d);
-    assertThrows(RangeError, () => { 10d / 3d } );
-    assert(BigDecimal.div(20d, 3d,
-                       { roundingMode: "half-even",
-                         maximumSignificantDigits: 3 }) === 6.67d);
-    assert(BigDecimal.div(20d, 3d,
-                       { roundingMode: "half-even",
-                         maximumFractionDigits: 3 }) === 6.667d);
+    assert(123m + 1m === 124m);
+    assert(123m - 1m === 122m);
 
-    assert(10d % 3d === 1d);
-    assert(-10d % 3d === -1d);
+    assert(3.2m * 3m === 9.6m);
+    assert(10m / 2m === 5m);
+    assertThrows(RangeError, () => { 10m / 3m } );
 
-    assert(-10d % 3d === -1d);
+    assert(10m % 3m === 1m);
+    assert(-10m % 3m === -1m);
 
-    assert(1234.5d ** 3d === 1881365963.625d);
-    assertThrows(RangeError, () => { 2d ** 3.1d } );
-    assertThrows(RangeError, () => { 2d ** -3d } );
+    assert(1234.5m ** 3m === 1881365963.625m);
+    assertThrows(RangeError, () => { 2m ** 3.1m } );
+    assertThrows(RangeError, () => { 2m ** -3m } );
     
-    assert(BigDecimal.sqrt(2d,
+    assert(BigDecimal.sqrt(2m,
+                       { roundingMode: "half-even",
+                         maximumSignificantDigits: 4 }) === 1.414m);
+    assert(BigDecimal.sqrt(101m,
                        { roundingMode: "half-even",
-                         maximumSignificantDigits: 4 }) === 1.414d);
+                         maximumFractionDigits: 3 }) === 10.050m);
+    assert(BigDecimal.sqrt(0.002m,
+                       { roundingMode: "half-even",
+                         maximumFractionDigits: 3 }) === 0.045m);
     
-    assert(BigDecimal.round(3.14159d,
+    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: 3 }) === 3.142d);
+                         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_bigint1();
diff --git a/tests/test_op_overloading.js b/tests/test_op_overloading.js
new file mode 100644 (file)
index 0000000..d08a85e
--- /dev/null
@@ -0,0 +1,207 @@
+"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();
index f7bf8045eaaf720f85c731c6857eb6da3ec997eb..1483466d2c307ea3c9e1279ae7502f6896303d30 100644 (file)
@@ -66,13 +66,14 @@ function test_integer()
     
     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()
 {
-    var e, a, b, sqrt2;
-    
-    assert(typeof 1 === "bigint");
     assert(typeof 1.0 === "bigfloat");
     assert(1 == 1.0);
     assert(1 !== 1.0);
@@ -101,6 +102,16 @@ function test_modulo()
     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;
@@ -235,6 +246,7 @@ test_integer();
 test_float();
 
 test_modulo();
+test_fraction();
 test_mod();
 test_polynomial();
 test_poly_mod();