Import mpdecimal_4.0.1.orig.tar.gz
authorOndřej Surý <ondrej@debian.org>
Fri, 9 May 2025 11:49:34 +0000 (13:49 +0200)
committerOndřej Surý <ondrej@debian.org>
Fri, 9 May 2025 11:49:34 +0000 (13:49 +0200)
[dgit import orig mpdecimal_4.0.1.orig.tar.gz]

141 files changed:
CHANGELOG.txt [new file with mode: 0644]
COPYRIGHT.txt [new file with mode: 0644]
INSTALL.txt [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README.txt [new file with mode: 0644]
config.guess [new file with mode: 0755]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.ac [new file with mode: 0644]
doc/COPYRIGHT.txt [new file with mode: 0644]
doc/libmpdec++.3 [new file with mode: 0644]
doc/libmpdec.3 [new file with mode: 0644]
doc/mpdecimal.3 [new file with mode: 0644]
install-sh [new file with mode: 0755]
libmpdec++/.objs/README.txt [new file with mode: 0644]
libmpdec++/.pc/libmpdec++.pc.in [new file with mode: 0644]
libmpdec++/.profile/train.sh [new file with mode: 0755]
libmpdec++/Makefile.in [new file with mode: 0644]
libmpdec++/Makefile.vc [new file with mode: 0644]
libmpdec++/bench.cc [new file with mode: 0644]
libmpdec++/bench_full.cc [new file with mode: 0644]
libmpdec++/decimal.cc [new file with mode: 0644]
libmpdec++/decimal.hh [new file with mode: 0644]
libmpdec++/examples/factorial.cc [new file with mode: 0644]
libmpdec++/examples/pi.cc [new file with mode: 0644]
libmpdec/.objs/README.txt [new file with mode: 0644]
libmpdec/.objs/symbols32.exp [new file with mode: 0644]
libmpdec/.objs/symbols64.exp [new file with mode: 0644]
libmpdec/.pc/libmpdec.pc.in [new file with mode: 0644]
libmpdec/.profile/train.sh [new file with mode: 0755]
libmpdec/Makefile.in [new file with mode: 0644]
libmpdec/Makefile.vc [new file with mode: 0644]
libmpdec/README.txt [new file with mode: 0644]
libmpdec/basearith.c [new file with mode: 0644]
libmpdec/basearith.h [new file with mode: 0644]
libmpdec/bench.c [new file with mode: 0644]
libmpdec/bench_full.c [new file with mode: 0644]
libmpdec/bits.h [new file with mode: 0644]
libmpdec/constants.c [new file with mode: 0644]
libmpdec/constants.h [new file with mode: 0644]
libmpdec/context.c [new file with mode: 0644]
libmpdec/convolute.c [new file with mode: 0644]
libmpdec/convolute.h [new file with mode: 0644]
libmpdec/crt.c [new file with mode: 0644]
libmpdec/crt.h [new file with mode: 0644]
libmpdec/difradix2.c [new file with mode: 0644]
libmpdec/difradix2.h [new file with mode: 0644]
libmpdec/examples/README.txt [new file with mode: 0644]
libmpdec/examples/compare.c [new file with mode: 0644]
libmpdec/examples/div.c [new file with mode: 0644]
libmpdec/examples/divmod.c [new file with mode: 0644]
libmpdec/examples/multiply.c [new file with mode: 0644]
libmpdec/examples/pow.c [new file with mode: 0644]
libmpdec/examples/powmod.c [new file with mode: 0644]
libmpdec/examples/shift.c [new file with mode: 0644]
libmpdec/examples/sqrt.c [new file with mode: 0644]
libmpdec/fnt.c [new file with mode: 0644]
libmpdec/fnt.h [new file with mode: 0644]
libmpdec/fourstep.c [new file with mode: 0644]
libmpdec/fourstep.h [new file with mode: 0644]
libmpdec/io.c [new file with mode: 0644]
libmpdec/io.h [new file with mode: 0644]
libmpdec/literature/REFERENCES.txt [new file with mode: 0644]
libmpdec/literature/bignum.txt [new file with mode: 0644]
libmpdec/literature/fnt.py [new file with mode: 0644]
libmpdec/literature/matrix-transform.txt [new file with mode: 0644]
libmpdec/literature/mulmod-64.txt [new file with mode: 0644]
libmpdec/literature/mulmod-ppro.txt [new file with mode: 0644]
libmpdec/literature/six-step.txt [new file with mode: 0644]
libmpdec/literature/umodarith.lisp [new file with mode: 0644]
libmpdec/mpalloc.c [new file with mode: 0644]
libmpdec/mpalloc.h [new file with mode: 0644]
libmpdec/mpdecimal.c [new file with mode: 0644]
libmpdec/mpdecimal.h.in [new file with mode: 0644]
libmpdec/mpdecimal32vc.h [new file with mode: 0644]
libmpdec/mpdecimal64vc.h [new file with mode: 0644]
libmpdec/mpsignal.c [new file with mode: 0644]
libmpdec/numbertheory.c [new file with mode: 0644]
libmpdec/numbertheory.h [new file with mode: 0644]
libmpdec/sixstep.c [new file with mode: 0644]
libmpdec/sixstep.h [new file with mode: 0644]
libmpdec/transpose.c [new file with mode: 0644]
libmpdec/transpose.h [new file with mode: 0644]
libmpdec/typearith.h [new file with mode: 0644]
libmpdec/umodarith.h [new file with mode: 0644]
libmpdec/vcdiv64.asm [new file with mode: 0644]
tests++/Makefile.in [new file with mode: 0644]
tests++/Makefile.vc [new file with mode: 0644]
tests++/README.txt [new file with mode: 0644]
tests++/additional.topTest [new file with mode: 0644]
tests++/apitest.cc [new file with mode: 0644]
tests++/gettests.bat [new file with mode: 0644]
tests++/gettests.sh [new file with mode: 0755]
tests++/official.topTest [new file with mode: 0644]
tests++/runshort.sh [new file with mode: 0755]
tests++/runshort_alloc.sh [new file with mode: 0755]
tests++/runtest.cc [new file with mode: 0644]
tests++/test.cc [new file with mode: 0644]
tests++/test.hh [new file with mode: 0644]
tests++/vctest.hh [new file with mode: 0644]
tests/Makefile.in [new file with mode: 0644]
tests/Makefile.vc [new file with mode: 0644]
tests/README.txt [new file with mode: 0644]
tests/additional.decTest [new file with mode: 0644]
tests/gettests.bat [new file with mode: 0755]
tests/gettests.sh [new file with mode: 0755]
tests/official.decTest [new file with mode: 0644]
tests/runshort.sh [new file with mode: 0755]
tests/runshort_alloc.sh [new file with mode: 0755]
tests/runtest.c [new file with mode: 0644]
tests/test.c [new file with mode: 0644]
tests/test.h [new file with mode: 0644]
tests/testdata_dist/baseconv.decTest [new file with mode: 0644]
tests/testdata_dist/binop_eq.decTest [new file with mode: 0644]
tests/testdata_dist/cov.decTest [new file with mode: 0644]
tests/testdata_dist/divmod.decTest [new file with mode: 0644]
tests/testdata_dist/divmod_eq.decTest [new file with mode: 0644]
tests/testdata_dist/extra.decTest [new file with mode: 0644]
tests/testdata_dist/fma_eq.decTest [new file with mode: 0644]
tests/testdata_dist/format.decTest [new file with mode: 0644]
tests/testdata_dist/getint.decTest [new file with mode: 0644]
tests/testdata_dist/invroot.decTest [new file with mode: 0644]
tests/testdata_dist/largeint.decTest [new file with mode: 0644]
tests/testdata_dist/maxprec.decTest [new file with mode: 0644]
tests/testdata_dist/powmod.decTest [new file with mode: 0644]
tests/testdata_dist/powmod_eq.decTest [new file with mode: 0644]
tests/testdata_dist/shiftlr.decTest [new file with mode: 0644]
tests/testdata_dist/testruntest.decTest [new file with mode: 0644]
tests/vctest.h [new file with mode: 0644]
vcbuild/README.txt [new file with mode: 0644]
vcbuild/pgobuild32.bat [new file with mode: 0755]
vcbuild/pgobuild64.bat [new file with mode: 0755]
vcbuild/runshort.bat [new file with mode: 0755]
vcbuild/runshort_alloc.bat [new file with mode: 0755]
vcbuild/vcbuild32.bat [new file with mode: 0755]
vcbuild/vcbuild64.bat [new file with mode: 0755]
vcbuild/vcbuild_arm32.bat [new file with mode: 0755]
vcbuild/vcbuild_arm64.bat [new file with mode: 0755]
vcbuild/vcclean.bat [new file with mode: 0755]
vcbuild/vcdistclean.bat [new file with mode: 0755]

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644 (file)
index 0000000..de1295d
--- /dev/null
@@ -0,0 +1,502 @@
+
+
+Changelog
+=========
+
+Version 4.0.1
+-------------
+
+build/install
+~~~~~~~~~~~~~
+
+    **features**
+
+    - Add Cygwin support.
+
+    - Update config.guess and config.sub to the latest versions.
+
+    **build fixes**
+
+    - Fix pkg-config files for custom paths.
+
+    - Set LD/LDXX unconditionally to CC/CXX, since LDFLAGS/LDXXFLAGS from
+      ./configure rely on it.
+
+    - macOS: use libdir instead of RPATH for the install_name.
+
+
+Version 4.0.0
+-------------
+
+general
+~~~~~~~
+
+    **sync soversion and major_version**
+
+    - The added number formatting feature requires an ABI change, hence the
+      increase to SOVERSION=4.
+
+    - Packagers outside of the Linux distributions sometimes use the major
+      version number as the equivalent of SOVERSION on their platforms and
+      have an incorrect SOVERSION for 2.5.1, which requires SOVERSION=3.
+
+    - While SOVERSION is not required to match the major version number
+      (example: glibc), mpdecimal will from now on take the path of least
+      resistance and always use SOVERSION=MPD_MAJOR_VERSION.
+
+    - The jump to 4.0.0 should also remind users that a C++ library is
+      available.
+
+build/install
+~~~~~~~~~~~~~
+
+    **features**
+
+    - Support for out-of-tree build.
+
+    - Support for pkg-config.
+
+    - Unix: support for Loongson.
+
+    - Unix: support for CheriBSD.
+
+    - Compilers: support for icx, icpx, ibm-clang_r, ibm-clang++_r, CompCert,
+      clang-cl and emscripten.
+
+    - Windows: support for MSYS2/MinGW.
+
+    - MSVC: the build now uses /O2 /DNDEBUG.
+
+    - MSVC: new arm64/arm32 cross build scripts.
+
+    - AIX: the shared libraries are now installed as versioned objects, e.g.,
+      shr4.o, shr4_64.o.
+
+    - New ``./configure`` switches:
+
+      ``--enable-static``: enable/disable the build of the static libraries
+      (default: enabled).
+
+      ``--enable-pc``: enable/disable the install of the pkgconfig files
+      (default: enabled).
+
+      ``--enable-doc``: enable/disable the install of the documentation
+      (default: enabled).
+
+    - New ``./configure`` behavior:
+
+      On multilib platforms like AIX that default to 32-bit the 64-bit build can
+      be forced with a single ``./configure MACHINE=uint128`` (ibm-clang, gcc)
+      or ``./configure MACHINE=ansi64`` (xlc) rather than explicitly setting
+      ``CFLAGS``, ``LDFLAGS``, ``CXXFLAGS`` and ``LDXXFLAGS``.
+
+      Note that on most other (multilib) platforms the default is 64-bit and the
+      optimal configuration is chosen by just using ``./configure``.
+
+    - New man pages direct users to the mpdecimal-doc package or the online
+      HTML documentation.
+
+    **tests**
+
+    - The C++ tests now automatically skip a small number of bignum tests if
+      the std::thread stack size (which cannot be altered) is less than 512K.
+
+      For all other tests a thread stack size of roughly 50K is sufficient.
+
+    - Files in the tests/testdata_dist directory have been significantly
+      abbreviated in order to facilitate testing on slower platforms. The
+      tests take 5s on a modern x86_64 platform.
+
+      The previous tests have been moved to the separate mpdecimal-testdata
+      package (replace tests/testdata_dist with the renamed mpdecimal-testdata
+      directory).
+
+    **toolchain issues**
+
+    - AIX: The preferred C/C++ compilers are ibm-clang_r and ibm-clang_r++.
+      g++ has sporadic C++11 threading issues that only show up in long running
+      programs (or short ones with a sufficient number of repetitions):
+
+         https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98390
+
+      If you only use libmpdec (``--disable-cxx``), gcc and xlc are very stable.
+
+    **changed**
+
+    - The ``--enable-profile`` configure option has been removed due to fragile
+      integration with a sequence like ``make && make install``. The new method
+      is ``make profile && make install``.
+
+    **removed**
+
+    - The prebuilt HTML documentation is now in the separate mpdecimal-doc
+      package, which gives distributions that reject prebuilt documentation
+      the option to disregard it and use the new man pages.
+
+libmpdec
+~~~~~~~~
+
+    **features**
+
+    - Add the "z" format specifier (coerce negative zeros to positive).
+
+    - In extremely rare cases the transcendental functions (exp, ln, log10) did
+      not set the Subnormal/Underflow flags. The reason is that in the case of
+      an exponent boundary the Ziv correction loop for correct rounding requires
+      very few iterations to arrive at the correctly rounded result, but may
+      need many more iterations to arrive at the correct flags.
+
+      In these cases, Subnormal/Underflow is not very informative, so the status
+      quo was to skip the extra iterations.
+
+      Version 4.0.0 now specializes exponent boundary cases and uses up to five
+      additional iterations to set Subnormal/Underflow. The refactored code has
+      no speed penalty on average;  in fact, in the deccheck tests (random tests
+      with a bias towards corner cases) it is slightly faster.
+
+      No cases have been found where more than two additional iterations are
+      required, but they may exist.
+
+    **reliability fixes**
+
+    - mpd_qset_string_exact(), mpd_qset_i64_exact() and mpd_qset_u64_exact()
+      can now be called with a nonzero status.  Previously, the functions
+      could return NaN/Invalid_operation in that case.
+
+      This is listed under "reliability fixes" since there is no possible
+      scenario under which these functions would legitimately be called with
+      a nonzero status.
+
+libmpdec++
+~~~~~~~~~~
+
+    **features**
+
+    - Add input validation for Decimal.shiftl(), Decimal.shiftr() and
+      Decimal::ln10().
+
+
+Version 2.5.1
+-------------
+
+libmpdec
+~~~~~~~~
+
+    **features**
+
+    - New functions for conversion between mpd_t and a decimal triple
+      with a split uint128_t coefficient.
+
+    - All symbols in the header files now have an MPD_* or mpd_* prefix.
+
+    - libmpdec/.objs/symbols64.exp and libmpdec/.objs/symbols32.exp contain
+      all symbols of the exported API.  The files are almost identical, but
+      mpd_qsset_i64, mpd_sset_i64, mpd_qsset_u64 and mpd_sset_u64 are only
+      available in the 64-bit build.
+
+      libmpdec/.objs/libmpdec.imp is the unified 64/32-bit import file
+      for AIX.
+
+    - The new test target *make check_local* does not attempt to download
+      dectest.zip.  This is useful for packagers whose infrastructure does
+      not allow downloading during the testing phase.
+
+      *make check* is still preferable because it runs all tests.
+
+    **style fixes**
+
+    - Apply several clang-tidy suggestions (high diagnostic level).
+
+    **build features**
+
+    - AIX: full support for xlc/gcc builds in ./configure.
+    - full support for AIX-style shared libraries.
+    - full support for AIX multilib header.
+
+    **build fixes**
+
+    - Update config.guess and config.sub in order to support IBM Power
+      architectures.
+
+    - Fix false positive _FORTIFY_SOURCE warning on Fedora.
+
+libmpdec++
+~~~~~~~~~~
+
+    **features**
+
+    - New methods for conversion between Decimal and a decimal triple
+      with a split uint128_t coefficient.
+
+    - decimal::Context now uses default constructors wherever possible.
+
+    - AIX: The tests now have a --pthread option. The default thread stack
+      size on AIX (96K) makes std::thread unusable for runtest.cc, which needs
+      around 300K.
+
+    - Windows: The DLL can now be built. Since DLLs do not support the
+      C++11 extern thread_local context, each translation unit gets its
+      own static reference to the thread_local context.  This should
+      preserve the semantics at the expense of clarity.
+
+      Note that this scheme is specific to the DLL build; the static
+      library build on Windows as well as all other operating systems
+      (except for OpenBSD and Solaris) use the C++11 extern thread_local
+      context.
+
+    - OpenBSD, Solaris: The shared library now works using the above scheme.
+      For simplicity, both the shared and static libraries use the workaround.
+
+    **style fixes**
+
+    - Apply several clang-tidy suggestions (high diagnostic level).
+
+    **bug fixes**
+
+    - DecimalException now inherits from std::runtime_error instead
+      of std::exception.  This automatically provides a nothrow copy
+      constructor.
+
+    - Use mpd_free() instead of free(). This only affects applications using
+      the libmpdec custom allocators.
+
+    **build fixes**
+
+    - Undefine some additional math.h macros for non-compliant C++11
+      implementations.
+
+    - libmpdec++ is now linked against libmpdec.
+
+
+Version 2.5.0
+-------------
+
+    **New: libmpdec++**
+
+       libmpdec++ is a new C++ library around libmpdec. It frees users from
+       manual memory management and allows cleaner code using inline operators.
+
+       libmpdec++ has a thread local context, so inline operators work
+       seamlessly with threaded code.
+
+    **features**
+
+    1) New functions *mpd_qset_string_exact*, *mpd_qset_i64_exact* and
+       *mpd_qset_u64_exact*.
+
+    2) For very large precisions like *MPD_MAX_PREC* libmpdec failed with
+       *MPD_Malloc_error* even when the result was exact and required far
+       fewer digits.
+
+       Now libmpdec retries the operation with the smallest possible
+       precision for exact results.
+
+    3) ./configure now has the *--enable-profile* option for easier profile
+       guided optimization builds.  Using PGO, recent compilers produce
+       better optimized libraries with speedups in the order of 20%.
+
+    4) C++ use: All headers now assume at least C++11, so a couple of hacks
+       have been removed from the headers.  C++ compatibility is fully
+       tested in libmpdec++.
+
+    **behavior changes**
+
+    1) The functions *mpd_ceil*, *mpd_floor* and *mpd_trunc* now set
+       *MPD_Invalid_operation* when the input is *NaN* or *Infinity*.
+
+       Previously they returned special values unchanged. That behavior was
+       inspired by the similar *to_integral* function from the specification,
+       but does not seem useful for the above functions.
+
+    **build fixes**
+
+    1) OS X: Linking has been completely reworked and now uses .dylib.
+
+    2) Windows: vcstdint.h has been removed, Windows now supports stdint.h.
+
+    **bug fixes**
+
+    1) A couple of quiet functions have been made resilient against being
+       called with a "dirty" status. Note that it is not recommended in
+       general to call quiet functions with any status other than 0.
+
+    2) In runtest.c, fma_eq.decTest and powmod_eq.decTest operands were
+       accidentally reversed for the *op_eq_eq* tests.
+
+       Since both the code and the tests assumed reversed operands, the tests
+       were correct.  However, for readability this situation has been fixed.
+
+
+Version 2.4.2
+-------------
+
+    **build fixes**
+
+    1) ICC/Windows: the optimized x86 build requires -fp-model=precise.
+
+    2) OS X: the linker requires -dynamiclib and -install_name for building
+       a shared library.
+
+
+Version 2.4.1
+-------------
+
+    **build fixes**
+
+    1) The __uint128_t detection in ./configure has been fixed .  Failure to
+       detect the option resulted in building the significantly slower ANSI
+       target on non-x86/amd64 platforms.
+
+    2) Use -fPIC instead of -fpic to fix a build failure on SPARC platforms.
+
+    3) Split the tests into a faster "make check" and a slower "make check_alloc".
+       The latter tests allocation failures but is too slow on older machines.
+
+    4) Generate detailed test output for better feedback on slower machines.
+
+    5) The static library is now built without -fPIC, which is significantly
+       faster at least on x86.  Both the static and the shared library are
+       now tested separately.
+
+    **bug fixes**
+
+    1) PEP 3101 formatting:  With the '%' format type, a trailing percent sign
+       is now also added for infinities and NaNs.
+
+
+Version 2.4.0
+-------------
+
+    **features**
+
+    1) Faster integer to string conversion.
+
+    2) mpd_qln(), mpd_qlog10() and mpd_pow() are now thread-safe.
+
+    3) All functions that take or return C integers are now available in
+       both the 64-bit and the 32-bit builds.
+
+    4) Support for cross-compiling.
+
+    5) Scripts for Visual Studio builds.
+
+    **code improvements**
+
+    1) This version is exactly the same as the version shipped with Python-3.3+.
+       Large portions of the code have been refactored in order to facilitate
+       proofs.  Many ACL2 proofs have been added.
+
+    **removed**
+
+    1) The Python module has been removed from mpdecimal, since both libmpdec
+       and cdecimal are included in Python-3.3+.
+
+    2) The large test suite against decNumber as well as the multi-precision
+       tests against gmp have been removed, but will be available in a separate
+       package.  Naturally these tests are still run as part of the release
+       process.
+
+
+Version 2.3
+-----------
+
+    **features**
+
+    1) New test suite with comprehensive tests against decNumber.
+
+    2) Full support for compilers without uint64_t (tested with CompCert).
+
+    **bug fixes**
+
+    1) If ROUND_FLOOR is set and the operand is zero, the functions
+       mpd_plus() and mpd_minus() have special cases for the sign of
+       the result.
+
+
+Version 2.2
+-----------
+
+    **build process**
+
+    1) configure: append CFLAGS to CONFIG flags.
+
+    2) Makefile: use includedir, libdir, datarootdir, datadir, docdir, DESTDIR.
+
+    **workarounds for toolchain bugs**
+
+    1) Enable workaround for a gcc miscompilation. See:
+
+           `http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491>`_
+
+    2) Enable workaround for the glibc _FORTIFY_SOURCE/memmove bug, which is
+       exposed by gcc-4.6. See:
+
+           `http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html <http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html>`_
+
+    **features**
+
+    1) Make PPRO inline assembly PIC-compliant (for the dynamic library).
+
+
+Version 2.1
+-----------
+
+Version 2.1 was never released, but escaped into the wild via the Makefile
+and setup.py in cdecimal-1.97-rc2.tar.gz. Both files already had that version
+number.
+
+    **features**
+
+    1) Code coverage increased to 100%. This includes every possible
+       allocation failure.
+
+    2) Switch build process to ./configure.
+
+    3) Makefile targets for creating coverage reports.
+
+    **bug fixes**
+
+    1) mpd_qget_uint, mpd_qget_u64, mpd_qget_u32 did not raise for
+       negative input.
+
+    2) Handle allocation failures in _mpd_fntmul under extreme conditions.
+
+
+
+Version 1.2.1
+-------------
+
+    **bug fixes**
+
+    1) With MACHINE=ansi64, the macros BSR and BSF used x86 assembly.
+       This caused compilation to fail on non-x64 platforms.
+
+
+Version 1.2
+-----------
+
+    **features**
+
+    1) Support for compilers with __uint128_t
+       (option MACHINE=ansi64).
+
+    2) Support for other 64-bit compilers
+       (option MACHINE=ansi64c32).
+
+    3) Support for legacy compilers without uint64_t
+       (option MACHINE=ansi-legacy).
+
+    4) Slightly different build process (please read INSTALL.txt).
+
+    5) If clamp=1, the maximum payload length of a NaN is prec-1.
+
+    **bug fixes**
+
+    1) Fix for mpd_qround_to_int, which did not handle digits
+       exceeding the context precision correctly in all cases.
+
+    2) In rare corner cases Underflow was not set in
+       transcendental functions.
+
+
+
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..fc34843
--- /dev/null
@@ -0,0 +1,23 @@
+Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644 (file)
index 0000000..ca18bf7
--- /dev/null
@@ -0,0 +1,95 @@
+
+# ==============================================================================
+#                       Unix: library install instructions
+# ==============================================================================
+
+# NONGNU: gmake required!
+
+# This automatically selects the optimal build on all architectures except
+# for multilib systems that default to 32-bit builds (AIX, Solaris):
+./configure
+make
+
+# The default tests suite attempts to download the official tests cases:
+make check
+
+# Alternatively, if wget is not installed or no network is available, run:
+make check_local
+
+# Install:
+make install
+
+# On some systems it is necessary to run ``ldconfig'', so the newly installed
+# library is found by the system linker.  The Makefile does not run `ldconfig''
+# because it is not portable:
+ldconfig
+
+
+# ==============================================================================
+#                                  Custom build
+# ==============================================================================
+
+#
+# MACHINE variable:
+#
+# If ./configure fails to detect the optimal configuration, a specific
+# configuration can be enforced by providing the MACHINE variable.  This
+# should only be necessary on AIX, Solaris or for a MacOS universal build.
+#
+# Example:
+#
+#   ./configure MACHINE=x64
+#
+# Possible values (in decreasing order of preference):
+#
+#    1. x64         - 64-bit OS with x86_64 processor (AMD, Intel)
+#
+#    2. uint128     - 64-bit OS, compiler provides __uint128_t (gcc)
+#
+#    3. ansi64      - 64-bit OS, ANSI C
+#
+#    4. ppro        - 32-bit OS, x86 CPU, PentiumPro or later
+#
+#    5. ansi32      - 32-bit OS, ANSI C
+#
+#    6. ansi-legacy - 32-bit OS, compiler without uint64_t
+#
+# Multilib builds (Darwin and AIX):
+#
+#    7. universal   - builds a 64-bit or a 32-bit library according to the
+#                     compiler ABI settings:
+#
+#                        Darwin:    -m64 or -m32
+#                        AIX (gcc): -maix64 or -maix32
+#                        AIX (xlc): -q64 or -q32
+#
+#                     The generated header file is suitable for both 64-bit
+#                     and 32-bit installs.
+#
+
+#
+# CFLAGS, LDFLAGS, CXXFLAGS, LDXXFLAGS:
+#
+# If CFLAGS or LDFLAGS (or the C++ counterparts) are passed to ./configure,
+# they are appended to the minimal libmpdec (or libmpdec++) configuration:
+#
+#   ./configure CFLAGS="-m32 -march=i586 -O3" LDFLAGS="-m32"
+#
+#      ==> -DCONFIG_32 -DPPRO -DASM -O2 -fpic -m32 -march=i586 -O3
+#
+# Both MACHINE and CFLAGS can be specified, making it possible to have a
+# complete custom configuration:
+#
+#   ./configure MACHINE=ansi32 CFLAGS="-Wall -W -O3 -g"
+#
+#      ==> -DCONFIG_32 -DANSI -Wall -W -O3 -g
+#
+
+# ======================================================================
+#                   Windows: library install instructions
+# ======================================================================
+
+Build scripts for Visual Studio are in the vcbuild directory.
+
+
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..cb5f13c
--- /dev/null
@@ -0,0 +1,161 @@
+
+# ==============================================================================
+#                      Unix Makefile for libmpdec/libmpdec++
+# ==============================================================================
+
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+INSTALL = @INSTALL@
+
+ENABLE_CXX = @ENABLE_CXX@
+ENABLE_STATIC = @ENABLE_STATIC@
+ENABLE_SHARED = @ENABLE_SHARED@
+ENABLE_PC = @ENABLE_PC@
+ENABLE_DOC = @ENABLE_DOC@
+ENABLE_MINGW = @ENABLE_MINGW@
+PROFILE =
+
+LIBSTATIC = @LIBSTATIC@
+LIBNAME = @LIBNAME@
+LIBSONAME = @LIBSONAME@
+LIBSHARED = @LIBSHARED@
+LIBIMPORT = @LIBIMPORT@
+
+LIBSTATIC_CXX = @LIBSTATIC_CXX@
+LIBNAME_CXX = @LIBNAME_CXX@
+LIBSONAME_CXX = @LIBSONAME_CXX@
+LIBSHARED_CXX = @LIBSHARED_CXX@
+LIBIMPORT_CXX = @LIBIMPORT_CXX@
+
+LIBSHARED_USE_AR = @LIBSHARED_USE_AR@
+
+srcdir = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+includedir = @includedir@
+libdir = @libdir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+mandir = @mandir@
+
+
+ifeq ($(ENABLE_CXX), yes)
+default: libcxx
+
+check:
+       cd libmpdec && $(MAKE) check
+       cd libmpdec++ && $(MAKE) check
+
+check_local:
+       cd libmpdec && $(MAKE) check_local
+       cd libmpdec++ && $(MAKE) check_local
+
+check_alloc:
+       cd libmpdec && $(MAKE) check_alloc
+       cd libmpdec++ && $(MAKE) check_alloc
+else
+default: lib
+
+check:
+       cd libmpdec && $(MAKE) check
+
+check_local:
+       cd libmpdec && $(MAKE) check_local
+
+check_alloc:
+       cd libmpdec && $(MAKE) check_alloc
+endif
+
+
+lib:
+       cd libmpdec && $(MAKE) $(PROFILE)
+
+libcxx: lib
+       cd libmpdec++ && $(MAKE) $(PROFILE)
+
+
+install: install_dirs install_files
+
+install_dirs: default
+ifeq ($(ENABLE_MINGW), yes)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
+endif
+       $(INSTALL) -d -m 755 $(DESTDIR)$(includedir)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(libdir)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(docdir)
+ifeq ($(ENABLE_PC), yes)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(libdir)/pkgconfig
+endif
+ifeq ($(ENABLE_DOC), yes)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(mandir)/man3
+endif
+
+install_files: install_dirs
+       $(INSTALL) -m 644 libmpdec/mpdecimal.h $(DESTDIR)$(includedir)
+ifeq ($(ENABLE_STATIC), yes)
+       $(INSTALL) -m 644 libmpdec/$(LIBSTATIC) $(DESTDIR)$(libdir)
+endif
+ifeq ($(ENABLE_SHARED), yes)
+ifeq ($(ENABLE_MINGW), yes)
+       $(INSTALL) -m 644 libmpdec/$(LIBIMPORT) $(DESTDIR)$(libdir)
+       $(INSTALL) -m 755 libmpdec/$(LIBSHARED) $(DESTDIR)$(bindir)
+else
+ifeq ($(LIBSHARED_USE_AR), no)
+       $(INSTALL) -m 755 libmpdec/$(LIBSHARED) $(DESTDIR)$(libdir)
+       cd $(DESTDIR)$(libdir) && ln -sf $(LIBSHARED) $(LIBSONAME) && ln -sf $(LIBSHARED) $(LIBNAME)
+endif
+endif
+endif
+
+ifeq ($(ENABLE_CXX), yes)
+       $(INSTALL) -m 644 libmpdec++/decimal.hh $(DESTDIR)$(includedir)
+ifeq ($(ENABLE_STATIC), yes)
+       $(INSTALL) -m 644 libmpdec++/$(LIBSTATIC_CXX) $(DESTDIR)$(libdir)
+endif
+ifeq ($(ENABLE_SHARED), yes)
+ifeq ($(ENABLE_MINGW), yes)
+       $(INSTALL) -m 644 libmpdec++/$(LIBIMPORT_CXX) $(DESTDIR)$(libdir)
+       $(INSTALL) -m 755 libmpdec++/$(LIBSHARED_CXX) $(DESTDIR)$(bindir)
+else
+ifeq ($(LIBSHARED_USE_AR), no)
+       $(INSTALL) -m 755 libmpdec++/$(LIBSHARED_CXX) $(DESTDIR)$(libdir)
+       cd $(DESTDIR)$(libdir) && ln -sf $(LIBSHARED_CXX) $(LIBSONAME_CXX) && ln -sf $(LIBSHARED_CXX) $(LIBNAME_CXX)
+endif
+endif
+endif
+endif
+
+ifeq ($(ENABLE_PC), yes)
+       $(INSTALL) -m 644 libmpdec/.pc/libmpdec.pc $(DESTDIR)$(libdir)/pkgconfig
+ifeq ($(ENABLE_CXX), yes)
+       $(INSTALL) -m 644 libmpdec++/.pc/libmpdec++.pc $(DESTDIR)$(libdir)/pkgconfig
+endif
+endif
+
+       $(INSTALL) -m 644 doc/COPYRIGHT.txt $(DESTDIR)$(docdir)
+ifeq ($(ENABLE_DOC), yes)
+       $(INSTALL) -m 644 doc/mpdecimal.3 $(DESTDIR)$(mandir)/man3
+       $(INSTALL) -m 644 doc/libmpdec.3 $(DESTDIR)$(mandir)/man3
+ifeq ($(ENABLE_CXX), yes)
+       $(INSTALL) -m 644 doc/libmpdec++.3 $(DESTDIR)$(mandir)/man3
+endif
+endif
+
+
+profile: PROFILE := profile
+profile: default
+
+
+clean:
+       cd libmpdec && if [ -f Makefile ]; then $(MAKE) clean; else exit 0; fi
+       cd libmpdec++ && if [ -f Makefile ]; then $(MAKE) clean; else exit 0; fi
+       cd tests && if [ -f Makefile ]; then $(MAKE) clean; else exit 0; fi
+       cd tests++ && if [ -f Makefile ]; then $(MAKE) clean; else exit 0; fi
+
+distclean:
+       cd libmpdec && if [ -f Makefile ]; then $(MAKE) distclean; else exit 0; fi
+       cd libmpdec++ && if [ -f Makefile ]; then $(MAKE) distclean; else exit 0; fi
+       cd tests && if [ -f Makefile ]; then $(MAKE) distclean; else exit 0; fi
+       cd tests++ && if [ -f Makefile ]; then $(MAKE) distclean; else exit 0; fi
+       rm -f config.h config.log config.status Makefile
+       rm -rf autom4te.cache
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..3f7fc72
--- /dev/null
@@ -0,0 +1,53 @@
+
+
+libmpdec
+========
+
+libmpdec is a fast C/C++ library for correctly-rounded arbitrary precision
+decimal floating point arithmetic.  It is a complete implementation of
+Mike Cowlishaw/IBM's General Decimal Arithmetic Specification. The full
+specification is available here:
+
+http://speleotrove.com/decimal/
+
+
+libmpdec will - with minor restrictions - also conform to the IEEE 754-2008
+Standard for Floating-Point Arithmetic, provided that the appropriate context
+parameters are set.
+
+libmpdec is the basis for the decimal module in Python-3.3.
+
+
+The library has been tested on the following platforms:
+
+   amd64:  Linux, FreeBSD, OpenBSD, OpenSolaris, Windows
+
+   ppc64:  AIX
+
+   x86:    Linux, FreeBSD, OpenBSD, OpenSolaris, Windows
+
+   mips32: Debian
+
+
+libmpdec++
+==========
+
+libmpdec++ is a complete implementation of the General Decimal Arithmetic
+Specification.  libmpdec++ is mostly a header library around libmpdec's
+C functions.
+
+The library frees users from manual memory management and has an easy API
+with inline operators similar to the one in Python's decimal module. Like
+Python's decimal module, libmpdec++ has a thread local context for inline
+operators and other functions that use the implicit context.
+
+In benchmarks the speed is close to libmpdec (about 4% slower due to the
+copying, destructor overhead and the thread local context).
+
+libmpdec++ has been tested on 64/32-bit Linux, 64/32-bit FreeBSD and OpenBSD
+and 64/32-bit Windows.
+
+
+Contact: Stefan Krah <skrah@bytereef.org>
+
+
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..48a6846
--- /dev/null
@@ -0,0 +1,1815 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2024-07-27'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system '$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2024 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
+# use 'HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+       { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+       { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+       { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+       ,,)    echo "int x;" > "$dummy.c"
+              for driver in cc gcc c17 c99 c89 ; do
+                  if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+                      CC_FOR_BUILD=$driver
+                      break
+                  fi
+              done
+              if test x"$CC_FOR_BUILD" = x ; then
+                  CC_FOR_BUILD=no_compiler_found
+              fi
+              ;;
+       ,,*)   CC_FOR_BUILD=$CC ;;
+       ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+       LIBC=unknown
+
+       set_cc_for_build
+       cat <<-EOF > "$dummy.c"
+       #if defined(__ANDROID__)
+       LIBC=android
+       #else
+       #include <features.h>
+       #if defined(__UCLIBC__)
+       LIBC=uclibc
+       #elif defined(__dietlibc__)
+       LIBC=dietlibc
+       #elif defined(__GLIBC__)
+       LIBC=gnu
+       #elif defined(__LLVM_LIBC__)
+       LIBC=llvm
+       #else
+       #include <stdarg.h>
+       /* First heuristic to detect musl libc.  */
+       #ifdef __DEFINED_va_list
+       LIBC=musl
+       #endif
+       #endif
+       #endif
+       EOF
+       cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+       eval "$cc_set_libc"
+
+       # Second heuristic to detect musl libc.
+       if [ "$LIBC" = unknown ] &&
+          command -v ldd >/dev/null &&
+          ldd --version 2>&1 | grep -q ^musl; then
+               LIBC=musl
+       fi
+
+       # If the system lacks a compiler, then just pick glibc.
+       # We could probably try harder.
+       if [ "$LIBC" = unknown ]; then
+               LIBC=gnu
+       fi
+       ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+           /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+           /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+           echo unknown)`
+       case $UNAME_MACHINE_ARCH in
+           aarch64eb) machine=aarch64_be-unknown ;;
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           earmv*)
+               arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+               endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+               machine=${arch}${endian}-unknown
+               ;;
+           *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently (or will in the future) and ABI.
+       case $UNAME_MACHINE_ARCH in
+           earm*)
+               os=netbsdelf
+               ;;
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep -q __ELF__
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # Determine ABI tags.
+       case $UNAME_MACHINE_ARCH in
+           earm*)
+               expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+               abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case $UNAME_VERSION in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       GUESS=$machine-${os}${release}${abi-}
+       ;;
+    *:Bitrig:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+       ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+       ;;
+    *:SecBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+       ;;
+    *:LibertyBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+       ;;
+    *:MidnightBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+       ;;
+    *:ekkoBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+       ;;
+    *:SolidBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+       ;;
+    *:OS108:*:*)
+       GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+       ;;
+    macppc:MirBSD:*:*)
+       GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+       ;;
+    *:MirBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+       ;;
+    *:Sortix:*:*)
+       GUESS=$UNAME_MACHINE-unknown-sortix
+       ;;
+    *:Twizzler:*:*)
+       GUESS=$UNAME_MACHINE-unknown-twizzler
+       ;;
+    *:Redox:*:*)
+       GUESS=$UNAME_MACHINE-unknown-redox
+       ;;
+    mips:OSF1:*.*)
+       GUESS=mips-dec-osf1
+       ;;
+    alpha:OSF1:*:*)
+       # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+       trap '' 0
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case $ALPHA_CPU_TYPE in
+           "EV4 (21064)")
+               UNAME_MACHINE=alpha ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE=alpha ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE=alpha ;;
+           "EV5 (21164)")
+               UNAME_MACHINE=alphaev5 ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE=alphaev56 ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE=alphapca56 ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE=alphapca57 ;;
+           "EV6 (21264)")
+               UNAME_MACHINE=alphaev6 ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE=alphaev67 ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE=alphaev69 ;;
+           "EV7 (21364)")
+               UNAME_MACHINE=alphaev7 ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE=alphaev79 ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+       GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+       ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       GUESS=m68k-unknown-sysv4
+       ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       GUESS=$UNAME_MACHINE-unknown-amigaos
+       ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       GUESS=$UNAME_MACHINE-unknown-morphos
+       ;;
+    *:OS/390:*:*)
+       GUESS=i370-ibm-openedition
+       ;;
+    *:z/VM:*:*)
+       GUESS=s390-ibm-zvmoe
+       ;;
+    *:OS400:*:*)
+       GUESS=powerpc-ibm-os400
+       ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       GUESS=arm-acorn-riscix$UNAME_RELEASE
+       ;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+       GUESS=arm-unknown-riscos
+       ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       GUESS=hppa1.1-hitachi-hiuxmpp
+       ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       case `(/bin/universe) 2>/dev/null` in
+           att) GUESS=pyramid-pyramid-sysv3 ;;
+           *)   GUESS=pyramid-pyramid-bsd   ;;
+       esac
+       ;;
+    NILE*:*:*:dcosx)
+       GUESS=pyramid-pyramid-svr4
+       ;;
+    DRS?6000:unix:4.0:6*)
+       GUESS=sparc-icl-nx6
+       ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) GUESS=sparc-icl-nx7 ;;
+       esac
+       ;;
+    s390x:SunOS:*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+       ;;
+    sun4H:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-hal-solaris2$SUN_REL
+       ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-sun-solaris2$SUN_REL
+       ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+       GUESS=i386-pc-auroraux$UNAME_RELEASE
+       ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       set_cc_for_build
+       SUN_ARCH=i386
+       # If there is a compiler, see if it is configured for 64-bit objects.
+       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+       # This test works for both compilers.
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+               (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+               grep IS_64BIT_ARCH >/dev/null
+           then
+               SUN_ARCH=x86_64
+           fi
+       fi
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+       ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-sun-solaris3$SUN_REL
+       ;;
+    sun4*:SunOS:*:*)
+       case `/usr/bin/arch -k` in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like '4.1.3-JL'.
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+       GUESS=sparc-sun-sunos$SUN_REL
+       ;;
+    sun3*:SunOS:*:*)
+       GUESS=m68k-sun-sunos$UNAME_RELEASE
+       ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+       case `/bin/arch` in
+           sun3)
+               GUESS=m68k-sun-sunos$UNAME_RELEASE
+               ;;
+           sun4)
+               GUESS=sparc-sun-sunos$UNAME_RELEASE
+               ;;
+       esac
+       ;;
+    aushp:SunOS:*:*)
+       GUESS=sparc-auspex-sunos$UNAME_RELEASE
+       ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+       GUESS=m68k-milan-mint$UNAME_RELEASE
+       ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+       GUESS=m68k-hades-mint$UNAME_RELEASE
+       ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+       GUESS=m68k-unknown-mint$UNAME_RELEASE
+       ;;
+    m68k:machten:*:*)
+       GUESS=m68k-apple-machten$UNAME_RELEASE
+       ;;
+    powerpc:machten:*:*)
+       GUESS=powerpc-apple-machten$UNAME_RELEASE
+       ;;
+    RISC*:Mach:*:*)
+       GUESS=mips-dec-mach_bsd4.3
+       ;;
+    RISC*:ULTRIX:*:*)
+       GUESS=mips-dec-ultrix$UNAME_RELEASE
+       ;;
+    VAX*:ULTRIX*:*:*)
+       GUESS=vax-dec-ultrix$UNAME_RELEASE
+       ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       GUESS=clipper-intergraph-clix$UNAME_RELEASE
+       ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       set_cc_for_build
+       sed 's/^        //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+         dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       GUESS=mips-mips-riscos$UNAME_RELEASE
+       ;;
+    Motorola:PowerMAX_OS:*:*)
+       GUESS=powerpc-motorola-powermax
+       ;;
+    Motorola:*:4.3:PL8-*)
+       GUESS=powerpc-harris-powermax
+       ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       GUESS=powerpc-harris-powermax
+       ;;
+    Night_Hawk:Power_UNIX:*:*)
+       GUESS=powerpc-harris-powerunix
+       ;;
+    m88k:CX/UX:7*:*)
+       GUESS=m88k-harris-cxux7
+       ;;
+    m88k:*:4*:R4*)
+       GUESS=m88k-motorola-sysv4
+       ;;
+    m88k:*:3*:R3*)
+       GUESS=m88k-motorola-sysv3
+       ;;
+    AViiON:dgux:*:*)
+       # DG/UX returns AViiON for all architectures
+       UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+       then
+           if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+              test "$TARGET_BINARY_INTERFACE"x = x
+           then
+               GUESS=m88k-dg-dgux$UNAME_RELEASE
+           else
+               GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+           fi
+       else
+           GUESS=i586-dg-dgux$UNAME_RELEASE
+       fi
+       ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       GUESS=m88k-dolphin-sysv3
+       ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       GUESS=m88k-motorola-sysv3
+       ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       GUESS=m88k-tektronix-sysv3
+       ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       GUESS=m68k-tektronix-bsd
+       ;;
+    *:IRIX*:*:*)
+       IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+       GUESS=mips-sgi-irix$IRIX_REL
+       ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+       ;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       GUESS=i386-ibm-aix
+       ;;
+    ia64:AIX:*:*)
+       if test -x /usr/bin/oslevel ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+       fi
+       GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+       ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               set_cc_for_build
+               sed 's/^                //' << EOF > "$dummy.c"
+               #include <sys/systemcfg.h>
+
+               int
+               main ()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+               then
+                       GUESS=$SYSTEM_NAME
+               else
+                       GUESS=rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               GUESS=rs6000-ibm-aix3.2.4
+       else
+               GUESS=rs6000-ibm-aix3.2
+       fi
+       ;;
+    *:AIX:*:[4567])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if test -x /usr/bin/lslpp ; then
+               IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+                          awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+       else
+               IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+       fi
+       GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+       ;;
+    *:AIX:*:*)
+       GUESS=rs6000-ibm-aix
+       ;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+       GUESS=romp-ibm-bsd4.4
+       ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+       ;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       GUESS=rs6000-bull-bosx
+       ;;
+    DPX/2?00:B.O.S.:*:*)
+       GUESS=m68k-bull-sysv3
+       ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       GUESS=m68k-hp-bsd
+       ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       GUESS=m68k-hp-bsd4.4
+       ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+       case $UNAME_MACHINE in
+           9000/31?)            HP_ARCH=m68000 ;;
+           9000/[34]??)         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if test -x /usr/bin/getconf; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                   sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                   case $sc_cpu_version in
+                     523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+                     528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+                     532)                      # CPU_PA_RISC2_0
+                       case $sc_kernel_bits in
+                         32) HP_ARCH=hppa2.0n ;;
+                         64) HP_ARCH=hppa2.0w ;;
+                         '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+                       esac ;;
+                   esac
+               fi
+               if test "$HP_ARCH" = ""; then
+                   set_cc_for_build
+                   sed 's/^            //' << EOF > "$dummy.c"
+
+               #define _HPUX_SOURCE
+               #include <stdlib.h>
+               #include <unistd.h>
+
+               int
+               main ()
+               {
+               #if defined(_SC_KERNEL_BITS)
+                   long bits = sysconf(_SC_KERNEL_BITS);
+               #endif
+                   long cpu  = sysconf (_SC_CPU_VERSION);
+
+                   switch (cpu)
+                       {
+                       case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+                       case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+                       case CPU_PA_RISC2_0:
+               #if defined(_SC_KERNEL_BITS)
+                           switch (bits)
+                               {
+                               case 64: puts ("hppa2.0w"); break;
+                               case 32: puts ("hppa2.0n"); break;
+                               default: puts ("hppa2.0"); break;
+                               } break;
+               #else  /* !defined(_SC_KERNEL_BITS) */
+                           puts ("hppa2.0"); break;
+               #endif
+                       default: puts ("hppa1.0"); break;
+                       }
+                   exit (0);
+               }
+EOF
+                   (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if test "$HP_ARCH" = hppa2.0w
+       then
+           set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep -q __LP64__
+           then
+               HP_ARCH=hppa2.0w
+           else
+               HP_ARCH=hppa64
+           fi
+       fi
+       GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+       ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+       GUESS=ia64-hp-hpux$HPUX_REV
+       ;;
+    3050*:HI-UX:*:*)
+       set_cc_for_build
+       sed 's/^        //' << EOF > "$dummy.c"
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       GUESS=unknown-hitachi-hiuxwe2
+       ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+       GUESS=hppa1.1-hp-bsd
+       ;;
+    9000/8??:4.3bsd:*:*)
+       GUESS=hppa1.0-hp-bsd
+       ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       GUESS=hppa1.0-hp-mpeix
+       ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+       GUESS=hppa1.1-hp-osf
+       ;;
+    hp8??:OSF1:*:*)
+       GUESS=hppa1.0-hp-osf
+       ;;
+    i*86:OSF1:*:*)
+       if test -x /usr/sbin/sysversion ; then
+           GUESS=$UNAME_MACHINE-unknown-osf1mk
+       else
+           GUESS=$UNAME_MACHINE-unknown-osf1
+       fi
+       ;;
+    parisc*:Lites*:*:*)
+       GUESS=hppa1.1-hp-lites
+       ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       GUESS=c1-convex-bsd
+       ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       GUESS=c34-convex-bsd
+       ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       GUESS=c38-convex-bsd
+       ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       GUESS=c4-convex-bsd
+       ;;
+    CRAY*Y-MP:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=ymp-cray-unicos$CRAY_REL
+       ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=t90-cray-unicos$CRAY_REL
+       ;;
+    CRAY*T3E:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=alphaev5-cray-unicosmk$CRAY_REL
+       ;;
+    CRAY*SV1:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=sv1-cray-unicos$CRAY_REL
+       ;;
+    *:UNICOS/mp:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=craynv-cray-unicosmp$CRAY_REL
+       ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+       FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+       GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+       ;;
+    5000:UNIX_System_V:4.*:*)
+       FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+       GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+       ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+       ;;
+    sparc*:BSD/OS:*:*)
+       GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+       ;;
+    *:BSD/OS:*:*)
+       GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+       ;;
+    arm:FreeBSD:*:*)
+       UNAME_PROCESSOR=`uname -p`
+       set_cc_for_build
+       if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_PCS_VFP
+       then
+           FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+           GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+       else
+           FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+           GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+       fi
+       ;;
+    *:FreeBSD:*:*)
+       UNAME_PROCESSOR=`uname -p`
+       case $UNAME_PROCESSOR in
+           amd64)
+               UNAME_PROCESSOR=x86_64 ;;
+           i386)
+               UNAME_PROCESSOR=i586 ;;
+       esac
+       FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+       ;;
+    i*:CYGWIN*:*)
+       GUESS=$UNAME_MACHINE-pc-cygwin
+       ;;
+    *:MINGW64*:*)
+       GUESS=$UNAME_MACHINE-pc-mingw64
+       ;;
+    *:MINGW*:*)
+       GUESS=$UNAME_MACHINE-pc-mingw32
+       ;;
+    *:MSYS*:*)
+       GUESS=$UNAME_MACHINE-pc-msys
+       ;;
+    i*:PW*:*)
+       GUESS=$UNAME_MACHINE-pc-pw32
+       ;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+       case $UNAME_MACHINE in
+           x86)
+               GUESS=i586-pc-interix$UNAME_RELEASE
+               ;;
+           authenticamd | genuineintel | EM64T)
+               GUESS=x86_64-unknown-interix$UNAME_RELEASE
+               ;;
+           IA64)
+               GUESS=ia64-unknown-interix$UNAME_RELEASE
+               ;;
+       esac ;;
+    i*:UWIN*:*)
+       GUESS=$UNAME_MACHINE-pc-uwin
+       ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       GUESS=x86_64-pc-cygwin
+       ;;
+    prep*:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=powerpcle-unknown-solaris2$SUN_REL
+       ;;
+    *:GNU:*:*)
+       # the GNU system
+       GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+       GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+       GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+       ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+       GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+       ;;
+    x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
+       GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
+       ;;
+    *:[Mm]anagarm:*:*)
+       GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
+       ;;
+    *:Minix:*:*)
+       GUESS=$UNAME_MACHINE-unknown-minix
+       ;;
+    aarch64:Linux:*:*)
+       set_cc_for_build
+       CPU=$UNAME_MACHINE
+       LIBCABI=$LIBC
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           ABI=64
+           sed 's/^        //' << EOF > "$dummy.c"
+           #ifdef __ARM_EABI__
+           #ifdef __ARM_PCS_VFP
+           ABI=eabihf
+           #else
+           ABI=eabi
+           #endif
+           #endif
+EOF
+           cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+           eval "$cc_set_abi"
+           case $ABI in
+               eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
+           esac
+       fi
+       GUESS=$CPU-unknown-linux-$LIBCABI
+       ;;
+    aarch64_be:Linux:*:*)
+       UNAME_MACHINE=aarch64_be
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+       esac
+       objdump --private-headers /bin/sh | grep -q ld.so.1
+       if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    arm*:Linux:*:*)
+       set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       else
+           if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+               | grep -q __ARM_PCS_VFP
+           then
+               GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+           else
+               GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+           fi
+       fi
+       ;;
+    avr32*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    cris:Linux:*:*)
+       GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+       ;;
+    crisv32:Linux:*:*)
+       GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+       ;;
+    e2k:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    frv:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    hexagon:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    i*86:Linux:*:*)
+       GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+       ;;
+    ia64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    k1om:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    kvx:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    kvx:cos:*:*)
+       GUESS=$UNAME_MACHINE-unknown-cos
+       ;;
+    kvx:mbr:*:*)
+       GUESS=$UNAME_MACHINE-unknown-mbr
+       ;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    m32r*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    m68*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+       set_cc_for_build
+       IS_GLIBC=0
+       test x"${LIBC}" = xgnu && IS_GLIBC=1
+       sed 's/^        //' << EOF > "$dummy.c"
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #undef mips64
+       #undef mips64el
+       #if ${IS_GLIBC} && defined(_ABI64)
+       LIBCABI=gnuabi64
+       #else
+       #if ${IS_GLIBC} && defined(_ABIN32)
+       LIBCABI=gnuabin32
+       #else
+       LIBCABI=${LIBC}
+       #endif
+       #endif
+
+       #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+       CPU=mipsisa64r6
+       #else
+       #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+       CPU=mipsisa32r6
+       #else
+       #if defined(__mips64)
+       CPU=mips64
+       #else
+       CPU=mips
+       #endif
+       #endif
+       #endif
+
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       MIPS_ENDIAN=el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       MIPS_ENDIAN=
+       #else
+       MIPS_ENDIAN=
+       #endif
+       #endif
+EOF
+       cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+       eval "$cc_set_vars"
+       test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+       ;;
+    mips64el:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    openrisc*:Linux:*:*)
+       GUESS=or1k-unknown-linux-$LIBC
+       ;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    padre:Linux:*:*)
+       GUESS=sparc-unknown-linux-$LIBC
+       ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       GUESS=hppa64-unknown-linux-$LIBC
+       ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+         PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+         *)    GUESS=hppa-unknown-linux-$LIBC ;;
+       esac
+       ;;
+    ppc64:Linux:*:*)
+       GUESS=powerpc64-unknown-linux-$LIBC
+       ;;
+    ppc:Linux:*:*)
+       GUESS=powerpc-unknown-linux-$LIBC
+       ;;
+    ppc64le:Linux:*:*)
+       GUESS=powerpc64le-unknown-linux-$LIBC
+       ;;
+    ppcle:Linux:*:*)
+       GUESS=powerpcle-unknown-linux-$LIBC
+       ;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+       ;;
+    sh64*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    sh*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    tile*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    vax:Linux:*:*)
+       GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+       ;;
+    x86_64:Linux:*:*)
+       set_cc_for_build
+       CPU=$UNAME_MACHINE
+       LIBCABI=$LIBC
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           ABI=64
+           sed 's/^        //' << EOF > "$dummy.c"
+           #ifdef __i386__
+           ABI=x86
+           #else
+           #ifdef __ILP32__
+           ABI=x32
+           #endif
+           #endif
+EOF
+           cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+           eval "$cc_set_abi"
+           case $ABI in
+               x86) CPU=i686 ;;
+               x32) LIBCABI=${LIBC}x32 ;;
+           esac
+       fi
+       GUESS=$CPU-pc-linux-$LIBCABI
+       ;;
+    xtensa*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       GUESS=i386-sequent-sysv4
+       ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+       # Unixware is an offshoot of SVR4, but it has its own version
+       # number series starting with 2...
+       # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+       # Use sysv4.2uw... so that sysv4* matches it.
+       GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+       ;;
+    i*86:OS/2:*:*)
+       # If we were able to find 'uname', then EMX Unix compatibility
+       # is probably installed.
+       GUESS=$UNAME_MACHINE-pc-os2-emx
+       ;;
+    i*86:XTS-300:*:STOP)
+       GUESS=$UNAME_MACHINE-unknown-stop
+       ;;
+    i*86:atheos:*:*)
+       GUESS=$UNAME_MACHINE-unknown-atheos
+       ;;
+    i*86:syllable:*:*)
+       GUESS=$UNAME_MACHINE-pc-syllable
+       ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+       GUESS=i386-unknown-lynxos$UNAME_RELEASE
+       ;;
+    i*86:*DOS:*:*)
+       GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+       ;;
+    i*86:*:4.*:*)
+       UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+       else
+               GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+       fi
+       ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+       else
+               GUESS=$UNAME_MACHINE-pc-sysv32
+       fi
+       ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+       # uname -m prints for DJGPP always 'pc', but it prints nothing about
+       # the processor, so we play safe by assuming i586.
+       # Note: whatever this is, it MUST be the same as what config.sub
+       # prints for the "djgpp" host, or else GDB configure will decide that
+       # this is a cross-build.
+       GUESS=i586-pc-msdosdjgpp
+       ;;
+    Intel:Mach:3*:*)
+       GUESS=i386-pc-mach3
+       ;;
+    paragon:*:*:*)
+       GUESS=i860-intel-osf1
+       ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+       fi
+       ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       GUESS=m68010-convergent-sysv
+       ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       GUESS=m68k-convergent-sysv
+       ;;
+    M680?0:D-NIX:5.3:*)
+       GUESS=m68k-diab-dnix
+       ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+       OS_REL='.3'
+       test -r /etc/.relid \
+           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+           && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+       ;;
+    mc68030:UNIX_System_V:4.*:*)
+       GUESS=m68k-atari-sysv4
+       ;;
+    TSUNAMI:LynxOS:2.*:*)
+       GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+       ;;
+    rs6000:LynxOS:2.*:*)
+       GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+       ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+       GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+       ;;
+    SM[BE]S:UNIX_SV:*:*)
+       GUESS=mips-dde-sysv$UNAME_RELEASE
+       ;;
+    RM*:ReliantUNIX-*:*:*)
+       GUESS=mips-sni-sysv4
+       ;;
+    RM*:SINIX-*:*:*)
+       GUESS=mips-sni-sysv4
+       ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               GUESS=$UNAME_MACHINE-sni-sysv4
+       else
+               GUESS=ns32k-sni-sysv
+       fi
+       ;;
+    PENTIUM:*:4.0*:*)  # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
+                       # says <Richard.M.Bartel@ccMail.Census.GOV>
+       GUESS=i586-unisys-sysv4
+       ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       GUESS=hppa1.1-stratus-sysv4
+       ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       GUESS=i860-stratus-sysv4
+       ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       GUESS=$UNAME_MACHINE-stratus-vos
+       ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       GUESS=hppa1.1-stratus-vos
+       ;;
+    mc68*:A/UX:*:*)
+       GUESS=m68k-apple-aux$UNAME_RELEASE
+       ;;
+    news*:NEWS-OS:6*:*)
+       GUESS=mips-sony-newsos6
+       ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if test -d /usr/nec; then
+               GUESS=mips-nec-sysv$UNAME_RELEASE
+       else
+               GUESS=mips-unknown-sysv$UNAME_RELEASE
+       fi
+       ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       GUESS=powerpc-be-beos
+       ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       GUESS=powerpc-apple-beos
+       ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       GUESS=i586-pc-beos
+       ;;
+    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
+       GUESS=i586-pc-haiku
+       ;;
+    ppc:Haiku:*:*)     # Haiku running on Apple PowerPC
+       GUESS=powerpc-apple-haiku
+       ;;
+    *:Haiku:*:*)       # Haiku modern gcc (not bound by BeOS compat)
+       GUESS=$UNAME_MACHINE-unknown-haiku
+       ;;
+    SX-4:SUPER-UX:*:*)
+       GUESS=sx4-nec-superux$UNAME_RELEASE
+       ;;
+    SX-5:SUPER-UX:*:*)
+       GUESS=sx5-nec-superux$UNAME_RELEASE
+       ;;
+    SX-6:SUPER-UX:*:*)
+       GUESS=sx6-nec-superux$UNAME_RELEASE
+       ;;
+    SX-7:SUPER-UX:*:*)
+       GUESS=sx7-nec-superux$UNAME_RELEASE
+       ;;
+    SX-8:SUPER-UX:*:*)
+       GUESS=sx8-nec-superux$UNAME_RELEASE
+       ;;
+    SX-8R:SUPER-UX:*:*)
+       GUESS=sx8r-nec-superux$UNAME_RELEASE
+       ;;
+    SX-ACE:SUPER-UX:*:*)
+       GUESS=sxace-nec-superux$UNAME_RELEASE
+       ;;
+    Power*:Rhapsody:*:*)
+       GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+       ;;
+    *:Rhapsody:*:*)
+       GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+       ;;
+    arm64:Darwin:*:*)
+       GUESS=aarch64-apple-darwin$UNAME_RELEASE
+       ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p`
+       case $UNAME_PROCESSOR in
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       if command -v xcode-select > /dev/null 2> /dev/null && \
+               ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+           # Avoid executing cc if there is no toolchain installed as
+           # cc will be a stub that puts up a graphical alert
+           # prompting the user to install developer tools.
+           CC_FOR_BUILD=no_compiler_found
+       else
+           set_cc_for_build
+       fi
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+                  (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+                  grep IS_64BIT_ARCH >/dev/null
+           then
+               case $UNAME_PROCESSOR in
+                   i386) UNAME_PROCESSOR=x86_64 ;;
+                   powerpc) UNAME_PROCESSOR=powerpc64 ;;
+               esac
+           fi
+           # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+           if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+                  (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+                  grep IS_PPC >/dev/null
+           then
+               UNAME_PROCESSOR=powerpc
+           fi
+       elif test "$UNAME_PROCESSOR" = i386 ; then
+           # uname -m returns i386 or x86_64
+           UNAME_PROCESSOR=$UNAME_MACHINE
+       fi
+       GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+       ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = x86; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+       ;;
+    *:QNX:*:4*)
+       GUESS=i386-pc-qnx
+       ;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+       GUESS=neo-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+       GUESS=nse-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsr-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsv-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsx-tandem-nsk$UNAME_RELEASE
+       ;;
+    *:NonStop-UX:*:*)
+       GUESS=mips-compaq-nonstopux
+       ;;
+    BS2000:POSIX*:*:*)
+       GUESS=bs2000-siemens-sysv
+       ;;
+    DS/*:UNIX_System_V:*:*)
+       GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+       ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "${cputype-}" = 386; then
+           UNAME_MACHINE=i386
+       elif test "x${cputype-}" != x; then
+           UNAME_MACHINE=$cputype
+       fi
+       GUESS=$UNAME_MACHINE-unknown-plan9
+       ;;
+    *:TOPS-10:*:*)
+       GUESS=pdp10-unknown-tops10
+       ;;
+    *:TENEX:*:*)
+       GUESS=pdp10-unknown-tenex
+       ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       GUESS=pdp10-dec-tops20
+       ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       GUESS=pdp10-xkl-tops20
+       ;;
+    *:TOPS-20:*:*)
+       GUESS=pdp10-unknown-tops20
+       ;;
+    *:ITS:*:*)
+       GUESS=pdp10-unknown-its
+       ;;
+    SEI:*:*:SEIUX)
+       GUESS=mips-sei-seiux$UNAME_RELEASE
+       ;;
+    *:DragonFly:*:*)
+       DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+       ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case $UNAME_MACHINE in
+           A*) GUESS=alpha-dec-vms ;;
+           I*) GUESS=ia64-dec-vms ;;
+           V*) GUESS=vax-dec-vms ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       GUESS=i386-pc-xenix
+       ;;
+    i*86:skyos:*:*)
+       SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+       GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+       ;;
+    i*86:rdos:*:*)
+       GUESS=$UNAME_MACHINE-pc-rdos
+       ;;
+    i*86:Fiwix:*:*)
+       GUESS=$UNAME_MACHINE-pc-fiwix
+       ;;
+    *:AROS:*:*)
+       GUESS=$UNAME_MACHINE-unknown-aros
+       ;;
+    x86_64:VMkernel:*:*)
+       GUESS=$UNAME_MACHINE-unknown-esx
+       ;;
+    amd64:Isilon\ OneFS:*:*)
+       GUESS=x86_64-unknown-onefs
+       ;;
+    *:Unleashed:*:*)
+       GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+       ;;
+    *:Ironclad:*:*)
+       GUESS=$UNAME_MACHINE-unknown-ironclad
+       ;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+int
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+       # If we got here on MIPS GNU/Linux, output extra information.
+       cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+       ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..0692f6f
--- /dev/null
@@ -0,0 +1,111 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+
+/*
+ * The generated config.h is only included in runtest.cc, and there are
+ * no plans to use it elsewhere.  All defines apart from HAVE_PTHREAD_H
+ * are purely informational.
+ */
+
+
+/* Define if we can use x64 gcc inline assembler. */
+#undef HAVE_GCC_ASM_FOR_X64
+
+/* Define if we can use x87 gcc inline assembler. */
+#undef HAVE_GCC_ASM_FOR_X87
+
+/* Define if glibc has incorrect _FORTIFY_SOURCE wrappers for memmove and
+   bcopy. */
+#undef HAVE_GLIBC_MEMMOVE_BUG
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if gcc has the ipa-pure-const bug. */
+#undef HAVE_IPA_PURE_CONST_BUG
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if your compiler provides __uint128_t. */
+#undef HAVE_UINT128_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `size_t', as computed by sizeof. */
+#undef SIZEOF_SIZE_T
+
+/* The size of `__uint128_t', as computed by sizeof. */
+#undef SIZEOF___UINT128_T
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+#undef int32_t
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef int64_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint64_t
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..4aaae46
--- /dev/null
@@ -0,0 +1,2354 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale
+
+timestamp='2024-05-27'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2024 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+       *-*-*-*-*)
+               echo "Invalid configuration '$1': more than four components" >&2
+               exit 1
+               ;;
+       *-*-*-*)
+               basic_machine=$field1-$field2
+               basic_os=$field3-$field4
+               ;;
+       *-*-*)
+               # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+               # parts
+               maybe_os=$field2-$field3
+               case $maybe_os in
+                         cloudabi*-eabi* \
+                       | kfreebsd*-gnu* \
+                       | knetbsd*-gnu* \
+                       | kopensolaris*-gnu* \
+                       | linux-* \
+                       | managarm-* \
+                       | netbsd*-eabi* \
+                       | netbsd*-gnu* \
+                       | nto-qnx* \
+                       | os2-emx* \
+                       | rtmk-nova* \
+                       | storm-chaos* \
+                       | uclinux-gnu* \
+                       | uclinux-uclibc* \
+                       | windows-* )
+                               basic_machine=$field1
+                               basic_os=$maybe_os
+                               ;;
+                       android-linux)
+                               basic_machine=$field1-unknown
+                               basic_os=linux-android
+                               ;;
+                       *)
+                               basic_machine=$field1-$field2
+                               basic_os=$field3
+                               ;;
+               esac
+               ;;
+       *-*)
+               case $field1-$field2 in
+                       # Shorthands that happen to contain a single dash
+                       convex-c[12] | convex-c3[248])
+                               basic_machine=$field2-convex
+                               basic_os=
+                               ;;
+                       decstation-3100)
+                               basic_machine=mips-dec
+                               basic_os=
+                               ;;
+                       *-*)
+                               # Second component is usually, but not always the OS
+                               case $field2 in
+                                       # Do not treat sunos as a manufacturer
+                                       sun*os*)
+                                               basic_machine=$field1
+                                               basic_os=$field2
+                                               ;;
+                                       # Manufacturers
+                                         3100* \
+                                       | 32* \
+                                       | 3300* \
+                                       | 3600* \
+                                       | 7300* \
+                                       | acorn \
+                                       | altos* \
+                                       | apollo \
+                                       | apple \
+                                       | atari \
+                                       | att* \
+                                       | axis \
+                                       | be \
+                                       | bull \
+                                       | cbm \
+                                       | ccur \
+                                       | cisco \
+                                       | commodore \
+                                       | convergent* \
+                                       | convex* \
+                                       | cray \
+                                       | crds \
+                                       | dec* \
+                                       | delta* \
+                                       | dg \
+                                       | digital \
+                                       | dolphin \
+                                       | encore* \
+                                       | gould \
+                                       | harris \
+                                       | highlevel \
+                                       | hitachi* \
+                                       | hp \
+                                       | ibm* \
+                                       | intergraph \
+                                       | isi* \
+                                       | knuth \
+                                       | masscomp \
+                                       | microblaze* \
+                                       | mips* \
+                                       | motorola* \
+                                       | ncr* \
+                                       | news \
+                                       | next \
+                                       | ns \
+                                       | oki \
+                                       | omron* \
+                                       | pc533* \
+                                       | rebel \
+                                       | rom68k \
+                                       | rombug \
+                                       | semi \
+                                       | sequent* \
+                                       | siemens \
+                                       | sgi* \
+                                       | siemens \
+                                       | sim \
+                                       | sni \
+                                       | sony* \
+                                       | stratus \
+                                       | sun \
+                                       | sun[234]* \
+                                       | tektronix \
+                                       | tti* \
+                                       | ultra \
+                                       | unicom* \
+                                       | wec \
+                                       | winbond \
+                                       | wrs)
+                                               basic_machine=$field1-$field2
+                                               basic_os=
+                                               ;;
+                                       zephyr*)
+                                               basic_machine=$field1-unknown
+                                               basic_os=$field2
+                                               ;;
+                                       *)
+                                               basic_machine=$field1
+                                               basic_os=$field2
+                                               ;;
+                               esac
+                       ;;
+               esac
+               ;;
+       *)
+               # Convert single-component short-hands not valid as part of
+               # multi-component configurations.
+               case $field1 in
+                       386bsd)
+                               basic_machine=i386-pc
+                               basic_os=bsd
+                               ;;
+                       a29khif)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       adobe68k)
+                               basic_machine=m68010-adobe
+                               basic_os=scout
+                               ;;
+                       alliant)
+                               basic_machine=fx80-alliant
+                               basic_os=
+                               ;;
+                       altos | altos3068)
+                               basic_machine=m68k-altos
+                               basic_os=
+                               ;;
+                       am29k)
+                               basic_machine=a29k-none
+                               basic_os=bsd
+                               ;;
+                       amdahl)
+                               basic_machine=580-amdahl
+                               basic_os=sysv
+                               ;;
+                       amiga)
+                               basic_machine=m68k-unknown
+                               basic_os=
+                               ;;
+                       amigaos | amigados)
+                               basic_machine=m68k-unknown
+                               basic_os=amigaos
+                               ;;
+                       amigaunix | amix)
+                               basic_machine=m68k-unknown
+                               basic_os=sysv4
+                               ;;
+                       apollo68)
+                               basic_machine=m68k-apollo
+                               basic_os=sysv
+                               ;;
+                       apollo68bsd)
+                               basic_machine=m68k-apollo
+                               basic_os=bsd
+                               ;;
+                       aros)
+                               basic_machine=i386-pc
+                               basic_os=aros
+                               ;;
+                       aux)
+                               basic_machine=m68k-apple
+                               basic_os=aux
+                               ;;
+                       balance)
+                               basic_machine=ns32k-sequent
+                               basic_os=dynix
+                               ;;
+                       blackfin)
+                               basic_machine=bfin-unknown
+                               basic_os=linux
+                               ;;
+                       cegcc)
+                               basic_machine=arm-unknown
+                               basic_os=cegcc
+                               ;;
+                       cray)
+                               basic_machine=j90-cray
+                               basic_os=unicos
+                               ;;
+                       crds | unos)
+                               basic_machine=m68k-crds
+                               basic_os=
+                               ;;
+                       da30)
+                               basic_machine=m68k-da30
+                               basic_os=
+                               ;;
+                       decstation | pmax | pmin | dec3100 | decstatn)
+                               basic_machine=mips-dec
+                               basic_os=
+                               ;;
+                       delta88)
+                               basic_machine=m88k-motorola
+                               basic_os=sysv3
+                               ;;
+                       dicos)
+                               basic_machine=i686-pc
+                               basic_os=dicos
+                               ;;
+                       djgpp)
+                               basic_machine=i586-pc
+                               basic_os=msdosdjgpp
+                               ;;
+                       ebmon29k)
+                               basic_machine=a29k-amd
+                               basic_os=ebmon
+                               ;;
+                       es1800 | OSE68k | ose68k | ose | OSE)
+                               basic_machine=m68k-ericsson
+                               basic_os=ose
+                               ;;
+                       gmicro)
+                               basic_machine=tron-gmicro
+                               basic_os=sysv
+                               ;;
+                       go32)
+                               basic_machine=i386-pc
+                               basic_os=go32
+                               ;;
+                       h8300hms)
+                               basic_machine=h8300-hitachi
+                               basic_os=hms
+                               ;;
+                       h8300xray)
+                               basic_machine=h8300-hitachi
+                               basic_os=xray
+                               ;;
+                       h8500hms)
+                               basic_machine=h8500-hitachi
+                               basic_os=hms
+                               ;;
+                       harris)
+                               basic_machine=m88k-harris
+                               basic_os=sysv3
+                               ;;
+                       hp300 | hp300hpux)
+                               basic_machine=m68k-hp
+                               basic_os=hpux
+                               ;;
+                       hp300bsd)
+                               basic_machine=m68k-hp
+                               basic_os=bsd
+                               ;;
+                       hppaosf)
+                               basic_machine=hppa1.1-hp
+                               basic_os=osf
+                               ;;
+                       hppro)
+                               basic_machine=hppa1.1-hp
+                               basic_os=proelf
+                               ;;
+                       i386mach)
+                               basic_machine=i386-mach
+                               basic_os=mach
+                               ;;
+                       isi68 | isi)
+                               basic_machine=m68k-isi
+                               basic_os=sysv
+                               ;;
+                       m68knommu)
+                               basic_machine=m68k-unknown
+                               basic_os=linux
+                               ;;
+                       magnum | m3230)
+                               basic_machine=mips-mips
+                               basic_os=sysv
+                               ;;
+                       merlin)
+                               basic_machine=ns32k-utek
+                               basic_os=sysv
+                               ;;
+                       mingw64)
+                               basic_machine=x86_64-pc
+                               basic_os=mingw64
+                               ;;
+                       mingw32)
+                               basic_machine=i686-pc
+                               basic_os=mingw32
+                               ;;
+                       mingw32ce)
+                               basic_machine=arm-unknown
+                               basic_os=mingw32ce
+                               ;;
+                       monitor)
+                               basic_machine=m68k-rom68k
+                               basic_os=coff
+                               ;;
+                       morphos)
+                               basic_machine=powerpc-unknown
+                               basic_os=morphos
+                               ;;
+                       moxiebox)
+                               basic_machine=moxie-unknown
+                               basic_os=moxiebox
+                               ;;
+                       msdos)
+                               basic_machine=i386-pc
+                               basic_os=msdos
+                               ;;
+                       msys)
+                               basic_machine=i686-pc
+                               basic_os=msys
+                               ;;
+                       mvs)
+                               basic_machine=i370-ibm
+                               basic_os=mvs
+                               ;;
+                       nacl)
+                               basic_machine=le32-unknown
+                               basic_os=nacl
+                               ;;
+                       ncr3000)
+                               basic_machine=i486-ncr
+                               basic_os=sysv4
+                               ;;
+                       netbsd386)
+                               basic_machine=i386-pc
+                               basic_os=netbsd
+                               ;;
+                       netwinder)
+                               basic_machine=armv4l-rebel
+                               basic_os=linux
+                               ;;
+                       news | news700 | news800 | news900)
+                               basic_machine=m68k-sony
+                               basic_os=newsos
+                               ;;
+                       news1000)
+                               basic_machine=m68030-sony
+                               basic_os=newsos
+                               ;;
+                       necv70)
+                               basic_machine=v70-nec
+                               basic_os=sysv
+                               ;;
+                       nh3000)
+                               basic_machine=m68k-harris
+                               basic_os=cxux
+                               ;;
+                       nh[45]000)
+                               basic_machine=m88k-harris
+                               basic_os=cxux
+                               ;;
+                       nindy960)
+                               basic_machine=i960-intel
+                               basic_os=nindy
+                               ;;
+                       mon960)
+                               basic_machine=i960-intel
+                               basic_os=mon960
+                               ;;
+                       nonstopux)
+                               basic_machine=mips-compaq
+                               basic_os=nonstopux
+                               ;;
+                       os400)
+                               basic_machine=powerpc-ibm
+                               basic_os=os400
+                               ;;
+                       OSE68000 | ose68000)
+                               basic_machine=m68000-ericsson
+                               basic_os=ose
+                               ;;
+                       os68k)
+                               basic_machine=m68k-none
+                               basic_os=os68k
+                               ;;
+                       paragon)
+                               basic_machine=i860-intel
+                               basic_os=osf
+                               ;;
+                       parisc)
+                               basic_machine=hppa-unknown
+                               basic_os=linux
+                               ;;
+                       psp)
+                               basic_machine=mipsallegrexel-sony
+                               basic_os=psp
+                               ;;
+                       pw32)
+                               basic_machine=i586-unknown
+                               basic_os=pw32
+                               ;;
+                       rdos | rdos64)
+                               basic_machine=x86_64-pc
+                               basic_os=rdos
+                               ;;
+                       rdos32)
+                               basic_machine=i386-pc
+                               basic_os=rdos
+                               ;;
+                       rom68k)
+                               basic_machine=m68k-rom68k
+                               basic_os=coff
+                               ;;
+                       sa29200)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       sei)
+                               basic_machine=mips-sei
+                               basic_os=seiux
+                               ;;
+                       sequent)
+                               basic_machine=i386-sequent
+                               basic_os=
+                               ;;
+                       sps7)
+                               basic_machine=m68k-bull
+                               basic_os=sysv2
+                               ;;
+                       st2000)
+                               basic_machine=m68k-tandem
+                               basic_os=
+                               ;;
+                       stratus)
+                               basic_machine=i860-stratus
+                               basic_os=sysv4
+                               ;;
+                       sun2)
+                               basic_machine=m68000-sun
+                               basic_os=
+                               ;;
+                       sun2os3)
+                               basic_machine=m68000-sun
+                               basic_os=sunos3
+                               ;;
+                       sun2os4)
+                               basic_machine=m68000-sun
+                               basic_os=sunos4
+                               ;;
+                       sun3)
+                               basic_machine=m68k-sun
+                               basic_os=
+                               ;;
+                       sun3os3)
+                               basic_machine=m68k-sun
+                               basic_os=sunos3
+                               ;;
+                       sun3os4)
+                               basic_machine=m68k-sun
+                               basic_os=sunos4
+                               ;;
+                       sun4)
+                               basic_machine=sparc-sun
+                               basic_os=
+                               ;;
+                       sun4os3)
+                               basic_machine=sparc-sun
+                               basic_os=sunos3
+                               ;;
+                       sun4os4)
+                               basic_machine=sparc-sun
+                               basic_os=sunos4
+                               ;;
+                       sun4sol2)
+                               basic_machine=sparc-sun
+                               basic_os=solaris2
+                               ;;
+                       sun386 | sun386i | roadrunner)
+                               basic_machine=i386-sun
+                               basic_os=
+                               ;;
+                       sv1)
+                               basic_machine=sv1-cray
+                               basic_os=unicos
+                               ;;
+                       symmetry)
+                               basic_machine=i386-sequent
+                               basic_os=dynix
+                               ;;
+                       t3e)
+                               basic_machine=alphaev5-cray
+                               basic_os=unicos
+                               ;;
+                       t90)
+                               basic_machine=t90-cray
+                               basic_os=unicos
+                               ;;
+                       toad1)
+                               basic_machine=pdp10-xkl
+                               basic_os=tops20
+                               ;;
+                       tpf)
+                               basic_machine=s390x-ibm
+                               basic_os=tpf
+                               ;;
+                       udi29k)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       ultra3)
+                               basic_machine=a29k-nyu
+                               basic_os=sym1
+                               ;;
+                       v810 | necv810)
+                               basic_machine=v810-nec
+                               basic_os=none
+                               ;;
+                       vaxv)
+                               basic_machine=vax-dec
+                               basic_os=sysv
+                               ;;
+                       vms)
+                               basic_machine=vax-dec
+                               basic_os=vms
+                               ;;
+                       vsta)
+                               basic_machine=i386-pc
+                               basic_os=vsta
+                               ;;
+                       vxworks960)
+                               basic_machine=i960-wrs
+                               basic_os=vxworks
+                               ;;
+                       vxworks68)
+                               basic_machine=m68k-wrs
+                               basic_os=vxworks
+                               ;;
+                       vxworks29k)
+                               basic_machine=a29k-wrs
+                               basic_os=vxworks
+                               ;;
+                       xbox)
+                               basic_machine=i686-pc
+                               basic_os=mingw32
+                               ;;
+                       ymp)
+                               basic_machine=ymp-cray
+                               basic_os=unicos
+                               ;;
+                       *)
+                               basic_machine=$1
+                               basic_os=
+                               ;;
+               esac
+               ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+       # Here we handle the default manufacturer of certain CPU types.  It is in
+       # some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               cpu=hppa1.1
+               vendor=winbond
+               ;;
+       op50n)
+               cpu=hppa1.1
+               vendor=oki
+               ;;
+       op60c)
+               cpu=hppa1.1
+               vendor=oki
+               ;;
+       ibm*)
+               cpu=i370
+               vendor=ibm
+               ;;
+       orion105)
+               cpu=clipper
+               vendor=highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               cpu=m68k
+               vendor=apple
+               ;;
+       pmac | pmac-mpw)
+               cpu=powerpc
+               vendor=apple
+               ;;
+
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               cpu=m68000
+               vendor=att
+               ;;
+       3b*)
+               cpu=we32k
+               vendor=att
+               ;;
+       bluegene*)
+               cpu=powerpc
+               vendor=ibm
+               basic_os=cnk
+               ;;
+       decsystem10* | dec10*)
+               cpu=pdp10
+               vendor=dec
+               basic_os=tops10
+               ;;
+       decsystem20* | dec20*)
+               cpu=pdp10
+               vendor=dec
+               basic_os=tops20
+               ;;
+       delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300)
+               cpu=m68k
+               vendor=motorola
+               ;;
+       # This used to be dpx2*, but that gets the RS6000-based
+       # DPX/20 and the x86-based DPX/2-100 wrong.  See
+       # https://oldskool.silicium.org/stations/bull_dpx20.htm
+       # https://www.feb-patrimoine.com/english/bull_dpx2.htm
+       # https://www.feb-patrimoine.com/english/unix_and_bull.htm
+       dpx2 | dpx2[23]00 | dpx2[23]xx)
+               cpu=m68k
+               vendor=bull
+               ;;
+       dpx2100 | dpx21xx)
+               cpu=i386
+               vendor=bull
+               ;;
+       dpx20)
+               cpu=rs6000
+               vendor=bull
+               ;;
+       encore | umax | mmax)
+               cpu=ns32k
+               vendor=encore
+               ;;
+       elxsi)
+               cpu=elxsi
+               vendor=elxsi
+               basic_os=${basic_os:-bsd}
+               ;;
+       fx2800)
+               cpu=i860
+               vendor=alliant
+               ;;
+       genix)
+               cpu=ns32k
+               vendor=ns
+               ;;
+       h3050r* | hiux*)
+               cpu=hppa1.1
+               vendor=hitachi
+               basic_os=hiuxwe2
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               cpu=m68000
+               vendor=hp
+               ;;
+       hp9k3[2-9][0-9])
+               cpu=m68k
+               vendor=hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       i*86v32)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv32
+               ;;
+       i*86v4*)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv4
+               ;;
+       i*86v)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv
+               ;;
+       i*86sol2)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=solaris2
+               ;;
+       j90 | j90-cray)
+               cpu=j90
+               vendor=cray
+               basic_os=${basic_os:-unicos}
+               ;;
+       iris | iris4d)
+               cpu=mips
+               vendor=sgi
+               case $basic_os in
+                   irix*)
+                       ;;
+                   *)
+                       basic_os=irix4
+                       ;;
+               esac
+               ;;
+       miniframe)
+               cpu=m68000
+               vendor=convergent
+               ;;
+       *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               cpu=m68k
+               vendor=atari
+               basic_os=mint
+               ;;
+       news-3600 | risc-news)
+               cpu=mips
+               vendor=sony
+               basic_os=newsos
+               ;;
+       next | m*-next)
+               cpu=m68k
+               vendor=next
+               ;;
+       np1)
+               cpu=np1
+               vendor=gould
+               ;;
+       op50n-* | op60c-*)
+               cpu=hppa1.1
+               vendor=oki
+               basic_os=proelf
+               ;;
+       pa-hitachi)
+               cpu=hppa1.1
+               vendor=hitachi
+               basic_os=hiuxwe2
+               ;;
+       pbd)
+               cpu=sparc
+               vendor=tti
+               ;;
+       pbb)
+               cpu=m68k
+               vendor=tti
+               ;;
+       pc532)
+               cpu=ns32k
+               vendor=pc532
+               ;;
+       pn)
+               cpu=pn
+               vendor=gould
+               ;;
+       power)
+               cpu=power
+               vendor=ibm
+               ;;
+       ps2)
+               cpu=i386
+               vendor=ibm
+               ;;
+       rm[46]00)
+               cpu=mips
+               vendor=siemens
+               ;;
+       rtpc | rtpc-*)
+               cpu=romp
+               vendor=ibm
+               ;;
+       sde)
+               cpu=mipsisa32
+               vendor=sde
+               basic_os=${basic_os:-elf}
+               ;;
+       simso-wrs)
+               cpu=sparclite
+               vendor=wrs
+               basic_os=vxworks
+               ;;
+       tower | tower-32)
+               cpu=m68k
+               vendor=ncr
+               ;;
+       vpp*|vx|vx-*)
+               cpu=f301
+               vendor=fujitsu
+               ;;
+       w65)
+               cpu=w65
+               vendor=wdc
+               ;;
+       w89k-*)
+               cpu=hppa1.1
+               vendor=winbond
+               basic_os=proelf
+               ;;
+       none)
+               cpu=none
+               vendor=none
+               ;;
+       leon|leon[3-9])
+               cpu=sparc
+               vendor=$basic_machine
+               ;;
+       leon-*|leon[3-9]-*)
+               cpu=sparc
+               vendor=`echo "$basic_machine" | sed 's/-.*//'`
+               ;;
+
+       *-*)
+               saved_IFS=$IFS
+               IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+               IFS=$saved_IFS
+               ;;
+       # We use 'pc' rather than 'unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+               cpu=$basic_machine
+               vendor=pc
+               ;;
+       # These rules are duplicated from below for sake of the special case above;
+       # i.e. things that normalized to x86 arches should also default to "pc"
+       pc98)
+               cpu=i386
+               vendor=pc
+               ;;
+       x64 | amd64)
+               cpu=x86_64
+               vendor=pc
+               ;;
+       # Recognize the basic CPU types without company name.
+       *)
+               cpu=$basic_machine
+               vendor=unknown
+               ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+       # Here we handle the default manufacturer of certain CPU types in canonical form.
+       # It is in some cases the only manufacturer, in others, it is the most popular.
+       c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown)
+               vendor=convex
+               basic_os=${basic_os:-bsd}
+               ;;
+       craynv-unknown)
+               vendor=cray
+               basic_os=${basic_os:-unicosmp}
+               ;;
+       c90-unknown | c90-cray)
+               vendor=cray
+               basic_os=${basic_os:-unicos}
+               ;;
+       fx80-unknown)
+               vendor=alliant
+               ;;
+       romp-unknown)
+               vendor=ibm
+               ;;
+       mmix-unknown)
+               vendor=knuth
+               ;;
+       microblaze-unknown | microblazeel-unknown)
+               vendor=xilinx
+               ;;
+       rs6000-unknown)
+               vendor=ibm
+               ;;
+       vax-unknown)
+               vendor=dec
+               ;;
+       pdp11-unknown)
+               vendor=dec
+               ;;
+       we32k-unknown)
+               vendor=att
+               ;;
+       cydra-unknown)
+               vendor=cydrome
+               ;;
+       i370-ibm*)
+               vendor=ibm
+               ;;
+       orion-unknown)
+               vendor=highlevel
+               ;;
+       xps-unknown | xps100-unknown)
+               cpu=xps100
+               vendor=honeywell
+               ;;
+
+       # Here we normalize CPU types with a missing or matching vendor
+       armh-unknown | armh-alt)
+               cpu=armv7l
+               vendor=alt
+               basic_os=${basic_os:-linux-gnueabihf}
+               ;;
+
+       # Normalized CPU+vendor pairs that imply an OS, if not otherwise specified
+       m68k-isi)
+               basic_os=${basic_os:-sysv}
+               ;;
+       m68k-sony)
+               basic_os=${basic_os:-newsos}
+               ;;
+       m68k-tektronix)
+               basic_os=${basic_os:-bsd}
+               ;;
+       m88k-harris)
+               basic_os=${basic_os:-sysv3}
+               ;;
+       i386-bull | m68k-bull)
+               basic_os=${basic_os:-sysv3}
+               ;;
+       rs6000-bull)
+               basic_os=${basic_os:-bosx}
+               ;;
+       mips-sni)
+               basic_os=${basic_os:-sysv4}
+               ;;
+
+       # Here we normalize CPU types irrespective of the vendor
+       amd64-*)
+               cpu=x86_64
+               ;;
+       blackfin-*)
+               cpu=bfin
+               basic_os=${basic_os:-linux}
+               ;;
+       c54x-*)
+               cpu=tic54x
+               ;;
+       c55x-*)
+               cpu=tic55x
+               ;;
+       c6x-*)
+               cpu=tic6x
+               ;;
+       e500v[12]-*)
+               cpu=powerpc
+               basic_os=${basic_os}"spe"
+               ;;
+       mips3*-*)
+               cpu=mips64
+               ;;
+       ms1-*)
+               cpu=mt
+               ;;
+       m68knommu-*)
+               cpu=m68k
+               basic_os=${basic_os:-linux}
+               ;;
+       m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+               cpu=s12z
+               ;;
+       openrisc-*)
+               cpu=or32
+               ;;
+       parisc-*)
+               cpu=hppa
+               basic_os=${basic_os:-linux}
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               cpu=i586
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
+               cpu=i686
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               cpu=i686
+               ;;
+       pentium4-*)
+               cpu=i786
+               ;;
+       ppc-* | ppcbe-*)
+               cpu=powerpc
+               ;;
+       ppcle-* | powerpclittle-*)
+               cpu=powerpcle
+               ;;
+       ppc64-*)
+               cpu=powerpc64
+               ;;
+       ppc64le-* | powerpc64little-*)
+               cpu=powerpc64le
+               ;;
+       sb1-*)
+               cpu=mipsisa64sb1
+               ;;
+       sb1el-*)
+               cpu=mipsisa64sb1el
+               ;;
+       sh5e[lb]-*)
+               cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+               ;;
+       spur-*)
+               cpu=spur
+               ;;
+       strongarm-* | thumb-*)
+               cpu=arm
+               ;;
+       tx39-*)
+               cpu=mipstx39
+               ;;
+       tx39el-*)
+               cpu=mipstx39el
+               ;;
+       xscale-* | xscalee[bl]-*)
+               cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+               ;;
+       arm64-* | aarch64le-*)
+               cpu=aarch64
+               ;;
+
+       # Recognize the canonical CPU Types that limit and/or modify the
+       # company names they are paired with.
+       cr16-*)
+               basic_os=${basic_os:-elf}
+               ;;
+       crisv32-* | etraxfs*-*)
+               cpu=crisv32
+               vendor=axis
+               ;;
+       cris-* | etrax*-*)
+               cpu=cris
+               vendor=axis
+               ;;
+       crx-*)
+               basic_os=${basic_os:-elf}
+               ;;
+       neo-tandem)
+               cpu=neo
+               vendor=tandem
+               ;;
+       nse-tandem)
+               cpu=nse
+               vendor=tandem
+               ;;
+       nsr-tandem)
+               cpu=nsr
+               vendor=tandem
+               ;;
+       nsv-tandem)
+               cpu=nsv
+               vendor=tandem
+               ;;
+       nsx-tandem)
+               cpu=nsx
+               vendor=tandem
+               ;;
+       mipsallegrexel-sony)
+               cpu=mipsallegrexel
+               vendor=sony
+               ;;
+       tile*-*)
+               basic_os=${basic_os:-linux-gnu}
+               ;;
+
+       *)
+               # Recognize the canonical CPU types that are allowed with any
+               # company name.
+               case $cpu in
+                         1750a \
+                       | 580 \
+                       | [cjt]90 \
+                       | a29k \
+                       | aarch64 \
+                       | aarch64_be \
+                       | aarch64c \
+                       | abacus \
+                       | alpha \
+                       | alpha64 \
+                       | alpha64ev56 \
+                       | alpha64ev6[78] \
+                       | alpha64ev[4-8] \
+                       | alpha64pca5[67] \
+                       | alphaev56 \
+                       | alphaev6[78] \
+                       | alphaev[4-8] \
+                       | alphapca5[67] \
+                       | am33_2.0 \
+                       | amdgcn \
+                       | arc \
+                       | arc32 \
+                       | arc64 \
+                       | arceb \
+                       | arm \
+                       | arm64e \
+                       | arm64ec \
+                       | arm[lb]e \
+                       | arme[lb] \
+                       | armv* \
+                       | asmjs \
+                       | avr \
+                       | avr32 \
+                       | ba \
+                       | be32 \
+                       | be64 \
+                       | bfin \
+                       | bpf \
+                       | bs2000 \
+                       | c30 \
+                       | c4x \
+                       | c8051 \
+                       | c[123]* \
+                       | clipper \
+                       | craynv \
+                       | csky \
+                       | cydra \
+                       | d10v \
+                       | d30v \
+                       | dlx \
+                       | dsp16xx \
+                       | e2k \
+                       | elxsi \
+                       | epiphany \
+                       | f30[01] \
+                       | f700 \
+                       | fido \
+                       | fr30 \
+                       | frv \
+                       | ft32 \
+                       | fx80 \
+                       | h8300 \
+                       | h8500 \
+                       | hexagon \
+                       | hppa \
+                       | hppa1.[01] \
+                       | hppa2.0 \
+                       | hppa2.0[nw] \
+                       | hppa64 \
+                       | i*86 \
+                       | i370 \
+                       | i860 \
+                       | i960 \
+                       | ia16 \
+                       | ia64 \
+                       | ip2k \
+                       | iq2000 \
+                       | javascript \
+                       | k1om \
+                       | kvx \
+                       | le32 \
+                       | le64 \
+                       | lm32 \
+                       | loongarch32 \
+                       | loongarch64 \
+                       | m32c \
+                       | m32r \
+                       | m32rle \
+                       | m5200 \
+                       | m68000 \
+                       | m680[012346]0 \
+                       | m6811 \
+                       | m6812 \
+                       | m68360 \
+                       | m683?2 \
+                       | m68hc11 \
+                       | m68hc12 \
+                       | m68hcs12x \
+                       | m68k \
+                       | m88110 \
+                       | m88k \
+                       | maxq \
+                       | mb \
+                       | mcore \
+                       | mep \
+                       | metag \
+                       | microblaze \
+                       | microblazeel \
+                       | mips* \
+                       | mmix \
+                       | mn10200 \
+                       | mn10300 \
+                       | moxie \
+                       | msp430 \
+                       | mt \
+                       | nanomips* \
+                       | nds32 \
+                       | nds32be \
+                       | nds32le \
+                       | nfp \
+                       | nios \
+                       | nios2 \
+                       | nios2eb \
+                       | nios2el \
+                       | none \
+                       | np1 \
+                       | ns16k \
+                       | ns32k \
+                       | nvptx \
+                       | open8 \
+                       | or1k* \
+                       | or32 \
+                       | orion \
+                       | pdp10 \
+                       | pdp11 \
+                       | picochip \
+                       | pj \
+                       | pjl \
+                       | pn \
+                       | power \
+                       | powerpc \
+                       | powerpc64 \
+                       | powerpc64le \
+                       | powerpcle \
+                       | powerpcspe \
+                       | pru \
+                       | pyramid \
+                       | riscv \
+                       | riscv32 \
+                       | riscv32be \
+                       | riscv64 \
+                       | riscv64be \
+                       | rl78 \
+                       | romp \
+                       | rs6000 \
+                       | rx \
+                       | s390 \
+                       | s390x \
+                       | score \
+                       | sh \
+                       | sh64 \
+                       | sh64le \
+                       | sh[12345][lb]e \
+                       | sh[1234] \
+                       | sh[1234]e[lb] \
+                       | sh[23]e \
+                       | sh[23]ele \
+                       | sh[24]a \
+                       | sh[24]ae[lb] \
+                       | sh[lb]e \
+                       | she[lb] \
+                       | shl \
+                       | sparc \
+                       | sparc64 \
+                       | sparc64b \
+                       | sparc64v \
+                       | sparc86x \
+                       | sparclet \
+                       | sparclite \
+                       | sparcv8 \
+                       | sparcv9 \
+                       | sparcv9b \
+                       | sparcv9v \
+                       | spu \
+                       | sv1 \
+                       | sx* \
+                       | tahoe \
+                       | thumbv7* \
+                       | tic30 \
+                       | tic4x \
+                       | tic54x \
+                       | tic55x \
+                       | tic6x \
+                       | tic80 \
+                       | tron \
+                       | ubicom32 \
+                       | v70 \
+                       | v810 \
+                       | v850 \
+                       | v850e \
+                       | v850e1 \
+                       | v850e2 \
+                       | v850e2v3 \
+                       | v850es \
+                       | vax \
+                       | vc4 \
+                       | visium \
+                       | w65 \
+                       | wasm32 \
+                       | wasm64 \
+                       | we32k \
+                       | x86 \
+                       | x86_64 \
+                       | xc16x \
+                       | xgate \
+                       | xps100 \
+                       | xstormy16 \
+                       | xtensa* \
+                       | ymp \
+                       | z80 \
+                       | z8k)
+                               ;;
+
+                       *)
+                               echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
+                               exit 1
+                               ;;
+               esac
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+       digital*)
+               vendor=dec
+               ;;
+       commodore*)
+               vendor=cbm
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x"$basic_os" != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+obj=
+case $basic_os in
+       gnu/linux*)
+               kernel=linux
+               os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+               ;;
+       os2-emx)
+               kernel=os2
+               os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+               ;;
+       nto-qnx*)
+               kernel=nto
+               os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+               ;;
+       *-*)
+               saved_IFS=$IFS
+               IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+               IFS=$saved_IFS
+               ;;
+       # Default OS when just kernel was specified
+       nto*)
+               kernel=nto
+               os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+               ;;
+       linux*)
+               kernel=linux
+               os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+               ;;
+       managarm*)
+               kernel=managarm
+               os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
+               ;;
+       *)
+               kernel=
+               os=$basic_os
+               ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+       # First match some system type aliases that might get confused
+       # with valid system types.
+       # solaris* is a basic system type, with this one exception.
+       auroraux)
+               os=auroraux
+               ;;
+       bluegene*)
+               os=cnk
+               ;;
+       solaris1 | solaris1.*)
+               os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+               ;;
+       solaris)
+               os=solaris2
+               ;;
+       unixware*)
+               os=sysv4.2uw
+               ;;
+       # The marketing names for NeXT's operating systems were
+       # NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4.  'openstep' is
+       # mapped to 'openstep3', but 'openstep1' and 'openstep2' are
+       # mapped to 'nextstep' and 'nextstep2', consistent with the
+       # treatment of SunOS/Solaris.
+       ns | ns1 | nextstep | nextstep1 | openstep1)
+               os=nextstep
+               ;;
+       ns2 | nextstep2 | openstep2)
+               os=nextstep2
+               ;;
+       ns3 | nextstep3 | openstep | openstep3)
+               os=openstep3
+               ;;
+       ns4 | nextstep4 | openstep4)
+               os=openstep4
+               ;;
+       # es1800 is here to avoid being matched by es* (a different OS)
+       es1800*)
+               os=ose
+               ;;
+       # Some version numbers need modification
+       chorusos*)
+               os=chorusos
+               ;;
+       isc)
+               os=isc2.2
+               ;;
+       sco6)
+               os=sco5v6
+               ;;
+       sco5)
+               os=sco3.2v5
+               ;;
+       sco4)
+               os=sco3.2v4
+               ;;
+       sco3.2.[4-9]*)
+               os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+               ;;
+       sco*v* | scout)
+               # Don't match below
+               ;;
+       sco*)
+               os=sco3.2v2
+               ;;
+       psos*)
+               os=psos
+               ;;
+       qnx*)
+               os=qnx
+               ;;
+       hiux*)
+               os=hiuxwe2
+               ;;
+       lynx*178)
+               os=lynxos178
+               ;;
+       lynx*5)
+               os=lynxos5
+               ;;
+       lynxos*)
+               # don't get caught up in next wildcard
+               ;;
+       lynx*)
+               os=lynxos
+               ;;
+       mac[0-9]*)
+               os=`echo "$os" | sed -e 's|mac|macos|'`
+               ;;
+       opened*)
+               os=openedition
+               ;;
+       os400*)
+               os=os400
+               ;;
+       sunos5*)
+               os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+               ;;
+       sunos6*)
+               os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+               ;;
+       wince*)
+               os=wince
+               ;;
+       utek*)
+               os=bsd
+               vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'`
+               ;;
+       dynix*)
+               os=bsd
+               ;;
+       acis*)
+               os=aos
+               ;;
+       atheos*)
+               os=atheos
+               ;;
+       syllable*)
+               os=syllable
+               ;;
+       386bsd)
+               os=bsd
+               ;;
+       ctix*)
+               os=sysv
+               vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'`
+               ;;
+       uts*)
+               os=sysv
+               ;;
+       nova*)
+               kernel=rtmk
+               os=nova
+               ;;
+       # Preserve the version number of sinix5.
+       sinix5.*)
+               os=`echo "$os" | sed -e 's|sinix|sysv|'`
+               vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+               ;;
+       sinix*)
+               os=sysv4
+               vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+               ;;
+       tpf*)
+               os=tpf
+               ;;
+       triton*)
+               os=sysv3
+               ;;
+       oss*)
+               os=sysv3
+               ;;
+       svr4*)
+               os=sysv4
+               ;;
+       svr3)
+               os=sysv3
+               ;;
+       sysvr4)
+               os=sysv4
+               ;;
+       ose*)
+               os=ose
+               ;;
+       *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+               os=mint
+               ;;
+       dicos*)
+               os=dicos
+               ;;
+       pikeos*)
+               # Until real need of OS specific support for
+               # particular features comes up, bare metal
+               # configurations are quite functional.
+               case $cpu in
+                   arm*)
+                       os=eabi
+                       ;;
+                   *)
+                       os=
+                       obj=elf
+                       ;;
+               esac
+               ;;
+       aout* | coff* | elf* | pe*)
+               # These are machine code file formats, not OSes
+               obj=$os
+               os=
+               ;;
+       *)
+               # No normalization, but not necessarily accepted, that comes below.
+               ;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+obj=
+case $cpu-$vendor in
+       score-*)
+               os=
+               obj=elf
+               ;;
+       spu-*)
+               os=
+               obj=elf
+               ;;
+       *-acorn)
+               os=riscix1.2
+               ;;
+       arm*-rebel)
+               kernel=linux
+               os=gnu
+               ;;
+       arm*-semi)
+               os=
+               obj=aout
+               ;;
+       c4x-* | tic4x-*)
+               os=
+               obj=coff
+               ;;
+       c8051-*)
+               os=
+               obj=elf
+               ;;
+       clipper-intergraph)
+               os=clix
+               ;;
+       hexagon-*)
+               os=
+               obj=elf
+               ;;
+       tic54x-*)
+               os=
+               obj=coff
+               ;;
+       tic55x-*)
+               os=
+               obj=coff
+               ;;
+       tic6x-*)
+               os=
+               obj=coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=tops20
+               ;;
+       pdp11-*)
+               os=none
+               ;;
+       *-dec | vax-*)
+               os=ultrix4.2
+               ;;
+       m68*-apollo)
+               os=domain
+               ;;
+       i386-sun)
+               os=sunos4.0.2
+               ;;
+       m68000-sun)
+               os=sunos3
+               ;;
+       m68*-cisco)
+               os=
+               obj=aout
+               ;;
+       mep-*)
+               os=
+               obj=elf
+               ;;
+       # The -sgi and -siemens entries must be before the mips- entry
+       # or we get the wrong os.
+       *-sgi)
+               os=irix
+               ;;
+       *-siemens)
+               os=sysv4
+               ;;
+       mips*-cisco)
+               os=
+               obj=elf
+               ;;
+       mips*-*|nanomips*-*)
+               os=
+               obj=elf
+               ;;
+       or32-*)
+               os=
+               obj=coff
+               ;;
+       # This must be before the sparc-* entry or we get the wrong os.
+       *-tti)
+               os=sysv3
+               ;;
+       sparc-* | *-sun)
+               os=sunos4.1.1
+               ;;
+       pru-*)
+               os=
+               obj=elf
+               ;;
+       *-be)
+               os=beos
+               ;;
+       *-ibm)
+               os=aix
+               ;;
+       *-knuth)
+               os=mmixware
+               ;;
+       *-wec)
+               os=proelf
+               ;;
+       *-winbond)
+               os=proelf
+               ;;
+       *-oki)
+               os=proelf
+               ;;
+       *-hp)
+               os=hpux
+               ;;
+       *-hitachi)
+               os=hiuxwe2
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=sysv
+               ;;
+       *-cbm)
+               os=amigaos
+               ;;
+       *-dg)
+               os=dgux
+               ;;
+       *-dolphin)
+               os=sysv3
+               ;;
+       m68k-ccur)
+               os=rtu
+               ;;
+       m88k-omron*)
+               os=luna
+               ;;
+       *-next)
+               os=nextstep
+               ;;
+       *-sequent)
+               os=ptx
+               ;;
+       *-crds)
+               os=unos
+               ;;
+       *-ns)
+               os=genix
+               ;;
+       i370-*)
+               os=mvs
+               ;;
+       *-gould)
+               os=sysv
+               ;;
+       *-highlevel)
+               os=bsd
+               ;;
+       *-encore)
+               os=bsd
+               ;;
+       *-masscomp)
+               os=rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=uxpv
+               ;;
+       *-rom68k)
+               os=
+               obj=coff
+               ;;
+       *-*bug)
+               os=
+               obj=coff
+               ;;
+       *-apple)
+               os=macos
+               ;;
+       *-atari*)
+               os=mint
+               ;;
+       *-wrs)
+               os=vxworks
+               ;;
+       *)
+               os=none
+               ;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ).
+
+case $os in
+       # Sometimes we do "kernel-libc", so those need to count as OSes.
+       llvm* | musl* | newlib* | relibc* | uclibc*)
+               ;;
+       # Likewise for "kernel-abi"
+       eabi* | gnueabi*)
+               ;;
+       # VxWorks passes extra cpu info in the 4th filed.
+       simlinux | simwindows | spe)
+               ;;
+       # See `case $cpu-$os` validation below
+       ghcjs)
+               ;;
+       # Now accept the basic system types.
+       # Each alternative MUST end in a * to match a version number.
+         abug \
+       | aix* \
+       | amdhsa* \
+       | amigados* \
+       | amigaos* \
+       | android* \
+       | aof* \
+       | aos* \
+       | aros* \
+       | atheos* \
+       | auroraux* \
+       | aux* \
+       | beos* \
+       | bitrig* \
+       | bme* \
+       | bosx* \
+       | bsd* \
+       | cegcc* \
+       | chorusos* \
+       | chorusrdb* \
+       | clix* \
+       | cloudabi* \
+       | cnk* \
+       | conix* \
+       | cos* \
+       | cxux* \
+       | cygwin* \
+       | darwin* \
+       | dgux* \
+       | dicos* \
+       | dnix* \
+       | domain* \
+       | dragonfly* \
+       | drops* \
+       | ebmon* \
+       | ecoff* \
+       | ekkobsd* \
+       | emscripten* \
+       | emx* \
+       | es* \
+       | fiwix* \
+       | freebsd* \
+       | fuchsia* \
+       | genix* \
+       | genode* \
+       | glidix* \
+       | gnu* \
+       | go32* \
+       | haiku* \
+       | hcos* \
+       | hiux* \
+       | hms* \
+       | hpux* \
+       | ieee* \
+       | interix* \
+       | ios* \
+       | iris* \
+       | irix* \
+       | ironclad* \
+       | isc* \
+       | its* \
+       | l4re* \
+       | libertybsd* \
+       | lites* \
+       | lnews* \
+       | luna* \
+       | lynxos* \
+       | mach* \
+       | macos* \
+       | magic* \
+       | mbr* \
+       | midipix* \
+       | midnightbsd* \
+       | mingw32* \
+       | mingw64* \
+       | minix* \
+       | mint* \
+       | mirbsd* \
+       | mks* \
+       | mlibc* \
+       | mmixware* \
+       | mon960* \
+       | morphos* \
+       | moss* \
+       | moxiebox* \
+       | mpeix* \
+       | mpw* \
+       | msdos* \
+       | msys* \
+       | mvs* \
+       | nacl* \
+       | netbsd* \
+       | netware* \
+       | newsos* \
+       | nextstep* \
+       | nindy* \
+       | nonstopux* \
+       | nova* \
+       | nsk* \
+       | nucleus* \
+       | nx6 \
+       | nx7 \
+       | oabi* \
+       | ohos* \
+       | onefs* \
+       | openbsd* \
+       | openedition* \
+       | openstep* \
+       | os108* \
+       | os2* \
+       | os400* \
+       | os68k* \
+       | os9* \
+       | ose* \
+       | osf* \
+       | oskit* \
+       | osx* \
+       | palmos* \
+       | phoenix* \
+       | plan9* \
+       | powermax* \
+       | powerunix* \
+       | proelf* \
+       | psos* \
+       | psp* \
+       | ptx* \
+       | pw32* \
+       | qnx* \
+       | rdos* \
+       | redox* \
+       | rhapsody* \
+       | riscix* \
+       | riscos* \
+       | rtems* \
+       | rtmk* \
+       | rtu* \
+       | scout* \
+       | secbsd* \
+       | sei* \
+       | serenity* \
+       | sim* \
+       | skyos* \
+       | solaris* \
+       | solidbsd* \
+       | sortix* \
+       | storm-chaos* \
+       | sunos \
+       | sunos[34]* \
+       | superux* \
+       | syllable* \
+       | sym* \
+       | sysv* \
+       | tenex* \
+       | tirtos* \
+       | toppers* \
+       | tops10* \
+       | tops20* \
+       | tpf* \
+       | tvos* \
+       | twizzler* \
+       | uclinux* \
+       | udi* \
+       | udk* \
+       | ultrix* \
+       | unicos* \
+       | uniplus* \
+       | unleashed* \
+       | unos* \
+       | uwin* \
+       | uxpv* \
+       | v88r* \
+       |*vms* \
+       | vos* \
+       | vsta* \
+       | vxsim* \
+       | vxworks* \
+       | wasi* \
+       | watchos* \
+       | wince* \
+       | windiss* \
+       | windows* \
+       | winnt* \
+       | xenix* \
+       | xray* \
+       | zephyr* \
+       | zvmoe* )
+               ;;
+       # This one is extra strict with allowed versions
+       sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               ;;
+       # This refers to builds using the UEFI calling convention
+       # (which depends on the architecture) and PE file format.
+       # Note that this is both a different calling convention and
+       # different file format than that of GNU-EFI
+       # (x86_64-w64-mingw32).
+       uefi)
+               ;;
+       none)
+               ;;
+       kernel* | msvc* )
+               # Restricted further below
+               ;;
+       '')
+               if test x"$obj" = x
+               then
+                       echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2
+               fi
+               ;;
+       *)
+               echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
+               exit 1
+               ;;
+esac
+
+case $obj in
+       aout* | coff* | elf* | pe*)
+               ;;
+       '')
+               # empty is fine
+               ;;
+       *)
+               echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we handle the constraint that a (synthetic) cpu and os are
+# valid only in combination with each other and nowhere else.
+case $cpu-$os in
+       # The "javascript-unknown-ghcjs" triple is used by GHC; we
+       # accept it here in order to tolerate that, but reject any
+       # variations.
+       javascript-ghcjs)
+               ;;
+       javascript-* | *-ghcjs)
+               echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2
+               exit 1
+               ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os-$obj in
+       linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
+                   | linux-mlibc*- | linux-musl*- | linux-newlib*- \
+                   | linux-relibc*- | linux-uclibc*- | linux-ohos*- )
+               ;;
+       uclinux-uclibc*- | uclinux-gnu*- )
+               ;;
+       managarm-mlibc*- | managarm-kernel*- )
+               ;;
+       windows*-msvc*-)
+               ;;
+       -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \
+                   | -uclibc*- )
+               # These are just libc implementations, not actual OSes, and thus
+               # require a kernel.
+               echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
+               exit 1
+               ;;
+       -kernel*- )
+               echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
+               exit 1
+               ;;
+       *-kernel*- )
+               echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
+               exit 1
+               ;;
+       *-msvc*- )
+               echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2
+               exit 1
+               ;;
+       kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-)
+               ;;
+       vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-)
+               ;;
+       nto-qnx*-)
+               ;;
+       os2-emx-)
+               ;;
+       rtmk-nova-)
+               ;;
+       *-eabi*- | *-gnueabi*-)
+               ;;
+       none--*)
+               # None (no kernel, i.e. freestanding / bare metal),
+               # can be paired with an machine code file format
+               ;;
+       -*-)
+               # Blank kernel with real OS is always fine.
+               ;;
+       --*)
+               # Blank kernel and OS with real machine code file format is always fine.
+               ;;
+       *-*-*)
+               echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+       unknown)
+               case $cpu-$os in
+                       *-riscix*)
+                               vendor=acorn
+                               ;;
+                       *-sunos* | *-solaris*)
+                               vendor=sun
+                               ;;
+                       *-cnk* | *-aix*)
+                               vendor=ibm
+                               ;;
+                       *-beos*)
+                               vendor=be
+                               ;;
+                       *-hpux*)
+                               vendor=hp
+                               ;;
+                       *-mpeix*)
+                               vendor=hp
+                               ;;
+                       *-hiux*)
+                               vendor=hitachi
+                               ;;
+                       *-unos*)
+                               vendor=crds
+                               ;;
+                       *-dgux*)
+                               vendor=dg
+                               ;;
+                       *-luna*)
+                               vendor=omron
+                               ;;
+                       *-genix*)
+                               vendor=ns
+                               ;;
+                       *-clix*)
+                               vendor=intergraph
+                               ;;
+                       *-mvs* | *-opened*)
+                               vendor=ibm
+                               ;;
+                       *-os400*)
+                               vendor=ibm
+                               ;;
+                       s390-* | s390x-*)
+                               vendor=ibm
+                               ;;
+                       *-ptx*)
+                               vendor=sequent
+                               ;;
+                       *-tpf*)
+                               vendor=ibm
+                               ;;
+                       *-vxsim* | *-vxworks* | *-windiss*)
+                               vendor=wrs
+                               ;;
+                       *-aux*)
+                               vendor=apple
+                               ;;
+                       *-hms*)
+                               vendor=hitachi
+                               ;;
+                       *-mpw* | *-macos*)
+                               vendor=apple
+                               ;;
+                       *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       *-vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               ;;
+esac
+
+echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..ad0b243
--- /dev/null
+++ b/configure
@@ -0,0 +1,6840 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for mpdecimal 4.0.1.
+#
+# Report bugs to <mpdecimal-bugs@bytereef.org>.
+#
+#
+# Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: mpdecimal-bugs@bytereef.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='mpdecimal'
+PACKAGE_TARNAME='mpdecimal'
+PACKAGE_VERSION='4.0.1'
+PACKAGE_STRING='mpdecimal 4.0.1'
+PACKAGE_BUGREPORT='mpdecimal-bugs@bytereef.org'
+PACKAGE_URL='https://www.bytereef.org/mpdecimal/index.html'
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+CONFIGURE_LDXXFLAGS
+CONFIGURE_CXXFLAGS
+CONFIGURE_LDFLAGS
+CONFIGURE_CFLAGS
+CONFIGURE_LIBMPDEC_CFLAGS
+MPD_PREC
+MPD_GNU99
+MPD_HEADER_CONFIG
+OBJECT_SUFFIX
+EXPORTSYMS
+INSTALL
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+EGREP
+GREP
+CPP
+MPD_PUSE
+MPD_PGEN
+MPD_PTHREAD
+FILTER_FOR_STATIC
+LDXX
+LD
+LDXXFLAGS
+MACHINE
+RANLIB
+AR
+ac_ct_CXX
+CXXFLAGS
+CXX
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+ENABLE_PC
+ENABLE_DOC
+ENABLE_SHARED
+ENABLE_STATIC
+ENABLE_CXX
+ENABLE_MINGW
+LIBIMPORT_CXX
+LIBSHARED_CXX
+LIBSONAME_CXX
+LIBNAME_CXX
+LIBSTATIC_CXX
+LINK_DYNAMIC
+LINK_STATIC
+LIBSHARED_USE_AR
+LIBIMPORT
+LIBSHARED
+LIBSONAME
+LIBNAME
+LIBSTATIC
+FPIC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_cxx
+enable_static
+enable_shared
+enable_doc
+enable_pc
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CXX
+CXXFLAGS
+CCC
+MACHINE
+LDXXFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures mpdecimal 4.0.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/mpdecimal]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of mpdecimal 4.0.1:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-cxx            enable building libmpdec++ (default is yes)
+  --enable-static         enable building static libraries (default is yes)
+  --enable-shared         enable building shared libraries (default is yes)
+  --enable-doc            enable installing the documentation (default is yes)
+  --enable-pc             enable installing the pkg-config files (default is
+                          yes)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  MACHINE     force configuration: x64, uint128, ansi64, ppro, ansi32,
+              ansi-legacy, universal
+  LDXXFLAGS   C++ linker flags
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <mpdecimal-bugs@bytereef.org>.
+mpdecimal home page: <https://www.bytereef.org/mpdecimal/index.html>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+mpdecimal configure 4.0.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+
+
+Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ------------------------------------------ ##
+## Report this to mpdecimal-bugs@bytereef.org ##
+## ------------------------------------------ ##"
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_find_intX_t LINENO BITS VAR
+# -----------------------------------
+# Finds a signed integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_intX_t ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5
+$as_echo_n "checking for int$2_t... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+     # Order is important - never check a type that is potentially smaller
+     # than half of the expected target width.
+     for ac_type in int$2_t 'int' 'long int' \
+        'long long int' 'short int' 'signed char'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+            enum { N = $2 / 2 - 1 };
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+               enum { N = $2 / 2 - 1 };
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1)
+                < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  case $ac_type in #(
+  int$2_t) :
+    eval "$3=yes" ;; #(
+  *) :
+    eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       if eval test \"x\$"$3"\" = x"no"; then :
+
+else
+  break
+fi
+     done
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_intX_t
+
+# ac_fn_c_find_uintX_t LINENO BITS VAR
+# ------------------------------------
+# Finds an unsigned integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_uintX_t ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5
+$as_echo_n "checking for uint$2_t... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+     # Order is important - never check a type that is potentially smaller
+     # than half of the expected target width.
+     for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \
+        'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  case $ac_type in #(
+  uint$2_t) :
+    eval "$3=yes" ;; #(
+  *) :
+    eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       if eval test \"x\$"$3"\" = x"no"; then :
+
+else
+  break
+fi
+     done
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_uintX_t
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+                       if test $ac_lo -le $ac_mid; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+                       if test $ac_mid -le $ac_hi; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by mpdecimal $as_me 4.0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+ac_config_files="$ac_config_files Makefile libmpdec/Makefile libmpdec/mpdecimal.h libmpdec/.pc/libmpdec.pc libmpdec++/Makefile libmpdec++/.pc/libmpdec++.pc tests/Makefile tests++/Makefile"
+
+
+# Link files for out-of-tree builds:
+ac_config_links="$ac_config_links CHANGELOG.txt:CHANGELOG.txt COPYRIGHT.txt:COPYRIGHT.txt INSTALL.txt:INSTALL.txt Makefile.in:Makefile.in README.txt:README.txt config.guess:config.guess config.h.in:config.h.in config.sub:config.sub configure:configure configure.ac:configure.ac install-sh:install-sh doc/COPYRIGHT.txt:doc/COPYRIGHT.txt doc/libmpdec.3:doc/libmpdec.3 doc/libmpdec++.3:doc/libmpdec++.3 doc/mpdecimal.3:doc/mpdecimal.3 libmpdec/Makefile.in:libmpdec/Makefile.in libmpdec/Makefile.vc:libmpdec/Makefile.vc libmpdec/README.txt:libmpdec/README.txt libmpdec/basearith.c:libmpdec/basearith.c libmpdec/basearith.h:libmpdec/basearith.h libmpdec/bench.c:libmpdec/bench.c libmpdec/bench_full.c:libmpdec/bench_full.c libmpdec/bits.h:libmpdec/bits.h libmpdec/constants.c:libmpdec/constants.c libmpdec/constants.h:libmpdec/constants.h libmpdec/context.c:libmpdec/context.c libmpdec/convolute.c:libmpdec/convolute.c libmpdec/convolute.h:libmpdec/convolute.h libmpdec/crt.c:libmpdec/crt.c libmpdec/crt.h:libmpdec/crt.h libmpdec/difradix2.c:libmpdec/difradix2.c libmpdec/difradix2.h:libmpdec/difradix2.h libmpdec/fnt.c:libmpdec/fnt.c libmpdec/fnt.h:libmpdec/fnt.h libmpdec/fourstep.c:libmpdec/fourstep.c libmpdec/fourstep.h:libmpdec/fourstep.h libmpdec/io.c:libmpdec/io.c libmpdec/io.h:libmpdec/io.h libmpdec/mpalloc.c:libmpdec/mpalloc.c libmpdec/mpalloc.h:libmpdec/mpalloc.h libmpdec/mpdecimal.c:libmpdec/mpdecimal.c libmpdec/mpdecimal32vc.h:libmpdec/mpdecimal32vc.h libmpdec/mpdecimal64vc.h:libmpdec/mpdecimal64vc.h libmpdec/mpdecimal.h.in:libmpdec/mpdecimal.h.in libmpdec/mpsignal.c:libmpdec/mpsignal.c libmpdec/numbertheory.c:libmpdec/numbertheory.c libmpdec/numbertheory.h:libmpdec/numbertheory.h libmpdec/sixstep.c:libmpdec/sixstep.c libmpdec/sixstep.h:libmpdec/sixstep.h libmpdec/transpose.c:libmpdec/transpose.c libmpdec/transpose.h:libmpdec/transpose.h libmpdec/typearith.h:libmpdec/typearith.h libmpdec/umodarith.h:libmpdec/umodarith.h libmpdec/vcdiv64.asm:libmpdec/vcdiv64.asm libmpdec/examples/README.txt:libmpdec/examples/README.txt libmpdec/examples/compare.c:libmpdec/examples/compare.c libmpdec/examples/div.c:libmpdec/examples/div.c libmpdec/examples/divmod.c:libmpdec/examples/divmod.c libmpdec/examples/multiply.c:libmpdec/examples/multiply.c libmpdec/examples/pow.c:libmpdec/examples/pow.c libmpdec/examples/powmod.c:libmpdec/examples/powmod.c libmpdec/examples/shift.c:libmpdec/examples/shift.c libmpdec/examples/sqrt.c:libmpdec/examples/sqrt.c libmpdec/.objs/README.txt:libmpdec/.objs/README.txt libmpdec/.objs/symbols32.exp:libmpdec/.objs/symbols32.exp libmpdec/.objs/symbols64.exp:libmpdec/.objs/symbols64.exp libmpdec/.pc/libmpdec.pc.in:libmpdec/.pc/libmpdec.pc.in libmpdec/.profile/train.sh:libmpdec/.profile/train.sh libmpdec++/Makefile.in:libmpdec++/Makefile.in libmpdec++/Makefile.vc:libmpdec++/Makefile.vc libmpdec++/bench.cc:libmpdec++/bench.cc libmpdec++/bench_full.cc:libmpdec++/bench_full.cc libmpdec++/decimal.cc:libmpdec++/decimal.cc libmpdec++/decimal.hh:libmpdec++/decimal.hh libmpdec++/examples/factorial.cc:libmpdec++/examples/factorial.cc libmpdec++/examples/pi.cc:libmpdec++/examples/pi.cc libmpdec++/.objs/README.txt:libmpdec++/.objs/README.txt libmpdec++/.pc/libmpdec++.pc.in:libmpdec++/.pc/libmpdec++.pc.in libmpdec++/.profile/train.sh:libmpdec++/.profile/train.sh tests/Makefile.in:tests/Makefile.in tests/Makefile.vc:tests/Makefile.vc tests/README.txt:tests/README.txt tests/additional.decTest:tests/additional.decTest tests/gettests.bat:tests/gettests.bat tests/gettests.sh:tests/gettests.sh tests/official.decTest:tests/official.decTest tests/runshort.sh:tests/runshort.sh tests/runshort_alloc.sh:tests/runshort_alloc.sh tests/runtest.c:tests/runtest.c tests/test.c:tests/test.c tests/test.h:tests/test.h tests/vctest.h:tests/vctest.h tests/testdata_dist/baseconv.decTest:tests/testdata_dist/baseconv.decTest tests/testdata_dist/binop_eq.decTest:tests/testdata_dist/binop_eq.decTest tests/testdata_dist/cov.decTest:tests/testdata_dist/cov.decTest tests/testdata_dist/divmod.decTest:tests/testdata_dist/divmod.decTest tests/testdata_dist/divmod_eq.decTest:tests/testdata_dist/divmod_eq.decTest tests/testdata_dist/extra.decTest:tests/testdata_dist/extra.decTest tests/testdata_dist/fma_eq.decTest:tests/testdata_dist/fma_eq.decTest tests/testdata_dist/format.decTest:tests/testdata_dist/format.decTest tests/testdata_dist/getint.decTest:tests/testdata_dist/getint.decTest tests/testdata_dist/invroot.decTest:tests/testdata_dist/invroot.decTest tests/testdata_dist/largeint.decTest:tests/testdata_dist/largeint.decTest tests/testdata_dist/maxprec.decTest:tests/testdata_dist/maxprec.decTest tests/testdata_dist/powmod.decTest:tests/testdata_dist/powmod.decTest tests/testdata_dist/powmod_eq.decTest:tests/testdata_dist/powmod_eq.decTest tests/testdata_dist/shiftlr.decTest:tests/testdata_dist/shiftlr.decTest tests/testdata_dist/testruntest.decTest:tests/testdata_dist/testruntest.decTest tests++/Makefile.in:tests++/Makefile.in tests++/Makefile.vc:tests++/Makefile.vc tests++/README.txt:tests++/README.txt tests++/additional.topTest:tests++/additional.topTest tests++/apitest.cc:tests++/apitest.cc tests++/gettests.bat:tests++/gettests.bat tests++/gettests.sh:tests++/gettests.sh tests++/official.topTest:tests++/official.topTest tests++/runshort.sh:tests++/runshort.sh tests++/runshort_alloc.sh:tests++/runshort_alloc.sh tests++/runtest.cc:tests++/runtest.cc tests++/test.cc:tests++/test.cc tests++/test.hh:tests++/test.hh tests++/vctest.hh:tests++/vctest.hh"
+
+
+# ==============================================================================
+#                               MPD_HEADER_CONFIG
+# ==============================================================================
+
+CONFIG_64="
+/* ABI: 64-bit */
+#define MPD_CONFIG_64 1
+
+#ifdef MPD_CONFIG_32
+  #error \"cannot use MPD_CONFIG_32 with 64-bit header.\"
+#endif
+
+#ifdef CONFIG_32
+  #error \"cannot use CONFIG_32 with 64-bit header.\"
+#endif"
+
+CONFIG_32="
+/* ABI: 32-bit */
+#define MPD_CONFIG_32 1
+
+#ifdef MPD_CONFIG_64
+  #error \"cannot use MPD_CONFIG_64 with 32-bit header.\"
+#endif
+
+#ifdef CONFIG_64
+  #error \"cannot use CONFIG_64 with 32-bit header.\"
+#endif"
+
+CONFIG_32_LEGACY="
+/* ABI: 32-bit */
+#define MPD_CONFIG_32 1
+
+/* libmpdec was compiled without support for int64_t */
+#define MPD_LEGACY_COMPILER 1
+
+#ifdef MPD_CONFIG_64
+  #error \"cannot use MPD_CONFIG_64 with 32-bit header.\"
+#endif
+
+#ifdef CONFIG_64
+  #error \"cannot use CONFIG_64 with 32-bit header.\"
+#endif"
+
+CONFIG_UNIVERSAL="
+/* Mac OS X: support for building universal binaries */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with universal header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with universal header.\"
+#endif
+
+#if defined(__ppc__)
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#elif defined(__ppc64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#elif defined(__i386__)
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#elif defined(__x86_64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ASM
+  #endif
+#elif defined(__arm64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#else
+  #error \"unknown architecture for universal build.\"
+#endif"
+
+CONFIG_UNIVERSAL_AIX_UINT128="
+/* AIX: support for multilib */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(__64BIT__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+    #define HAVE_UINT128_T
+  #endif
+#else
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#endif"
+
+CONFIG_UNIVERSAL_AIX_ANSI64="
+/* AIX: support for multilib */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(__64BIT__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#else
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#endif"
+
+# ==============================================================================
+#                         Detect build and host systems
+# ==============================================================================
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+# ==============================================================================
+#                              Select library names
+# ==============================================================================
+
+# MinGW/Cygwin/MSYS2 support:
+ENABLE_MINGW="no"
+
+# Set library names and linker flags:
+FPIC="-fPIC"
+LIBSTATIC=libmpdec.a
+LIBSHARED_USE_AR="no"
+LIBIMPORT=
+LINK_STATIC=
+LINK_DYNAMIC=
+case $host in
+  *-*-darwin*)
+    LIBNAME="libmpdec.dylib"
+    LIBSONAME="libmpdec.4.dylib"
+    LIBSHARED="libmpdec.4.0.1.dylib"
+    CONFIGURE_LDFLAGS="-dynamiclib $FPIC -install_name \$(libdir)/$LIBSONAME -compatibility_version 4.0 -current_version 4.0.1"
+    ;;
+  *-*-aix*)
+    LIBNAME=
+    LIBSONAME=
+    LIBSHARED="shr.o"
+    LIBSHARED_USE_AR="yes"
+    CONFIGURE_LDFLAGS="-shared $FPIC -Wl,-bnoentry -Wl,-bE:.objs/symbols.exp"
+    LINK_STATIC="-Wl,-bstatic"
+    LINK_DYNAMIC="-Wl,-bshared"
+    ;;
+  *-*-mingw*)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="libmpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *-*-cygwin*)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="cygmpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *-*-msys)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="msys-mpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *)
+    LIBNAME="libmpdec.so"
+    LIBSONAME="libmpdec.so.4"
+    LIBSHARED="libmpdec.so.4.0.1"
+    CONFIGURE_LDFLAGS="-shared $FPIC -Wl,-soname,$LIBSONAME"
+    ;;
+esac
+
+LIBSTATIC_CXX=libmpdec++.a
+LIBIMPORT_CXX=
+case $host in
+  *-*-darwin*)
+    LIBNAME_CXX="libmpdec++.dylib"
+    LIBSONAME_CXX="libmpdec++.4.dylib"
+    LIBSHARED_CXX="libmpdec++.4.0.1.dylib"
+    CONFIGURE_LDXXFLAGS="-dynamiclib $FPIC -install_name \$(libdir)/$LIBSONAME_CXX -compatibility_version 4.0 -current_version 4.0.1"
+    ;;
+  *-*-aix*)
+    LIBNAME_CXX=
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="shr.o"
+    CONFIGURE_LDXXFLAGS="-shared $FPIC -Wl,-bnoentry -Wl,-bE:.objs/symbols.exp"
+    ;;
+  *-*-mingw*)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="libmpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *-*-cygwin*)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="cygmpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *-*-msys)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="msys-mpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *)
+    LIBNAME_CXX="libmpdec++.so"
+    LIBSONAME_CXX="libmpdec++.so.4"
+    LIBSHARED_CXX="libmpdec++.so.4.0.1"
+    CONFIGURE_LDXXFLAGS="-shared $FPIC -Wl,-soname,$LIBSONAME_CXX"
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# ==============================================================================
+#                                  User options
+# ==============================================================================
+
+# Check whether to build libmpdec++:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-cxx" >&5
+$as_echo_n "checking for --enable-cxx... " >&6; }
+# Check whether --enable-cxx was given.
+if test "${enable_cxx+set}" = set; then :
+  enableval=$enable_cxx;
+fi
+
+
+ENABLE_CXX="$enable_cxx"
+if test -z "$ENABLE_CXX"
+then
+  if test x"$MACHINE" = x"ansi-legacy"; then
+    ENABLE_CXX="no"
+  else
+    ENABLE_CXX="yes"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_CXX" >&5
+$as_echo "$ENABLE_CXX" >&6; }
+
+# Check whether to build the static libraries:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-static" >&5
+$as_echo_n "checking for --enable-static... " >&6; }
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+  enableval=$enable_static;
+fi
+
+
+ENABLE_STATIC="$enable_static"
+if test -z "$ENABLE_STATIC"
+then
+    ENABLE_STATIC="yes"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_STATIC" >&5
+$as_echo "$ENABLE_STATIC" >&6; }
+
+# Check whether to build the shared libraries:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-shared" >&5
+$as_echo_n "checking for --enable-shared... " >&6; }
+# Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+  enableval=$enable_shared;
+fi
+
+
+ENABLE_SHARED="$enable_shared"
+if test -z "$ENABLE_SHARED"
+then
+  ENABLE_SHARED="yes"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_SHARED" >&5
+$as_echo "$ENABLE_SHARED" >&6; }
+
+# Check whether at least one library is enabled:
+if test x"$ENABLE_STATIC" = x"no" -a x"$ENABLE_SHARED" = x"no"
+then
+  as_fn_error $? "at least one of --enable-static or --enable-shared must be set" "$LINENO" 5
+fi
+
+# AIX: both static and shared libraries must be enabled:
+case $host in
+  *-*-aix*)
+    if test x"$ENABLE_STATIC" = x"no" -o x"$ENABLE_SHARED" = x"no"
+    then
+      as_fn_error $? "the AIX build requires --enable-static and --enable-shared" "$LINENO" 5
+    fi
+    ;;
+esac
+
+# Check whether to install the documentation:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-doc" >&5
+$as_echo_n "checking for --enable-doc... " >&6; }
+# Check whether --enable-doc was given.
+if test "${enable_doc+set}" = set; then :
+  enableval=$enable_doc;
+fi
+
+
+ENABLE_DOC="$enable_doc"
+if test -z "$ENABLE_DOC"
+then
+  ENABLE_DOC="yes"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_DOC" >&5
+$as_echo "$ENABLE_DOC" >&6; }
+
+# Check whether to install the pkg-config files:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-pc" >&5
+$as_echo_n "checking for --enable-pc... " >&6; }
+# Check whether --enable-pc was given.
+if test "${enable_pc+set}" = set; then :
+  enableval=$enable_pc;
+fi
+
+
+ENABLE_PC="$enable_pc"
+if test -z "$ENABLE_PC"
+then
+  ENABLE_PC="yes"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_PC" >&5
+$as_echo "$ENABLE_PC" >&6; }
+
+
+
+
+
+
+
+# ==============================================================================
+#                              Compilers and flags
+# ==============================================================================
+
+# Save initial compiler flags:
+INITIAL_CFLAGS="$CFLAGS"
+INITIAL_CXXFLAGS="$CXXFLAGS"
+INITIAL_LDFLAGS="$LDFLAGS"
+INITIAL_LDXXFLAGS="$LDXXFLAGS"
+
+# Rename compiler in case $CC=cc:
+case $CC in
+  cc)
+    case $build in
+      *-*-freebsd* | *-*-openbsd* | *-*-darwin*)
+        CC=clang
+        ;;
+      *-*-netbsd*)
+        CC=gcc
+        ;;
+      *-*-solaris* | *-*-sunos*)
+        CC=suncc
+        ;;
+    esac
+    ;;
+esac
+
+case $CXX in
+  c++)
+    case $build in
+      *-*-freebsd* | *-*-openbsd* | *-*-darwin*)
+        CXX=clang++
+        ;;
+      *-*-netbsd*)
+        CXX=g++
+        ;;
+    esac
+    ;;
+esac
+
+# Language and compiler:
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Preference of tested compilers:
+case $host in
+  *-*-aix*)
+    PREFERRED_CC="ibm-clang_r ibm-clang gcc xlc_r xlc cc"
+    PREFERRED_CXX="ibm-clang++_r g++ c++"
+    ;;
+  *-*-freebsd* | *-*-openbsd*)
+    PREFERRED_CC="clang gcc cc"
+    PREFERRED_CXX="clang++ g++ c++"
+    ;;
+  *)
+    PREFERRED_CC="gcc icc clang icx suncc ccomp cc"
+    PREFERRED_CXX="g++ icpc clang++ icpx c++"
+    ;;
+esac
+
+# Find CC:
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $PREFERRED_CC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in $PREFERRED_CC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+
+# These compilers do not fully support C++11.  Disable CXX to avoid mixing
+# different C and C++ compilers:
+case $CC in
+  *xlc* | suncc | ccomp)
+    ENABLE_CXX="no"
+    ;;
+esac
+
+# Find CXX:
+if test x"$ENABLE_CXX" = x"yes"; then
+  saved_cxxflags="$CXXFLAGS"
+  saved_ldxxflags="$LDXXFLAGS"
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in $PREFERRED_CXX
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $PREFERRED_CXX
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+        CXXFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+  CXXFLAGS="$saved_cxxflags"
+  LDXXFLAGS="$saved_ldxxflags"
+fi
+
+# Check availability of -O2:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -O2" >&5
+$as_echo_n "checking for -O2... " >&6; }
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+CFLAGS="-O2"
+LDFLAGS=
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  have_O2=yes
+else
+  have_O2=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_O2" >&5
+$as_echo "$have_O2" >&6; }
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+
+# Find ar and ranlib:
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="${ac_tool_prefix}ar"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+  ac_ct_AR=$AR
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="ar"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_AR" = x; then
+    AR="ar"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+else
+  AR="$ac_cv_prog_AR"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+# Force explicit configuration.  This section must be here because the size
+# checks in the next section require the correct CFLAGS.
+
+
+M64=
+M32=
+if test -n "$MACHINE"; then
+  case $host in
+    *-*-aix*)
+      case $CC in
+        *xlc*)
+          M64="-q64"
+          M32="-q32"
+          ;;
+        *gcc*)
+          M64="-maix64"
+          M32="-maix32"
+          ;;
+        *)
+          M64="-m64"
+          M32="-m32"
+          ;;
+      esac
+      ;;
+    *)
+      M64="-m64"
+      M32="-m32"
+      ;;
+  esac
+
+  case "$MACHINE" in
+    x64 | uint128 | ansi64)
+      CFLAGS="$CFLAGS $M64"
+      CXXFLAGS="$CXXFLAGS $M64"
+      LDFLAGS="$LDFLAGS $M64"
+      LDXXFLAGS="$LDXXFLAGS $M64"
+      ;;
+    ppro | ansi32 | ansi-legacy)
+      CFLAGS="$CFLAGS $M32"
+      CXXFLAGS="$CXXFLAGS $M32"
+      LDFLAGS="$LDFLAGS $M32"
+      LDXXFLAGS="$LDXXFLAGS $M64"
+      ;;
+    universal)
+      :
+      ;;
+    *)
+      as_fn_error $? "invalid MACHINE variable: $MACHINE" "$LINENO" 5
+      ;;
+  esac
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the toolchain supports the chosen ABI" >&5
+$as_echo_n "checking whether the toolchain supports the chosen ABI... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  machine_supported=yes
+else
+  machine_supported=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $machine_supported" >&5
+$as_echo "$machine_supported" >&6; }
+  if test "$machine_supported" = no; then
+    as_fn_error $? "toolchain cannot handle MACHINE=$MACHINE" "$LINENO" 5
+  fi
+fi
+
+# Compiler dependent settings:
+MPD_PTHREAD=
+MPD_WARN=
+MPD_WARNXX=
+MPD_OPT="-O2"
+MPD_PGEN=
+MPD_PUSE=
+CONFIG_UNIVERSAL_AIX=
+FILTER_FOR_STATIC=
+case $CC in
+  *gcc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_UINT128
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic"
+    MPD_WARNXX="-Wall -Wextra -std=c++11 -pedantic"
+    MPD_OPT="-DNDEBUG -O2"
+    MPD_PGEN="-fprofile-generate -fprofile-values"
+    MPD_PUSE="-fprofile-use -freorder-blocks"
+    FILTER_FOR_STATIC="-flto% -ffat-lto-objects"
+    ;;
+  *icc*)
+    AR=xiar
+    CXX=icpc
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic -diag-disable=11074,11076"
+    MPD_WARNXX="-Wall -Wextra -std=c++11 -pedantic -diag-disable=11074,11076"
+    MPD_OPT="-DNDEBUG -O2"
+    MPD_PGEN="-wd11505 -prof-gen"
+    MPD_PUSE="-wd11505 -prof-use"
+    ;;
+  *clang* | *icx* | *emcc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_UINT128
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic"
+    MPD_WARNXX="-Wall -Wextra -Wexit-time-destructors -std=c++11 -pedantic"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *xlc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_ANSI64
+    CONFIGURE_LDFLAGS="-qmkshrobj -bnoentry -bE:.objs/symbols.exp"
+    LINK_STATIC="-bstatic"
+    LINK_DYNAMIC="-bshared"
+    MPD_PTHREAD="-qthreaded -D_THREAD_SAFE"
+    MPD_WARN="-qlanglvl=stdc99"
+    MPD_OPT="-DNDEBUG -O2 -qalias=ansi -qmaxmem=-1"
+    ;;
+  *suncc*)
+    MPD_WARN="-xc99"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *ccomp*)
+    ENABLE_SHARED=no
+    LINK_STATIC="-Wl,-znoexecstack"
+    MPD_WARN="-Wall -Wno-unknown-pragmas -std=c99 -fstruct-passing"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *)
+    ;;
+esac
+
+
+
+# These variables are set unconditionally because CONFIGURE_LDFLAGS etc.
+# require the compiler frontends.  We leave them in the Makefiles in case
+# someone needs to edit the Makefiles manually.
+LD="$CC"
+LDXX="$CXX"
+
+
+
+
+
+
+
+
+
+
+# ==============================================================================
+#                   Headers, types, assembly, install program
+# ==============================================================================
+
+# Check for header files:
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in pthread.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+# Type availability checks:
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t"
+case $ac_cv_c_int32_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int32_t $ac_cv_c_int32_t
+_ACEOF
+;;
+esac
+
+ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t"
+case $ac_cv_c_int64_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int64_t $ac_cv_c_int64_t
+_ACEOF
+;;
+esac
+
+ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t"
+case $ac_cv_c_uint32_t in #(
+  no|yes) ;; #(
+  *)
+
+$as_echo "#define _UINT32_T 1" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint32_t $ac_cv_c_uint32_t
+_ACEOF
+;;
+  esac
+
+ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t"
+case $ac_cv_c_uint64_t in #(
+  no|yes) ;; #(
+  *)
+
+$as_echo "#define _UINT64_T 1" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint64_t $ac_cv_c_uint64_t
+_ACEOF
+;;
+  esac
+
+ac_fn_c_check_type "$LINENO" "__uint128_t" "ac_cv_type___uint128_t" "$ac_includes_default"
+if test "x$ac_cv_type___uint128_t" = xyes; then :
+
+$as_echo "#define HAVE_UINT128_T 1" >>confdefs.h
+
+fi
+
+
+# Sizes of various types:
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5
+$as_echo_n "checking size of size_t... " >&6; }
+if ${ac_cv_sizeof_size_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_size_t" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (size_t)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_size_t=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5
+$as_echo "$ac_cv_sizeof_size_t" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of __uint128_t" >&5
+$as_echo_n "checking size of __uint128_t... " >&6; }
+if ${ac_cv_sizeof___uint128_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (__uint128_t))" "ac_cv_sizeof___uint128_t"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type___uint128_t" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (__uint128_t)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof___uint128_t=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof___uint128_t" >&5
+$as_echo "$ac_cv_sizeof___uint128_t" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF___UINT128_T $ac_cv_sizeof___uint128_t
+_ACEOF
+
+
+
+# x64 with gcc asm:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x64 gcc inline assembler" >&5
+$as_echo_n "checking for x64 gcc inline assembler... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+__asm__ __volatile__ ("movq %rcx, %rax");
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  have_gcc_asm_for_x64=yes
+else
+  have_gcc_asm_for_x64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_asm_for_x64" >&5
+$as_echo "$have_gcc_asm_for_x64" >&6; }
+if test "$have_gcc_asm_for_x64" = yes; then
+
+$as_echo "#define HAVE_GCC_ASM_FOR_X64 1" >>confdefs.h
+
+fi
+
+# x87 with gcc asm:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x87 gcc inline assembler" >&5
+$as_echo_n "checking for x87 gcc inline assembler... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  unsigned short cw;
+  __asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
+  __asm__ __volatile__ ("fldcw %0" : : "m" (cw));
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  have_gcc_asm_for_x87=yes
+else
+  have_gcc_asm_for_x87=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_asm_for_x87" >&5
+$as_echo "$have_gcc_asm_for_x87" >&6; }
+if test "$have_gcc_asm_for_x87" = yes; then
+
+$as_echo "#define HAVE_GCC_ASM_FOR_X87 1" >>confdefs.h
+
+fi
+
+# Install program:
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+
+# ==============================================================================
+#                             Detect toolchain bugs
+# ==============================================================================
+
+# _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect:
+#    http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glibc _FORTIFY_SOURCE/memmove bug" >&5
+$as_echo_n "checking for glibc _FORTIFY_SOURCE/memmove bug... " >&6; }
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+CFLAGS="-O2 -D_FORTIFY_SOURCE=2"
+if test "$have_O2" = no; then
+  CFLAGS=
+fi
+LDFLAGS=
+if test "$cross_compiling" = yes; then :
+  have_glibc_memmove_bug=undefined
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+void foo(void *p, void *q) { memmove(p, q, 19); }
+int main() {
+  char a[32] = "123456789000000000";
+  foo(&a[9], a);
+  if (strcmp(a, "123456789123456789000000000") != 0)
+    return 1;
+  foo(a, &a[9]);
+  if (strcmp(a, "123456789000000000") != 0)
+    return 1;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_glibc_memmove_bug=no
+else
+  have_glibc_memmove_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_glibc_memmove_bug" >&5
+$as_echo "$have_glibc_memmove_bug" >&6; }
+if test "$have_glibc_memmove_bug" = yes; then
+
+$as_echo "#define HAVE_GLIBC_MEMMOVE_BUG 1" >>confdefs.h
+
+fi
+
+# ==============================================================================
+#                            Optimized configurations
+# ==============================================================================
+
+# Auto-detect machine dependent settings:
+AIX_AR=
+AIX_RANLIB=
+EXPORTSYMS=
+OBJECT_SUFFIX=
+DETECTED_MACHINE=
+if test $ac_cv_sizeof_size_t -eq 8; then
+  AIX_AR="ar -X64"
+  AIX_RANLIB="ranlib -X64"
+  EXPORTSYMS="symbols64.exp"
+  OBJECT_SUFFIX="4_64.o"
+  if test $have_gcc_asm_for_x64 = yes; then
+    DETECTED_MACHINE="x64"
+  elif test $ac_cv_type___uint128_t = yes; then
+    DETECTED_MACHINE="uint128"
+  else
+    DETECTED_MACHINE="ansi64"
+  fi
+else
+  AIX_AR="ar -X32"
+  AIX_RANLIB="ranlib -X32"
+  EXPORTSYMS="symbols32.exp"
+  OBJECT_SUFFIX="4.o"
+  DETECTED_MACHINE="ansi32"
+  if test $have_gcc_asm_for_x87 = yes; then
+    case $CC in
+      *gcc* | *clang*) # icc >= 11.0 works as well
+         case $host in
+           *-*-darwin*)
+             ;;
+           *)
+             DETECTED_MACHINE="ppro"
+             ;;
+         esac
+         ;;
+    esac
+  fi
+fi
+
+if test -z "$MACHINE"; then
+  MACHINE="$DETECTED_MACHINE"
+fi
+
+# Set configuration variables:
+MPD_ABI=
+MPD_PREC=9
+case "$MACHINE" in
+  x64)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DASM"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  uint128)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DANSI -DHAVE_UINT128_T"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  ansi64)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DANSI"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  ppro)
+    MPD_HEADER_CONFIG=$CONFIG_32
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DPPRO -DASM"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    # Some versions of gcc miscompile inline asm:
+    #    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491
+    #    http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html
+    case $CC in
+      *gcc*)
+          { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc ipa-pure-const bug" >&5
+$as_echo_n "checking for gcc ipa-pure-const bug... " >&6; }
+          saved_cflags="$CFLAGS"
+          saved_ldflags="$LDFLAGS"
+          CFLAGS="-O2"
+          LDFLAGS=
+          if test "$cross_compiling" = yes; then :
+  have_ipa_pure_const_bug=undefined
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          __attribute__((noinline)) int
+          foo(int *p) {
+            int r;
+            asm ( "movl \$6, (%1)\n\t"
+                  "xorl %0, %0\n\t"
+                  : "=r" (r) : "r" (p) : "memory"
+            );
+            return r;
+          }
+          int main() {
+            int p = 8;
+            if ((foo(&p) ? : p) != 6)
+              return 1;
+            return 0;
+          }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_ipa_pure_const_bug=no
+else
+  have_ipa_pure_const_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+          CFLAGS="$saved_cflags"
+          LDFLAGS="$saved_ldflags"
+          { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_ipa_pure_const_bug" >&5
+$as_echo "$have_ipa_pure_const_bug" >&6; }
+          if test "$have_ipa_pure_const_bug" = yes; then
+            MPD_CONFIG="$MPD_CONFIG -fno-ipa-pure-const"
+
+$as_echo "#define HAVE_IPA_PURE_CONST_BUG 1" >>confdefs.h
+
+          fi
+          ;;
+    esac
+    ;;
+  ansi32)
+    MPD_HEADER_CONFIG=$CONFIG_32
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DANSI"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    ;;
+  ansi-legacy)
+    MPD_HEADER_CONFIG=$CONFIG_32_LEGACY
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DANSI -DLEGACY_COMPILER"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    ;;
+  universal)
+    MPD_HEADER_CONFIG=$CONFIG_UNIVERSAL
+    MPD_CONFIG="-DUNIVERSAL"
+    ;;
+  *)
+    as_fn_error $? "cannot detect MACHINE" "$LINENO" 5
+    ;;
+esac
+
+# Special cases:
+MPD_CXXOPT=
+MPD_GNU99=
+case $host in
+  *-*-aix*)
+    AR=$AIX_AR
+    RANLIB=$AIX_RANLIB
+    MPD_OPT="-D_THREAD_SAFE $MPD_OPT"
+    MPD_CXXOPT="-D_THREAD_SAFE"
+    case "$MACHINE" in
+    universal)
+      MPD_HEADER_CONFIG=$CONFIG_UNIVERSAL_AIX
+      AR="ar -X32_64"
+      RANLIB="ranlib -X32_64"
+      ;;
+    esac
+    ;;
+  *-*-netbsd*)
+    MPD_OPT="-D_REENTRANT $MPD_OPT"
+    MPD_CXXOPT="-D_REENTRANT"
+    ;;
+  *-*-solaris* | *-*-sunos*)
+    MPD_OPT="-D_REENTRANT $MPD_OPT"
+    MPD_CXXOPT="-D_REENTRANT"
+    case $CC in
+      *gcc*)
+        MPD_GNU99=-std=gnu99
+        ;;
+    esac
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+# ==============================================================================
+#                         Substitute remaining variables
+# ==============================================================================
+
+if test -z "$INITIAL_CFLAGS"; then
+  CONFIGURE_LIBMPDEC_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT $MPD_ABI"
+  CONFIGURE_CFLAGS="$MPD_WARN $MPD_OPT $MPD_ABI"
+else
+  CONFIGURE_LIBMPDEC_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT $MPD_ABI $INITIAL_CFLAGS"
+  CONFIGURE_CFLAGS="$MPD_WARN $MPD_OPT $MPD_ABI $INITIAL_CFLAGS"
+fi
+
+if test "$have_glibc_memmove_bug" = yes; then
+  CONFIGURE_LIBMPDEC_CFLAGS="$CONFIGURE_LIBMPDEC_CFLAGS -U_FORTIFY_SOURCE"
+  CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -U_FORTIFY_SOURCE"
+fi
+
+if test -z "$INITIAL_CXXFLAGS"; then
+  CONFIGURE_CXXFLAGS="$MPD_WARNXX $MPD_CXXOPT -DNDEBUG -O3 $MPD_ABI"
+else
+  CONFIGURE_CXXFLAGS="$MPD_WARNXX $MPD_CXXOPT -DNDEBUG -O3 $MPD_ABI $INITIAL_CXXFLAGS"
+fi
+
+if test -n "$INITIAL_LDFLAGS"; then
+  CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $INITIAL_LDFLAGS"
+fi
+
+if test -n "$INITIAL_LDXXFLAGS"; then
+  CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $INITIAL_LDXXFLAGS"
+fi
+
+
+
+
+
+
+
+# ==============================================================================
+#                                  Write output
+# ==============================================================================
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by mpdecimal $as_me 4.0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_links="$ac_config_links"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration links:
+$config_links
+
+Report bugs to <mpdecimal-bugs@bytereef.org>.
+mpdecimal home page: <https://www.bytereef.org/mpdecimal/index.html>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+mpdecimal config.status 4.0.1
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "libmpdec/Makefile") CONFIG_FILES="$CONFIG_FILES libmpdec/Makefile" ;;
+    "libmpdec/mpdecimal.h") CONFIG_FILES="$CONFIG_FILES libmpdec/mpdecimal.h" ;;
+    "libmpdec/.pc/libmpdec.pc") CONFIG_FILES="$CONFIG_FILES libmpdec/.pc/libmpdec.pc" ;;
+    "libmpdec++/Makefile") CONFIG_FILES="$CONFIG_FILES libmpdec++/Makefile" ;;
+    "libmpdec++/.pc/libmpdec++.pc") CONFIG_FILES="$CONFIG_FILES libmpdec++/.pc/libmpdec++.pc" ;;
+    "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+    "tests++/Makefile") CONFIG_FILES="$CONFIG_FILES tests++/Makefile" ;;
+    "CHANGELOG.txt") CONFIG_LINKS="$CONFIG_LINKS CHANGELOG.txt:CHANGELOG.txt" ;;
+    "COPYRIGHT.txt") CONFIG_LINKS="$CONFIG_LINKS COPYRIGHT.txt:COPYRIGHT.txt" ;;
+    "INSTALL.txt") CONFIG_LINKS="$CONFIG_LINKS INSTALL.txt:INSTALL.txt" ;;
+    "Makefile.in") CONFIG_LINKS="$CONFIG_LINKS Makefile.in:Makefile.in" ;;
+    "README.txt") CONFIG_LINKS="$CONFIG_LINKS README.txt:README.txt" ;;
+    "config.guess") CONFIG_LINKS="$CONFIG_LINKS config.guess:config.guess" ;;
+    "config.h.in") CONFIG_LINKS="$CONFIG_LINKS config.h.in:config.h.in" ;;
+    "config.sub") CONFIG_LINKS="$CONFIG_LINKS config.sub:config.sub" ;;
+    "configure") CONFIG_LINKS="$CONFIG_LINKS configure:configure" ;;
+    "configure.ac") CONFIG_LINKS="$CONFIG_LINKS configure.ac:configure.ac" ;;
+    "install-sh") CONFIG_LINKS="$CONFIG_LINKS install-sh:install-sh" ;;
+    "doc/COPYRIGHT.txt") CONFIG_LINKS="$CONFIG_LINKS doc/COPYRIGHT.txt:doc/COPYRIGHT.txt" ;;
+    "doc/libmpdec.3") CONFIG_LINKS="$CONFIG_LINKS doc/libmpdec.3:doc/libmpdec.3" ;;
+    "doc/libmpdec++.3") CONFIG_LINKS="$CONFIG_LINKS doc/libmpdec++.3:doc/libmpdec++.3" ;;
+    "doc/mpdecimal.3") CONFIG_LINKS="$CONFIG_LINKS doc/mpdecimal.3:doc/mpdecimal.3" ;;
+    "libmpdec/Makefile.in") CONFIG_LINKS="$CONFIG_LINKS libmpdec/Makefile.in:libmpdec/Makefile.in" ;;
+    "libmpdec/Makefile.vc") CONFIG_LINKS="$CONFIG_LINKS libmpdec/Makefile.vc:libmpdec/Makefile.vc" ;;
+    "libmpdec/README.txt") CONFIG_LINKS="$CONFIG_LINKS libmpdec/README.txt:libmpdec/README.txt" ;;
+    "libmpdec/basearith.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/basearith.c:libmpdec/basearith.c" ;;
+    "libmpdec/basearith.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/basearith.h:libmpdec/basearith.h" ;;
+    "libmpdec/bench.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/bench.c:libmpdec/bench.c" ;;
+    "libmpdec/bench_full.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/bench_full.c:libmpdec/bench_full.c" ;;
+    "libmpdec/bits.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/bits.h:libmpdec/bits.h" ;;
+    "libmpdec/constants.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/constants.c:libmpdec/constants.c" ;;
+    "libmpdec/constants.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/constants.h:libmpdec/constants.h" ;;
+    "libmpdec/context.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/context.c:libmpdec/context.c" ;;
+    "libmpdec/convolute.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/convolute.c:libmpdec/convolute.c" ;;
+    "libmpdec/convolute.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/convolute.h:libmpdec/convolute.h" ;;
+    "libmpdec/crt.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/crt.c:libmpdec/crt.c" ;;
+    "libmpdec/crt.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/crt.h:libmpdec/crt.h" ;;
+    "libmpdec/difradix2.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/difradix2.c:libmpdec/difradix2.c" ;;
+    "libmpdec/difradix2.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/difradix2.h:libmpdec/difradix2.h" ;;
+    "libmpdec/fnt.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/fnt.c:libmpdec/fnt.c" ;;
+    "libmpdec/fnt.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/fnt.h:libmpdec/fnt.h" ;;
+    "libmpdec/fourstep.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/fourstep.c:libmpdec/fourstep.c" ;;
+    "libmpdec/fourstep.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/fourstep.h:libmpdec/fourstep.h" ;;
+    "libmpdec/io.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/io.c:libmpdec/io.c" ;;
+    "libmpdec/io.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/io.h:libmpdec/io.h" ;;
+    "libmpdec/mpalloc.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpalloc.c:libmpdec/mpalloc.c" ;;
+    "libmpdec/mpalloc.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpalloc.h:libmpdec/mpalloc.h" ;;
+    "libmpdec/mpdecimal.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpdecimal.c:libmpdec/mpdecimal.c" ;;
+    "libmpdec/mpdecimal32vc.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpdecimal32vc.h:libmpdec/mpdecimal32vc.h" ;;
+    "libmpdec/mpdecimal64vc.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpdecimal64vc.h:libmpdec/mpdecimal64vc.h" ;;
+    "libmpdec/mpdecimal.h.in") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpdecimal.h.in:libmpdec/mpdecimal.h.in" ;;
+    "libmpdec/mpsignal.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/mpsignal.c:libmpdec/mpsignal.c" ;;
+    "libmpdec/numbertheory.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/numbertheory.c:libmpdec/numbertheory.c" ;;
+    "libmpdec/numbertheory.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/numbertheory.h:libmpdec/numbertheory.h" ;;
+    "libmpdec/sixstep.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/sixstep.c:libmpdec/sixstep.c" ;;
+    "libmpdec/sixstep.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/sixstep.h:libmpdec/sixstep.h" ;;
+    "libmpdec/transpose.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/transpose.c:libmpdec/transpose.c" ;;
+    "libmpdec/transpose.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/transpose.h:libmpdec/transpose.h" ;;
+    "libmpdec/typearith.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/typearith.h:libmpdec/typearith.h" ;;
+    "libmpdec/umodarith.h") CONFIG_LINKS="$CONFIG_LINKS libmpdec/umodarith.h:libmpdec/umodarith.h" ;;
+    "libmpdec/vcdiv64.asm") CONFIG_LINKS="$CONFIG_LINKS libmpdec/vcdiv64.asm:libmpdec/vcdiv64.asm" ;;
+    "libmpdec/examples/README.txt") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/README.txt:libmpdec/examples/README.txt" ;;
+    "libmpdec/examples/compare.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/compare.c:libmpdec/examples/compare.c" ;;
+    "libmpdec/examples/div.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/div.c:libmpdec/examples/div.c" ;;
+    "libmpdec/examples/divmod.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/divmod.c:libmpdec/examples/divmod.c" ;;
+    "libmpdec/examples/multiply.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/multiply.c:libmpdec/examples/multiply.c" ;;
+    "libmpdec/examples/pow.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/pow.c:libmpdec/examples/pow.c" ;;
+    "libmpdec/examples/powmod.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/powmod.c:libmpdec/examples/powmod.c" ;;
+    "libmpdec/examples/shift.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/shift.c:libmpdec/examples/shift.c" ;;
+    "libmpdec/examples/sqrt.c") CONFIG_LINKS="$CONFIG_LINKS libmpdec/examples/sqrt.c:libmpdec/examples/sqrt.c" ;;
+    "libmpdec/.objs/README.txt") CONFIG_LINKS="$CONFIG_LINKS libmpdec/.objs/README.txt:libmpdec/.objs/README.txt" ;;
+    "libmpdec/.objs/symbols32.exp") CONFIG_LINKS="$CONFIG_LINKS libmpdec/.objs/symbols32.exp:libmpdec/.objs/symbols32.exp" ;;
+    "libmpdec/.objs/symbols64.exp") CONFIG_LINKS="$CONFIG_LINKS libmpdec/.objs/symbols64.exp:libmpdec/.objs/symbols64.exp" ;;
+    "libmpdec/.pc/libmpdec.pc.in") CONFIG_LINKS="$CONFIG_LINKS libmpdec/.pc/libmpdec.pc.in:libmpdec/.pc/libmpdec.pc.in" ;;
+    "libmpdec/.profile/train.sh") CONFIG_LINKS="$CONFIG_LINKS libmpdec/.profile/train.sh:libmpdec/.profile/train.sh" ;;
+    "libmpdec++/Makefile.in") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/Makefile.in:libmpdec++/Makefile.in" ;;
+    "libmpdec++/Makefile.vc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/Makefile.vc:libmpdec++/Makefile.vc" ;;
+    "libmpdec++/bench.cc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/bench.cc:libmpdec++/bench.cc" ;;
+    "libmpdec++/bench_full.cc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/bench_full.cc:libmpdec++/bench_full.cc" ;;
+    "libmpdec++/decimal.cc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/decimal.cc:libmpdec++/decimal.cc" ;;
+    "libmpdec++/decimal.hh") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/decimal.hh:libmpdec++/decimal.hh" ;;
+    "libmpdec++/examples/factorial.cc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/examples/factorial.cc:libmpdec++/examples/factorial.cc" ;;
+    "libmpdec++/examples/pi.cc") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/examples/pi.cc:libmpdec++/examples/pi.cc" ;;
+    "libmpdec++/.objs/README.txt") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/.objs/README.txt:libmpdec++/.objs/README.txt" ;;
+    "libmpdec++/.pc/libmpdec++.pc.in") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/.pc/libmpdec++.pc.in:libmpdec++/.pc/libmpdec++.pc.in" ;;
+    "libmpdec++/.profile/train.sh") CONFIG_LINKS="$CONFIG_LINKS libmpdec++/.profile/train.sh:libmpdec++/.profile/train.sh" ;;
+    "tests/Makefile.in") CONFIG_LINKS="$CONFIG_LINKS tests/Makefile.in:tests/Makefile.in" ;;
+    "tests/Makefile.vc") CONFIG_LINKS="$CONFIG_LINKS tests/Makefile.vc:tests/Makefile.vc" ;;
+    "tests/README.txt") CONFIG_LINKS="$CONFIG_LINKS tests/README.txt:tests/README.txt" ;;
+    "tests/additional.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/additional.decTest:tests/additional.decTest" ;;
+    "tests/gettests.bat") CONFIG_LINKS="$CONFIG_LINKS tests/gettests.bat:tests/gettests.bat" ;;
+    "tests/gettests.sh") CONFIG_LINKS="$CONFIG_LINKS tests/gettests.sh:tests/gettests.sh" ;;
+    "tests/official.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/official.decTest:tests/official.decTest" ;;
+    "tests/runshort.sh") CONFIG_LINKS="$CONFIG_LINKS tests/runshort.sh:tests/runshort.sh" ;;
+    "tests/runshort_alloc.sh") CONFIG_LINKS="$CONFIG_LINKS tests/runshort_alloc.sh:tests/runshort_alloc.sh" ;;
+    "tests/runtest.c") CONFIG_LINKS="$CONFIG_LINKS tests/runtest.c:tests/runtest.c" ;;
+    "tests/test.c") CONFIG_LINKS="$CONFIG_LINKS tests/test.c:tests/test.c" ;;
+    "tests/test.h") CONFIG_LINKS="$CONFIG_LINKS tests/test.h:tests/test.h" ;;
+    "tests/vctest.h") CONFIG_LINKS="$CONFIG_LINKS tests/vctest.h:tests/vctest.h" ;;
+    "tests/testdata_dist/baseconv.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/baseconv.decTest:tests/testdata_dist/baseconv.decTest" ;;
+    "tests/testdata_dist/binop_eq.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/binop_eq.decTest:tests/testdata_dist/binop_eq.decTest" ;;
+    "tests/testdata_dist/cov.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/cov.decTest:tests/testdata_dist/cov.decTest" ;;
+    "tests/testdata_dist/divmod.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/divmod.decTest:tests/testdata_dist/divmod.decTest" ;;
+    "tests/testdata_dist/divmod_eq.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/divmod_eq.decTest:tests/testdata_dist/divmod_eq.decTest" ;;
+    "tests/testdata_dist/extra.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/extra.decTest:tests/testdata_dist/extra.decTest" ;;
+    "tests/testdata_dist/fma_eq.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/fma_eq.decTest:tests/testdata_dist/fma_eq.decTest" ;;
+    "tests/testdata_dist/format.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/format.decTest:tests/testdata_dist/format.decTest" ;;
+    "tests/testdata_dist/getint.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/getint.decTest:tests/testdata_dist/getint.decTest" ;;
+    "tests/testdata_dist/invroot.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/invroot.decTest:tests/testdata_dist/invroot.decTest" ;;
+    "tests/testdata_dist/largeint.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/largeint.decTest:tests/testdata_dist/largeint.decTest" ;;
+    "tests/testdata_dist/maxprec.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/maxprec.decTest:tests/testdata_dist/maxprec.decTest" ;;
+    "tests/testdata_dist/powmod.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/powmod.decTest:tests/testdata_dist/powmod.decTest" ;;
+    "tests/testdata_dist/powmod_eq.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/powmod_eq.decTest:tests/testdata_dist/powmod_eq.decTest" ;;
+    "tests/testdata_dist/shiftlr.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/shiftlr.decTest:tests/testdata_dist/shiftlr.decTest" ;;
+    "tests/testdata_dist/testruntest.decTest") CONFIG_LINKS="$CONFIG_LINKS tests/testdata_dist/testruntest.decTest:tests/testdata_dist/testruntest.decTest" ;;
+    "tests++/Makefile.in") CONFIG_LINKS="$CONFIG_LINKS tests++/Makefile.in:tests++/Makefile.in" ;;
+    "tests++/Makefile.vc") CONFIG_LINKS="$CONFIG_LINKS tests++/Makefile.vc:tests++/Makefile.vc" ;;
+    "tests++/README.txt") CONFIG_LINKS="$CONFIG_LINKS tests++/README.txt:tests++/README.txt" ;;
+    "tests++/additional.topTest") CONFIG_LINKS="$CONFIG_LINKS tests++/additional.topTest:tests++/additional.topTest" ;;
+    "tests++/apitest.cc") CONFIG_LINKS="$CONFIG_LINKS tests++/apitest.cc:tests++/apitest.cc" ;;
+    "tests++/gettests.bat") CONFIG_LINKS="$CONFIG_LINKS tests++/gettests.bat:tests++/gettests.bat" ;;
+    "tests++/gettests.sh") CONFIG_LINKS="$CONFIG_LINKS tests++/gettests.sh:tests++/gettests.sh" ;;
+    "tests++/official.topTest") CONFIG_LINKS="$CONFIG_LINKS tests++/official.topTest:tests++/official.topTest" ;;
+    "tests++/runshort.sh") CONFIG_LINKS="$CONFIG_LINKS tests++/runshort.sh:tests++/runshort.sh" ;;
+    "tests++/runshort_alloc.sh") CONFIG_LINKS="$CONFIG_LINKS tests++/runshort_alloc.sh:tests++/runshort_alloc.sh" ;;
+    "tests++/runtest.cc") CONFIG_LINKS="$CONFIG_LINKS tests++/runtest.cc:tests++/runtest.cc" ;;
+    "tests++/test.cc") CONFIG_LINKS="$CONFIG_LINKS tests++/test.cc:tests++/test.cc" ;;
+    "tests++/test.hh") CONFIG_LINKS="$CONFIG_LINKS tests++/test.hh:tests++/test.hh" ;;
+    "tests++/vctest.hh") CONFIG_LINKS="$CONFIG_LINKS tests++/vctest.hh:tests++/vctest.hh" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_LINKS+set}" = set || CONFIG_LINKS=$config_links
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS  :L $CONFIG_LINKS  "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+  :L)
+  #
+  # CONFIG_LINK
+  #
+
+  if test "$ac_source" = "$ac_file" && test "$srcdir" = '.'; then
+    :
+  else
+    # Prefer the file from the source tree if names are identical.
+    if test "$ac_source" = "$ac_file" || test ! -r "$ac_source"; then
+      ac_source=$srcdir/$ac_source
+    fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: linking $ac_source to $ac_file" >&5
+$as_echo "$as_me: linking $ac_source to $ac_file" >&6;}
+
+    if test ! -r "$ac_source"; then
+      as_fn_error $? "$ac_source: file not found" "$LINENO" 5
+    fi
+    rm -f "$ac_file"
+
+    # Try a relative symlink, then a hard link, then a copy.
+    case $ac_source in
+    [\\/$]* | ?:[\\/]* ) ac_rel_source=$ac_source ;;
+       *) ac_rel_source=$ac_top_build_prefix$ac_source ;;
+    esac
+    ln -s "$ac_rel_source" "$ac_file" 2>/dev/null ||
+      ln "$ac_source" "$ac_file" 2>/dev/null ||
+      cp -p "$ac_source" "$ac_file" ||
+      as_fn_error $? "cannot link or copy $ac_source to $ac_file" "$LINENO" 5
+  fi
+ ;;
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+GLIBC_MEMMOVE_BUG_WARN="
+***************************** WARNING *********************************
+
+Detected glibc _FORTIFY_SOURCE/memmove bug. See:
+
+  http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+
+Enabling -U_FORTIFY_SOURCE workaround. If -D_FORTIFY_SOURCE is also
+present in the command line, make sure that the order of the two
+options is:
+
+  ... -D_FORTIFY_SOURCE=2 ... -U_FORTIFY_SOURCE ...
+
+A better solution is to upgrade glibc or to report the bug to your
+OS vendor.
+
+***************************** WARNING *********************************
+"
+
+if test "$have_glibc_memmove_bug" = yes; then
+  echo "$GLIBC_MEMMOVE_BUG_WARN"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..8be0106
--- /dev/null
@@ -0,0 +1,1123 @@
+AC_COPYRIGHT([
+Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+])
+
+AH_TOP([
+/*
+ * The generated config.h is only included in runtest.cc, and there are
+ * no plans to use it elsewhere.  All defines apart from HAVE_PTHREAD_H
+ * are purely informational.
+ */
+])
+
+
+AC_PREREQ([2.69])
+
+AC_INIT([mpdecimal],
+        [4.0.1],
+        [mpdecimal-bugs@bytereef.org],
+        [mpdecimal],
+        [https://www.bytereef.org/mpdecimal/index.html])
+
+AC_CONFIG_HEADERS([config.h])
+
+AC_CONFIG_FILES([Makefile
+                 libmpdec/Makefile
+                 libmpdec/mpdecimal.h
+                 libmpdec/.pc/libmpdec.pc
+                 libmpdec++/Makefile
+                 libmpdec++/.pc/libmpdec++.pc
+                 tests/Makefile
+                 tests++/Makefile])
+
+# Link files for out-of-tree builds:
+AC_CONFIG_LINKS([CHANGELOG.txt:CHANGELOG.txt
+                 COPYRIGHT.txt:COPYRIGHT.txt
+                 INSTALL.txt:INSTALL.txt
+                 Makefile.in:Makefile.in
+                 README.txt:README.txt
+                 config.guess:config.guess
+                 config.h.in:config.h.in
+                 config.sub:config.sub
+                 configure:configure
+                 configure.ac:configure.ac
+                 install-sh:install-sh
+
+                 doc/COPYRIGHT.txt:doc/COPYRIGHT.txt
+                 doc/libmpdec.3:doc/libmpdec.3
+                 doc/libmpdec++.3:doc/libmpdec++.3
+                 doc/mpdecimal.3:doc/mpdecimal.3
+
+                 libmpdec/Makefile.in:libmpdec/Makefile.in
+                 libmpdec/Makefile.vc:libmpdec/Makefile.vc
+                 libmpdec/README.txt:libmpdec/README.txt
+                 libmpdec/basearith.c:libmpdec/basearith.c
+                 libmpdec/basearith.h:libmpdec/basearith.h
+                 libmpdec/bench.c:libmpdec/bench.c
+                 libmpdec/bench_full.c:libmpdec/bench_full.c
+                 libmpdec/bits.h:libmpdec/bits.h
+                 libmpdec/constants.c:libmpdec/constants.c
+                 libmpdec/constants.h:libmpdec/constants.h
+                 libmpdec/context.c:libmpdec/context.c
+                 libmpdec/convolute.c:libmpdec/convolute.c
+                 libmpdec/convolute.h:libmpdec/convolute.h
+                 libmpdec/crt.c:libmpdec/crt.c
+                 libmpdec/crt.h:libmpdec/crt.h
+                 libmpdec/difradix2.c:libmpdec/difradix2.c
+                 libmpdec/difradix2.h:libmpdec/difradix2.h
+                 libmpdec/fnt.c:libmpdec/fnt.c
+                 libmpdec/fnt.h:libmpdec/fnt.h
+                 libmpdec/fourstep.c:libmpdec/fourstep.c
+                 libmpdec/fourstep.h:libmpdec/fourstep.h
+                 libmpdec/io.c:libmpdec/io.c
+                 libmpdec/io.h:libmpdec/io.h
+                 libmpdec/mpalloc.c:libmpdec/mpalloc.c
+                 libmpdec/mpalloc.h:libmpdec/mpalloc.h
+                 libmpdec/mpdecimal.c:libmpdec/mpdecimal.c
+                 libmpdec/mpdecimal32vc.h:libmpdec/mpdecimal32vc.h
+                 libmpdec/mpdecimal64vc.h:libmpdec/mpdecimal64vc.h
+                 libmpdec/mpdecimal.h.in:libmpdec/mpdecimal.h.in
+                 libmpdec/mpsignal.c:libmpdec/mpsignal.c
+                 libmpdec/numbertheory.c:libmpdec/numbertheory.c
+                 libmpdec/numbertheory.h:libmpdec/numbertheory.h
+                 libmpdec/sixstep.c:libmpdec/sixstep.c
+                 libmpdec/sixstep.h:libmpdec/sixstep.h
+                 libmpdec/transpose.c:libmpdec/transpose.c
+                 libmpdec/transpose.h:libmpdec/transpose.h
+                 libmpdec/typearith.h:libmpdec/typearith.h
+                 libmpdec/umodarith.h:libmpdec/umodarith.h
+                 libmpdec/vcdiv64.asm:libmpdec/vcdiv64.asm
+
+                 libmpdec/examples/README.txt:libmpdec/examples/README.txt
+                 libmpdec/examples/compare.c:libmpdec/examples/compare.c
+                 libmpdec/examples/div.c:libmpdec/examples/div.c
+                 libmpdec/examples/divmod.c:libmpdec/examples/divmod.c
+                 libmpdec/examples/multiply.c:libmpdec/examples/multiply.c
+                 libmpdec/examples/pow.c:libmpdec/examples/pow.c
+                 libmpdec/examples/powmod.c:libmpdec/examples/powmod.c
+                 libmpdec/examples/shift.c:libmpdec/examples/shift.c
+                 libmpdec/examples/sqrt.c:libmpdec/examples/sqrt.c
+
+                 libmpdec/.objs/README.txt:libmpdec/.objs/README.txt
+                 libmpdec/.objs/symbols32.exp:libmpdec/.objs/symbols32.exp
+                 libmpdec/.objs/symbols64.exp:libmpdec/.objs/symbols64.exp
+                 libmpdec/.pc/libmpdec.pc.in:libmpdec/.pc/libmpdec.pc.in
+                 libmpdec/.profile/train.sh:libmpdec/.profile/train.sh
+
+                 libmpdec++/Makefile.in:libmpdec++/Makefile.in
+                 libmpdec++/Makefile.vc:libmpdec++/Makefile.vc
+                 libmpdec++/bench.cc:libmpdec++/bench.cc
+                 libmpdec++/bench_full.cc:libmpdec++/bench_full.cc
+                 libmpdec++/decimal.cc:libmpdec++/decimal.cc
+                 libmpdec++/decimal.hh:libmpdec++/decimal.hh
+
+                 libmpdec++/examples/factorial.cc:libmpdec++/examples/factorial.cc
+                 libmpdec++/examples/pi.cc:libmpdec++/examples/pi.cc
+
+                 libmpdec++/.objs/README.txt:libmpdec++/.objs/README.txt
+                 libmpdec++/.pc/libmpdec++.pc.in:libmpdec++/.pc/libmpdec++.pc.in
+                 libmpdec++/.profile/train.sh:libmpdec++/.profile/train.sh
+
+                 tests/Makefile.in:tests/Makefile.in
+                 tests/Makefile.vc:tests/Makefile.vc
+                 tests/README.txt:tests/README.txt
+                 tests/additional.decTest:tests/additional.decTest
+                 tests/gettests.bat:tests/gettests.bat
+                 tests/gettests.sh:tests/gettests.sh
+                 tests/official.decTest:tests/official.decTest
+                 tests/runshort.sh:tests/runshort.sh
+                 tests/runshort_alloc.sh:tests/runshort_alloc.sh
+                 tests/runtest.c:tests/runtest.c
+                 tests/test.c:tests/test.c
+                 tests/test.h:tests/test.h
+                 tests/vctest.h:tests/vctest.h
+
+                 tests/testdata_dist/baseconv.decTest:tests/testdata_dist/baseconv.decTest
+                 tests/testdata_dist/binop_eq.decTest:tests/testdata_dist/binop_eq.decTest
+                 tests/testdata_dist/cov.decTest:tests/testdata_dist/cov.decTest
+                 tests/testdata_dist/divmod.decTest:tests/testdata_dist/divmod.decTest
+                 tests/testdata_dist/divmod_eq.decTest:tests/testdata_dist/divmod_eq.decTest
+                 tests/testdata_dist/extra.decTest:tests/testdata_dist/extra.decTest
+                 tests/testdata_dist/fma_eq.decTest:tests/testdata_dist/fma_eq.decTest
+                 tests/testdata_dist/format.decTest:tests/testdata_dist/format.decTest
+                 tests/testdata_dist/getint.decTest:tests/testdata_dist/getint.decTest
+                 tests/testdata_dist/invroot.decTest:tests/testdata_dist/invroot.decTest
+                 tests/testdata_dist/largeint.decTest:tests/testdata_dist/largeint.decTest
+                 tests/testdata_dist/maxprec.decTest:tests/testdata_dist/maxprec.decTest
+                 tests/testdata_dist/powmod.decTest:tests/testdata_dist/powmod.decTest
+                 tests/testdata_dist/powmod_eq.decTest:tests/testdata_dist/powmod_eq.decTest
+                 tests/testdata_dist/shiftlr.decTest:tests/testdata_dist/shiftlr.decTest
+                 tests/testdata_dist/testruntest.decTest:tests/testdata_dist/testruntest.decTest
+
+                 tests++/Makefile.in:tests++/Makefile.in
+                 tests++/Makefile.vc:tests++/Makefile.vc
+                 tests++/README.txt:tests++/README.txt
+                 tests++/additional.topTest:tests++/additional.topTest
+                 tests++/apitest.cc:tests++/apitest.cc
+                 tests++/gettests.bat:tests++/gettests.bat
+                 tests++/gettests.sh:tests++/gettests.sh
+                 tests++/official.topTest:tests++/official.topTest
+                 tests++/runshort.sh:tests++/runshort.sh
+                 tests++/runshort_alloc.sh:tests++/runshort_alloc.sh
+                 tests++/runtest.cc:tests++/runtest.cc
+                 tests++/test.cc:tests++/test.cc
+                 tests++/test.hh:tests++/test.hh
+                 tests++/vctest.hh:tests++/vctest.hh])
+
+# ==============================================================================
+#                               MPD_HEADER_CONFIG
+# ==============================================================================
+
+CONFIG_64="
+/* ABI: 64-bit */
+#define MPD_CONFIG_64 1
+
+#ifdef MPD_CONFIG_32
+  #error \"cannot use MPD_CONFIG_32 with 64-bit header.\"
+#endif
+
+#ifdef CONFIG_32
+  #error \"cannot use CONFIG_32 with 64-bit header.\"
+#endif"
+
+CONFIG_32="
+/* ABI: 32-bit */
+#define MPD_CONFIG_32 1
+
+#ifdef MPD_CONFIG_64
+  #error \"cannot use MPD_CONFIG_64 with 32-bit header.\"
+#endif
+
+#ifdef CONFIG_64
+  #error \"cannot use CONFIG_64 with 32-bit header.\"
+#endif"
+
+CONFIG_32_LEGACY="
+/* ABI: 32-bit */
+#define MPD_CONFIG_32 1
+
+/* libmpdec was compiled without support for int64_t */
+#define MPD_LEGACY_COMPILER 1
+
+#ifdef MPD_CONFIG_64
+  #error \"cannot use MPD_CONFIG_64 with 32-bit header.\"
+#endif
+
+#ifdef CONFIG_64
+  #error \"cannot use CONFIG_64 with 32-bit header.\"
+#endif"
+
+CONFIG_UNIVERSAL="
+/* Mac OS X: support for building universal binaries */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with universal header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with universal header.\"
+#endif
+
+#if defined(__ppc__)
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#elif defined(__ppc64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#elif defined(__i386__)
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#elif defined(__x86_64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ASM
+  #endif
+#elif defined(__arm64__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#else
+  #error \"unknown architecture for universal build.\"
+#endif"
+
+CONFIG_UNIVERSAL_AIX_UINT128="
+/* AIX: support for multilib */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(__64BIT__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+    #define HAVE_UINT128_T
+  #endif
+#else
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#endif"
+
+CONFIG_UNIVERSAL_AIX_ANSI64="
+/* AIX: support for multilib */
+#if defined(MPD_CONFIG_64) || defined(MPD_CONFIG_32)
+  #error \"cannot use MPD_CONFIG_64 or MPD_CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(CONFIG_64) || defined(CONFIG_32)
+  #error \"cannot use CONFIG_64 or CONFIG_32 with multilib header.\"
+#endif
+
+#if defined(__64BIT__)
+  #define MPD_CONFIG_64 1
+  #ifdef UNIVERSAL
+    #define CONFIG_64
+    #define ANSI
+  #endif
+#else
+  #define MPD_CONFIG_32 1
+  #ifdef UNIVERSAL
+    #define CONFIG_32
+    #define ANSI
+  #endif
+#endif"
+
+# ==============================================================================
+#                         Detect build and host systems
+# ==============================================================================
+
+AC_CANONICAL_HOST
+
+# ==============================================================================
+#                              Select library names
+# ==============================================================================
+
+# MinGW/Cygwin/MSYS2 support:
+ENABLE_MINGW="no"
+
+# Set library names and linker flags:
+FPIC="-fPIC"
+LIBSTATIC=libmpdec.a
+LIBSHARED_USE_AR="no"
+LIBIMPORT=
+LINK_STATIC=
+LINK_DYNAMIC=
+case $host in
+  *-*-darwin*)
+    LIBNAME="libmpdec.dylib"
+    LIBSONAME="libmpdec.4.dylib"
+    LIBSHARED="libmpdec.4.0.1.dylib"
+    CONFIGURE_LDFLAGS="-dynamiclib $FPIC -install_name \$(libdir)/$LIBSONAME -compatibility_version 4.0 -current_version 4.0.1"
+    ;;
+  *-*-aix*)
+    LIBNAME=
+    LIBSONAME=
+    LIBSHARED="shr.o"
+    LIBSHARED_USE_AR="yes"
+    CONFIGURE_LDFLAGS="-shared $FPIC -Wl,-bnoentry -Wl,-bE:.objs/symbols.exp"
+    LINK_STATIC="-Wl,-bstatic"
+    LINK_DYNAMIC="-Wl,-bshared"
+    ;;
+  *-*-mingw*)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="libmpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *-*-cygwin*)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="cygmpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *-*-msys)
+    FPIC=
+    LIBNAME=
+    LIBIMPORT="libmpdec.dll.a"
+    LIBSONAME=
+    LIBSHARED="msys-mpdec-4.dll"
+    CONFIGURE_LDFLAGS="-shared -Wl,--out-implib,$LIBIMPORT"
+    ENABLE_MINGW="yes"
+    ;;
+  *)
+    LIBNAME="libmpdec.so"
+    LIBSONAME="libmpdec.so.4"
+    LIBSHARED="libmpdec.so.4.0.1"
+    CONFIGURE_LDFLAGS="-shared $FPIC -Wl,-soname,$LIBSONAME"
+    ;;
+esac
+
+LIBSTATIC_CXX=libmpdec++.a
+LIBIMPORT_CXX=
+case $host in
+  *-*-darwin*)
+    LIBNAME_CXX="libmpdec++.dylib"
+    LIBSONAME_CXX="libmpdec++.4.dylib"
+    LIBSHARED_CXX="libmpdec++.4.0.1.dylib"
+    CONFIGURE_LDXXFLAGS="-dynamiclib $FPIC -install_name \$(libdir)/$LIBSONAME_CXX -compatibility_version 4.0 -current_version 4.0.1"
+    ;;
+  *-*-aix*)
+    LIBNAME_CXX=
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="shr.o"
+    CONFIGURE_LDXXFLAGS="-shared $FPIC -Wl,-bnoentry -Wl,-bE:.objs/symbols.exp"
+    ;;
+  *-*-mingw*)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="libmpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *-*-cygwin*)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="cygmpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *-*-msys)
+    LIBNAME_CXX=
+    LIBIMPORT_CXX="libmpdec++.dll.a"
+    LIBSONAME_CXX=
+    LIBSHARED_CXX="msys-mpdec++-4.dll"
+    CONFIGURE_LDXXFLAGS="-shared -Wl,--out-implib,$LIBIMPORT_CXX"
+    ;;
+  *)
+    LIBNAME_CXX="libmpdec++.so"
+    LIBSONAME_CXX="libmpdec++.so.4"
+    LIBSHARED_CXX="libmpdec++.so.4.0.1"
+    CONFIGURE_LDXXFLAGS="-shared $FPIC -Wl,-soname,$LIBSONAME_CXX"
+    ;;
+esac
+
+AC_SUBST(FPIC)
+AC_SUBST(LIBSTATIC)
+AC_SUBST(LIBNAME)
+AC_SUBST(LIBSONAME)
+AC_SUBST(LIBSHARED)
+AC_SUBST(LIBIMPORT)
+AC_SUBST(LIBSHARED_USE_AR)
+AC_SUBST(LINK_STATIC)
+AC_SUBST(LINK_DYNAMIC)
+AC_SUBST(LIBSTATIC_CXX)
+AC_SUBST(LIBNAME_CXX)
+AC_SUBST(LIBSONAME_CXX)
+AC_SUBST(LIBSHARED_CXX)
+AC_SUBST(LIBIMPORT_CXX)
+AC_SUBST(ENABLE_MINGW)
+
+# ==============================================================================
+#                                  User options
+# ==============================================================================
+
+# Check whether to build libmpdec++:
+AC_MSG_CHECKING(for --enable-cxx)
+AC_ARG_ENABLE(cxx, AS_HELP_STRING([--enable-cxx],
+                                  [enable building libmpdec++ (default is yes)]))
+
+ENABLE_CXX="$enable_cxx"
+if test -z "$ENABLE_CXX"
+then
+  if test x"$MACHINE" = x"ansi-legacy"; then
+    ENABLE_CXX="no"
+  else
+    ENABLE_CXX="yes"
+  fi
+fi
+AC_MSG_RESULT($ENABLE_CXX)
+
+# Check whether to build the static libraries:
+AC_MSG_CHECKING(for --enable-static)
+AC_ARG_ENABLE(static, AS_HELP_STRING([--enable-static],
+                                     [enable building static libraries (default is yes)]))
+
+ENABLE_STATIC="$enable_static"
+if test -z "$ENABLE_STATIC"
+then
+    ENABLE_STATIC="yes"
+fi
+AC_MSG_RESULT($ENABLE_STATIC)
+
+# Check whether to build the shared libraries:
+AC_MSG_CHECKING(for --enable-shared)
+AC_ARG_ENABLE(shared, AS_HELP_STRING([--enable-shared],
+                                     [enable building shared libraries (default is yes)]))
+
+ENABLE_SHARED="$enable_shared"
+if test -z "$ENABLE_SHARED"
+then
+  ENABLE_SHARED="yes"
+fi
+AC_MSG_RESULT($ENABLE_SHARED)
+
+# Check whether at least one library is enabled:
+if test x"$ENABLE_STATIC" = x"no" -a x"$ENABLE_SHARED" = x"no"
+then
+  AC_MSG_ERROR([at least one of --enable-static or --enable-shared must be set])
+fi
+
+# AIX: both static and shared libraries must be enabled:
+case $host in
+  *-*-aix*)
+    if test x"$ENABLE_STATIC" = x"no" -o x"$ENABLE_SHARED" = x"no"
+    then
+      AC_MSG_ERROR([the AIX build requires --enable-static and --enable-shared])
+    fi
+    ;;
+esac
+
+# Check whether to install the documentation:
+AC_MSG_CHECKING(for --enable-doc)
+AC_ARG_ENABLE(doc, AS_HELP_STRING([--enable-doc],
+                                  [enable installing the documentation (default is yes)]))
+
+ENABLE_DOC="$enable_doc"
+if test -z "$ENABLE_DOC"
+then
+  ENABLE_DOC="yes"
+fi
+AC_MSG_RESULT($ENABLE_DOC)
+
+# Check whether to install the pkg-config files:
+AC_MSG_CHECKING(for --enable-pc)
+AC_ARG_ENABLE(pc, AS_HELP_STRING([--enable-pc],
+                                 [enable installing the pkg-config files (default is yes)]))
+
+ENABLE_PC="$enable_pc"
+if test -z "$ENABLE_PC"
+then
+  ENABLE_PC="yes"
+fi
+AC_MSG_RESULT($ENABLE_PC)
+
+AC_SUBST(ENABLE_CXX)
+AC_SUBST(ENABLE_STATIC)
+AC_SUBST(ENABLE_SHARED)
+AC_SUBST(ENABLE_DOC)
+AC_SUBST(ENABLE_PC)
+
+# ==============================================================================
+#                              Compilers and flags
+# ==============================================================================
+
+# Save initial compiler flags:
+INITIAL_CFLAGS="$CFLAGS"
+INITIAL_CXXFLAGS="$CXXFLAGS"
+INITIAL_LDFLAGS="$LDFLAGS"
+INITIAL_LDXXFLAGS="$LDXXFLAGS"
+
+# Rename compiler in case $CC=cc:
+case $CC in
+  cc)
+    case $build in
+      *-*-freebsd* | *-*-openbsd* | *-*-darwin*)
+        CC=clang
+        ;;
+      *-*-netbsd*)
+        CC=gcc
+        ;;
+      *-*-solaris* | *-*-sunos*)
+        CC=suncc
+        ;;
+    esac
+    ;;
+esac
+
+case $CXX in
+  c++)
+    case $build in
+      *-*-freebsd* | *-*-openbsd* | *-*-darwin*)
+        CXX=clang++
+        ;;
+      *-*-netbsd*)
+        CXX=g++
+        ;;
+    esac
+    ;;
+esac
+
+# Language and compiler:
+AC_LANG([C])
+
+# Preference of tested compilers:
+case $host in
+  *-*-aix*)
+    PREFERRED_CC="ibm-clang_r ibm-clang gcc xlc_r xlc cc"
+    PREFERRED_CXX="ibm-clang++_r g++ c++"
+    ;;
+  *-*-freebsd* | *-*-openbsd*)
+    PREFERRED_CC="clang gcc cc"
+    PREFERRED_CXX="clang++ g++ c++"
+    ;;
+  *)
+    PREFERRED_CC="gcc icc clang icx suncc ccomp cc"
+    PREFERRED_CXX="g++ icpc clang++ icpx c++"
+    ;;
+esac
+
+# Find CC:
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+AC_PROG_CC([$PREFERRED_CC])
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+
+# These compilers do not fully support C++11.  Disable CXX to avoid mixing
+# different C and C++ compilers:
+case $CC in
+  *xlc* | suncc | ccomp)
+    ENABLE_CXX="no"
+    ;;
+esac
+
+# Find CXX:
+if test x"$ENABLE_CXX" = x"yes"; then
+  saved_cxxflags="$CXXFLAGS"
+  saved_ldxxflags="$LDXXFLAGS"
+  AC_PROG_CXX([$PREFERRED_CXX])
+  CXXFLAGS="$saved_cxxflags"
+  LDXXFLAGS="$saved_ldxxflags"
+fi
+
+# Check availability of -O2:
+AC_MSG_CHECKING(for -O2)
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+CFLAGS="-O2"
+LDFLAGS=
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[have_O2=yes],[have_O2=no])
+AC_MSG_RESULT($have_O2)
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+
+# Find ar and ranlib:
+AC_CHECK_TOOL(AR, ar, ar)
+AC_PROG_RANLIB
+
+# Force explicit configuration.  This section must be here because the size
+# checks in the next section require the correct CFLAGS.
+AC_ARG_VAR(MACHINE, [
+force configuration: x64, uint128, ansi64, ppro, ansi32, ansi-legacy, universal])
+
+M64=
+M32=
+if test -n "$MACHINE"; then
+  case $host in
+    *-*-aix*)
+      case $CC in
+        *xlc*)
+          M64="-q64"
+          M32="-q32"
+          ;;
+        *gcc*)
+          M64="-maix64"
+          M32="-maix32"
+          ;;
+        *)
+          M64="-m64"
+          M32="-m32"
+          ;;
+      esac
+      ;;
+    *)
+      M64="-m64"
+      M32="-m32"
+      ;;
+  esac
+
+  case "$MACHINE" in
+    x64 | uint128 | ansi64)
+      CFLAGS="$CFLAGS $M64"
+      CXXFLAGS="$CXXFLAGS $M64"
+      LDFLAGS="$LDFLAGS $M64"
+      LDXXFLAGS="$LDXXFLAGS $M64"
+      ;;
+    ppro | ansi32 | ansi-legacy)
+      CFLAGS="$CFLAGS $M32"
+      CXXFLAGS="$CXXFLAGS $M32"
+      LDFLAGS="$LDFLAGS $M32"
+      LDXXFLAGS="$LDXXFLAGS $M64"
+      ;;
+    universal)
+      :
+      ;;
+    *)
+      AC_MSG_ERROR([invalid MACHINE variable: $MACHINE])
+      ;;
+  esac
+
+  AC_MSG_CHECKING(whether the toolchain supports the chosen ABI)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+                 [machine_supported=yes],
+                 [machine_supported=no],
+                 [machine_supported=undefined])
+  AC_MSG_RESULT($machine_supported)
+  if test "$machine_supported" = no; then
+    AC_MSG_ERROR([toolchain cannot handle MACHINE=$MACHINE])
+  fi
+fi
+
+# Compiler dependent settings:
+MPD_PTHREAD=
+MPD_WARN=
+MPD_WARNXX=
+MPD_OPT="-O2"
+MPD_PGEN=
+MPD_PUSE=
+CONFIG_UNIVERSAL_AIX=
+FILTER_FOR_STATIC=
+case $CC in
+  *gcc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_UINT128
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic"
+    MPD_WARNXX="-Wall -Wextra -std=c++11 -pedantic"
+    MPD_OPT="-DNDEBUG -O2"
+    MPD_PGEN="-fprofile-generate -fprofile-values"
+    MPD_PUSE="-fprofile-use -freorder-blocks"
+    FILTER_FOR_STATIC="-flto% -ffat-lto-objects"
+    ;;
+  *icc*)
+    AR=xiar
+    CXX=icpc
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic -diag-disable=11074,11076"
+    MPD_WARNXX="-Wall -Wextra -std=c++11 -pedantic -diag-disable=11074,11076"
+    MPD_OPT="-DNDEBUG -O2"
+    MPD_PGEN="-wd11505 -prof-gen"
+    MPD_PUSE="-wd11505 -prof-use"
+    ;;
+  *clang* | *icx* | *emcc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_UINT128
+    MPD_PTHREAD="-pthread"
+    MPD_WARN="-Wall -Wextra -Wno-unknown-pragmas -std=c99 -pedantic"
+    MPD_WARNXX="-Wall -Wextra -Wexit-time-destructors -std=c++11 -pedantic"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *xlc*)
+    CONFIG_UNIVERSAL_AIX=$CONFIG_UNIVERSAL_AIX_ANSI64
+    CONFIGURE_LDFLAGS="-qmkshrobj -bnoentry -bE:.objs/symbols.exp"
+    LINK_STATIC="-bstatic"
+    LINK_DYNAMIC="-bshared"
+    MPD_PTHREAD="-qthreaded -D_THREAD_SAFE"
+    MPD_WARN="-qlanglvl=stdc99"
+    MPD_OPT="-DNDEBUG -O2 -qalias=ansi -qmaxmem=-1"
+    ;;
+  *suncc*)
+    MPD_WARN="-xc99"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *ccomp*)
+    ENABLE_SHARED=no
+    LINK_STATIC="-Wl,-znoexecstack"
+    MPD_WARN="-Wall -Wno-unknown-pragmas -std=c99 -fstruct-passing"
+    MPD_OPT="-DNDEBUG -O2"
+    ;;
+  *)
+    ;;
+esac
+
+AC_ARG_VAR(LDXXFLAGS, [C++ linker flags])
+
+# These variables are set unconditionally because CONFIGURE_LDFLAGS etc.
+# require the compiler frontends.  We leave them in the Makefiles in case
+# someone needs to edit the Makefiles manually.
+LD="$CC"
+LDXX="$CXX"
+
+AC_SUBST(CC)
+AC_SUBST(LD)
+AC_SUBST(CXX)
+AC_SUBST(LDXX)
+AC_SUBST(FILTER_FOR_STATIC)
+AC_SUBST(MPD_PTHREAD)
+AC_SUBST(MPD_PGEN)
+AC_SUBST(MPD_PUSE)
+
+# ==============================================================================
+#                   Headers, types, assembly, install program
+# ==============================================================================
+
+# Check for header files:
+AC_CHECK_HEADERS(pthread.h)
+
+# Type availability checks:
+AC_TYPE_SIZE_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_CHECK_TYPE(__uint128_t, AC_DEFINE(HAVE_UINT128_T, 1,
+              [Define if your compiler provides __uint128_t.]),,)
+
+# Sizes of various types:
+AC_CHECK_SIZEOF(size_t, 4)
+AC_CHECK_SIZEOF(__uint128_t, 16)
+
+# x64 with gcc asm:
+AC_MSG_CHECKING(for x64 gcc inline assembler)
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[
+__asm__ __volatile__ ("movq %rcx, %rax");
+]])],[have_gcc_asm_for_x64=yes],[have_gcc_asm_for_x64=no])
+AC_MSG_RESULT($have_gcc_asm_for_x64)
+if test "$have_gcc_asm_for_x64" = yes; then
+AC_DEFINE(HAVE_GCC_ASM_FOR_X64, 1,
+[Define if we can use x64 gcc inline assembler.])
+fi
+
+# x87 with gcc asm:
+AC_MSG_CHECKING(for x87 gcc inline assembler)
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[
+  unsigned short cw;
+  __asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
+  __asm__ __volatile__ ("fldcw %0" : : "m" (cw));
+]])],[have_gcc_asm_for_x87=yes],[have_gcc_asm_for_x87=no])
+AC_MSG_RESULT($have_gcc_asm_for_x87)
+if test "$have_gcc_asm_for_x87" = yes; then
+AC_DEFINE(HAVE_GCC_ASM_FOR_X87, 1,
+[Define if we can use x87 gcc inline assembler.])
+fi
+
+# Install program:
+AC_PROG_INSTALL
+AC_SUBST(INSTALL)
+
+# ==============================================================================
+#                             Detect toolchain bugs
+# ==============================================================================
+
+# _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect:
+#    http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+AC_MSG_CHECKING(for glibc _FORTIFY_SOURCE/memmove bug)
+saved_cflags="$CFLAGS"
+saved_ldflags="$LDFLAGS"
+CFLAGS="-O2 -D_FORTIFY_SOURCE=2"
+if test "$have_O2" = no; then
+  CFLAGS=
+fi
+LDFLAGS=
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+void foo(void *p, void *q) { memmove(p, q, 19); }
+int main() {
+  char a[32] = "123456789000000000";
+  foo(&a[9], a);
+  if (strcmp(a, "123456789123456789000000000") != 0)
+    return 1;
+  foo(a, &a[9]);
+  if (strcmp(a, "123456789000000000") != 0)
+    return 1;
+  return 0;
+}
+]])],
+[have_glibc_memmove_bug=no],
+[have_glibc_memmove_bug=yes],
+[have_glibc_memmove_bug=undefined])
+CFLAGS="$saved_cflags"
+LDFLAGS="$saved_ldflags"
+AC_MSG_RESULT($have_glibc_memmove_bug)
+if test "$have_glibc_memmove_bug" = yes; then
+  AC_DEFINE(HAVE_GLIBC_MEMMOVE_BUG, 1,
+  [Define if glibc has incorrect _FORTIFY_SOURCE wrappers
+   for memmove and bcopy.])
+fi
+
+# ==============================================================================
+#                            Optimized configurations
+# ==============================================================================
+
+# Auto-detect machine dependent settings:
+AIX_AR=
+AIX_RANLIB=
+EXPORTSYMS=
+OBJECT_SUFFIX=
+DETECTED_MACHINE=
+if test $ac_cv_sizeof_size_t -eq 8; then
+  AIX_AR="ar -X64"
+  AIX_RANLIB="ranlib -X64"
+  EXPORTSYMS="symbols64.exp"
+  OBJECT_SUFFIX="4_64.o"
+  if test $have_gcc_asm_for_x64 = yes; then
+    DETECTED_MACHINE="x64"
+  elif test $ac_cv_type___uint128_t = yes; then
+    DETECTED_MACHINE="uint128"
+  else
+    DETECTED_MACHINE="ansi64"
+  fi
+else
+  AIX_AR="ar -X32"
+  AIX_RANLIB="ranlib -X32"
+  EXPORTSYMS="symbols32.exp"
+  OBJECT_SUFFIX="4.o"
+  DETECTED_MACHINE="ansi32"
+  if test $have_gcc_asm_for_x87 = yes; then
+    case $CC in
+      *gcc* | *clang*) # icc >= 11.0 works as well
+         case $host in
+           *-*-darwin*)
+             ;;
+           *)
+             DETECTED_MACHINE="ppro"
+             ;;
+         esac
+         ;;
+    esac
+  fi
+fi
+
+if test -z "$MACHINE"; then
+  MACHINE="$DETECTED_MACHINE"
+fi
+
+# Set configuration variables:
+MPD_ABI=
+MPD_PREC=9
+case "$MACHINE" in
+  x64)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DASM"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  uint128)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DANSI -DHAVE_UINT128_T"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  ansi64)
+    MPD_HEADER_CONFIG=$CONFIG_64
+    MPD_ABI=$M64
+    MPD_CONFIG="-DCONFIG_64 -DANSI"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M64"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M64"
+    MPD_PREC=19
+    ;;
+  ppro)
+    MPD_HEADER_CONFIG=$CONFIG_32
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DPPRO -DASM"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    # Some versions of gcc miscompile inline asm:
+    #    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491
+    #    http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html
+    case $CC in
+      *gcc*)
+          AC_MSG_CHECKING(for gcc ipa-pure-const bug)
+          saved_cflags="$CFLAGS"
+          saved_ldflags="$LDFLAGS"
+          CFLAGS="-O2"
+          LDFLAGS=
+          AC_RUN_IFELSE([AC_LANG_SOURCE([[
+          __attribute__((noinline)) int
+          foo(int *p) {
+            int r;
+            asm ( "movl \$6, (%1)\n\t"
+                  "xorl %0, %0\n\t"
+                  : "=r" (r) : "r" (p) : "memory"
+            );
+            return r;
+          }
+          int main() {
+            int p = 8;
+            if ((foo(&p) ? : p) != 6)
+              return 1;
+            return 0;
+          }
+          ]])],
+          [have_ipa_pure_const_bug=no],
+          [have_ipa_pure_const_bug=yes],
+          [have_ipa_pure_const_bug=undefined])
+          CFLAGS="$saved_cflags"
+          LDFLAGS="$saved_ldflags"
+          AC_MSG_RESULT($have_ipa_pure_const_bug)
+          if test "$have_ipa_pure_const_bug" = yes; then
+            MPD_CONFIG="$MPD_CONFIG -fno-ipa-pure-const"
+            AC_DEFINE(HAVE_IPA_PURE_CONST_BUG, 1,
+                      [Define if gcc has the ipa-pure-const bug.])
+          fi
+          ;;
+    esac
+    ;;
+  ansi32)
+    MPD_HEADER_CONFIG=$CONFIG_32
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DANSI"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    ;;
+  ansi-legacy)
+    MPD_HEADER_CONFIG=$CONFIG_32_LEGACY
+    MPD_ABI=$M32
+    MPD_CONFIG="-DCONFIG_32 -DANSI -DLEGACY_COMPILER"
+    CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $M32"
+    CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $M32"
+    ;;
+  universal)
+    MPD_HEADER_CONFIG=$CONFIG_UNIVERSAL
+    MPD_CONFIG="-DUNIVERSAL"
+    ;;
+  *)
+    AC_MSG_ERROR([cannot detect MACHINE])
+    ;;
+esac
+
+# Special cases:
+MPD_CXXOPT=
+MPD_GNU99=
+case $host in
+  *-*-aix*)
+    AR=$AIX_AR
+    RANLIB=$AIX_RANLIB
+    MPD_OPT="-D_THREAD_SAFE $MPD_OPT"
+    MPD_CXXOPT="-D_THREAD_SAFE"
+    case "$MACHINE" in
+    universal)
+      MPD_HEADER_CONFIG=$CONFIG_UNIVERSAL_AIX
+      AR="ar -X32_64"
+      RANLIB="ranlib -X32_64"
+      ;;
+    esac
+    ;;
+  *-*-netbsd*)
+    MPD_OPT="-D_REENTRANT $MPD_OPT"
+    MPD_CXXOPT="-D_REENTRANT"
+    ;;
+  *-*-solaris* | *-*-sunos*)
+    MPD_OPT="-D_REENTRANT $MPD_OPT"
+    MPD_CXXOPT="-D_REENTRANT"
+    case $CC in
+      *gcc*)
+        MPD_GNU99=-std=gnu99
+        ;;
+    esac
+    ;;
+esac
+
+AC_SUBST(AR)
+AC_SUBST(RANLIB)
+AC_SUBST(EXPORTSYMS)
+AC_SUBST(OBJECT_SUFFIX)
+AC_SUBST(MPD_HEADER_CONFIG)
+AC_SUBST(MPD_GNU99)
+AC_SUBST(MPD_PREC)
+
+# ==============================================================================
+#                         Substitute remaining variables
+# ==============================================================================
+
+if test -z "$INITIAL_CFLAGS"; then
+  CONFIGURE_LIBMPDEC_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT $MPD_ABI"
+  CONFIGURE_CFLAGS="$MPD_WARN $MPD_OPT $MPD_ABI"
+else
+  CONFIGURE_LIBMPDEC_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT $MPD_ABI $INITIAL_CFLAGS"
+  CONFIGURE_CFLAGS="$MPD_WARN $MPD_OPT $MPD_ABI $INITIAL_CFLAGS"
+fi
+
+if test "$have_glibc_memmove_bug" = yes; then
+  CONFIGURE_LIBMPDEC_CFLAGS="$CONFIGURE_LIBMPDEC_CFLAGS -U_FORTIFY_SOURCE"
+  CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -U_FORTIFY_SOURCE"
+fi
+
+if test -z "$INITIAL_CXXFLAGS"; then
+  CONFIGURE_CXXFLAGS="$MPD_WARNXX $MPD_CXXOPT -DNDEBUG -O3 $MPD_ABI"
+else
+  CONFIGURE_CXXFLAGS="$MPD_WARNXX $MPD_CXXOPT -DNDEBUG -O3 $MPD_ABI $INITIAL_CXXFLAGS"
+fi
+
+if test -n "$INITIAL_LDFLAGS"; then
+  CONFIGURE_LDFLAGS="$CONFIGURE_LDFLAGS $INITIAL_LDFLAGS"
+fi
+
+if test -n "$INITIAL_LDXXFLAGS"; then
+  CONFIGURE_LDXXFLAGS="$CONFIGURE_LDXXFLAGS $INITIAL_LDXXFLAGS"
+fi
+
+AC_SUBST(CONFIGURE_LIBMPDEC_CFLAGS)
+AC_SUBST(CONFIGURE_CFLAGS)
+AC_SUBST(CONFIGURE_LDFLAGS)
+AC_SUBST(CONFIGURE_CXXFLAGS)
+AC_SUBST(CONFIGURE_LDXXFLAGS)
+
+# ==============================================================================
+#                                  Write output
+# ==============================================================================
+
+AC_OUTPUT
+
+GLIBC_MEMMOVE_BUG_WARN="
+***************************** WARNING *********************************
+
+Detected glibc _FORTIFY_SOURCE/memmove bug. See:
+
+  http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+
+Enabling -U_FORTIFY_SOURCE workaround. If -D_FORTIFY_SOURCE is also
+present in the command line, make sure that the order of the two
+options is:
+
+  ... -D_FORTIFY_SOURCE=2 ... -U_FORTIFY_SOURCE ...
+
+A better solution is to upgrade glibc or to report the bug to your
+OS vendor.
+
+***************************** WARNING *********************************
+"
+
+if test "$have_glibc_memmove_bug" = yes; then
+  echo "$GLIBC_MEMMOVE_BUG_WARN"
+fi
diff --git a/doc/COPYRIGHT.txt b/doc/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..d6748e8
--- /dev/null
@@ -0,0 +1,60 @@
+
+SOURCE CODE LICENSE
+===================
+
+Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+DOCUMENTATION LICENSE
+=====================
+
+Copyright 2010-2025 Stefan Krah. All rights reserved.
+
+Redistribution and use in source and 'compiled' forms (HTML, PDF, PostScript
+and so forth) with or without modification, are permitted provided that the
+following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer as the first
+      lines of this file unmodified.
+
+   2. Modified documents must carry a notice that modification has
+      occurred. This notice must also be present in any compiled form.
+
+   3. Redistributions in compiled form (converted to HTML, PDF,
+      PostScript and other formats) must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other materials provided with the
+      distribution.
+
+THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/doc/libmpdec++.3 b/doc/libmpdec++.3
new file mode 100644 (file)
index 0000000..fd78c15
--- /dev/null
@@ -0,0 +1,32 @@
+.TH LIBMPDEC++ 3 2025-05-01 mpdecimal-4.0.1 "Programmer's Manual"
+.ft C
+.
+.SH NAME
+libmpdec++ \- 4.0.1
+.
+.SH SYNOPSIS
+.nf
+C++ library for correctly rounded arbitrary precision decimal floating-point
+arithmetic.
+.ne
+.
+.SH DESCRIPTION
+.nf
+The \fIlibmpdec++\fP library is part of \fImpdecimal\fP and fully implements the General
+Decimal Arithmetic Specification, see:
+
+   https://speleotrove.com/decimal/decarith.html
+
+As described in the scope section of the specification, the library conforms
+\- with minor restrictions \- to the \fIIEEE 754\-2008\fP Standard for Floating\-Point
+Arithmetic if the context is initialized with the appropriate parameters.
+
+Depending on the distribution, complete HTML documentation is available in the
+\fImpdecimal-doc\fP package or at:
+
+   https://www.bytereef.org/mpdecimal/index.html
+
+.ne
+.
+.SH SEE ALSO
+mpdecimal.3, libmpdec.3
diff --git a/doc/libmpdec.3 b/doc/libmpdec.3
new file mode 100644 (file)
index 0000000..cadcdef
--- /dev/null
@@ -0,0 +1,32 @@
+.TH LIBMPDEC 3 2025-05-01 mpdecimal-4.0.1 "Programmer's Manual"
+.ft C
+.
+.SH NAME
+libmpdec \- 4.0.1
+.
+.SH SYNOPSIS
+.nf
+C library for correctly rounded arbitrary precision decimal floating-point
+arithmetic.
+.ne
+.
+.SH DESCRIPTION
+.nf
+The \fIlibmpdec\fP library is part of \fImpdecimal\fP and fully implements the General
+Decimal Arithmetic Specification, see:
+
+   https://speleotrove.com/decimal/decarith.html
+
+As described in the scope section of the specification, the library conforms
+\- with minor restrictions \- to the \fIIEEE 754\-2008\fP Standard for Floating\-Point
+Arithmetic if the context is initialized with the appropriate parameters.
+
+Depending on the distribution, complete HTML documentation is available in the
+\fImpdecimal-doc\fP package or at:
+
+   https://www.bytereef.org/mpdecimal/index.html
+
+.ne
+.
+.SH SEE ALSO
+mpdecimal.3, libmpdec++.3
diff --git a/doc/mpdecimal.3 b/doc/mpdecimal.3
new file mode 100644 (file)
index 0000000..04c7bea
--- /dev/null
@@ -0,0 +1,32 @@
+.TH MPDECIMAL 3 2025-05-01 mpdecimal-4.0.1 "Programmer's Manual"
+.ft C
+.
+.SH NAME
+mpdecimal \- 4.0.1
+.
+.SH SYNOPSIS
+.nf
+C/C++ libraries for correctly rounded arbitrary precision decimal floating-
+point arithmetic.
+.ne
+.
+.SH DESCRIPTION
+.nf
+The \fIlibmpdec\fP and \fIlibmpdec++\fP libraries fully implement the General Decimal
+Arithmetic Specification, see:
+
+   https://speleotrove.com/decimal/decarith.html
+
+As described in the scope section of the specification, the libraries conform
+\- with minor restrictions \- to the \fIIEEE 754\-2008\fP Standard for Floating\-Point
+Arithmetic if the context is initialized with the appropriate parameters.
+
+Depending on the distribution, complete HTML documentation is available in the
+\fImpdecimal-doc\fP package or at:
+
+   https://www.bytereef.org/mpdecimal/index.html
+
+.ne
+.
+.SH SEE ALSO
+libmpdec.3, libmpdec++.3
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..ec298b5
--- /dev/null
@@ -0,0 +1,541 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='  '
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
+  -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -p) cpprog="$cpprog -p";;
+
+    -s) stripcmd=$stripprog;;
+
+    -S) backupsuffix="$2"
+        shift;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+       */) dst=$dst$dstbase;;
+       *)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+       # The $RANDOM variable is not portable (e.g., dash).  Use it
+       # here however when possible just to lower collision chance.
+       tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+       trap '
+         ret=$?
+         rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+         exit $ret
+       ' 0
+
+       # Because "mkdir -p" follows existing symlinks and we likely work
+       # directly in world-writeable /tmp, make sure that the '$tmpdir'
+       # directory is successfully created first before we actually test
+       # 'mkdir -p'.
+       if (umask $mkdir_umask &&
+           $mkdirprog $mkdir_mode "$tmpdir" &&
+           exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+       then
+         if test -z "$dir_arg" || {
+              # Check for POSIX incompatibilities with -m.
+              # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+              # other-writable bit of parent directory when it shouldn't.
+              # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+              test_tmpdir="$tmpdir/a"
+              ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+              case $ls_ld_tmpdir in
+                d????-?r-*) different_mode=700;;
+                d????-?--*) different_mode=755;;
+                *) false;;
+              esac &&
+              $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+              }
+            }
+         then posix_mkdir=:
+         fi
+         rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+       else
+         # Remove any dirs left behind by ancient mkdir implementations.
+         rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+       fi
+       trap '' 0;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask $mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+        # Create $dsttmp read-write so that cp doesn't create it read-only,
+        # which would cause strip to fail.
+        if test -z "$doit"; then
+          : >"$dsttmp" # No need to fork-exec 'touch'.
+        else
+          $doit touch "$dsttmp"
+        fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/libmpdec++/.objs/README.txt b/libmpdec++/.objs/README.txt
new file mode 100644 (file)
index 0000000..c46d57e
--- /dev/null
@@ -0,0 +1,3 @@
+
+Directory for shared object files.
+
diff --git a/libmpdec++/.pc/libmpdec++.pc.in b/libmpdec++/.pc/libmpdec++.pc.in
new file mode 100644 (file)
index 0000000..c6d1cda
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libmpdec++
+Description: C++ library for decimal floating point arithmetic
+Version: 4.0.1
+URL: https://www.bytereef.org
+Requires: libmpdec = 4.0.1
+Cflags: -I${includedir}
+Libs: -L${libdir} -lmpdec++ -pthread
diff --git a/libmpdec++/.profile/train.sh b/libmpdec++/.profile/train.sh
new file mode 100755 (executable)
index 0000000..1f2c738
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+PORTABLE_PWD=`pwd`
+
+LD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$LD_LIBRARY_PATH"
+DYLD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$DYLD_LIBRARY_PATH"
+LD_64_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$LD_64_LIBRARY_PATH"
+LD_32_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$LD_32_LIBRARY_PATH"
+LD_LIBRARY_PATH_64="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$LD_LIBRARY_PATH_64"
+LD_LIBRARY_PATH_32="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD:$LD_LIBRARY_PATH_32"
+PATH="$LD_LIBRARY_PATH:$PATH"
+export LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH
+export LD_64_LIBRARY_PATH
+export LD_32_LIBRARY_PATH
+export LD_LIBRARY_PATH_64
+export LD_LIBRARY_PATH_32
+
+MPD_PREC="$1"
+MPD_DPREC=`expr "$MPD_PREC" \* 2`
+
+if [ ! -f ./bench_full -a ! -f ./bench_shared ]; then
+    printf "\n./train.sh: error: no training files found\n\n"
+    exit 1
+fi
+
+if [ -f ./bench_full ]; then
+    ./bench_full "$MPD_PREC" 1000 > /dev/null 2>&1
+    ./bench_full "$MPD_DPREC" 1000 > /dev/null 2>&1
+fi
+
+if [ -f ./bench_shared ]; then
+    ./bench_shared "$MPD_PREC" 1000000 > /dev/null 2>&1
+    ./bench_shared "$MPD_DPREC" 1000000 > /dev/null 2>&1
+fi
diff --git a/libmpdec++/Makefile.in b/libmpdec++/Makefile.in
new file mode 100644 (file)
index 0000000..decbbcc
--- /dev/null
@@ -0,0 +1,185 @@
+
+# ==============================================================================
+#                          Unix Makefile for libmpdec++
+# ==============================================================================
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+ENABLE_STATIC = @ENABLE_STATIC@
+ENABLE_SHARED = @ENABLE_SHARED@
+ENABLE_MINGW = @ENABLE_MINGW@
+
+FPIC = @FPIC@
+LIBSTATIC = @LIBSTATIC@
+LIBNAME = @LIBNAME@
+LIBSONAME = @LIBSONAME@
+LIBSHARED = @LIBSHARED@
+LIBIMPORT = @LIBIMPORT@
+
+LIBSTATIC_CXX = @LIBSTATIC_CXX@
+LIBNAME_CXX = @LIBNAME_CXX@
+LIBSONAME_CXX = @LIBSONAME_CXX@
+LIBSHARED_CXX = @LIBSHARED_CXX@
+LIBIMPORT_CXX = @LIBIMPORT_CXX@
+LIBSHARED_USE_AR = @LIBSHARED_USE_AR@
+LINK_STATIC = @LINK_STATIC@
+LINK_DYNAMIC = @LINK_DYNAMIC@
+
+OBJECT_SUFFIX = @OBJECT_SUFFIX@
+COPY = @cp -f $1 $2
+
+CXX = @CXX@
+LDXX = @LDXX@
+AR = @AR@
+RANLIB = @RANLIB@
+MPD_PTHREAD = @MPD_PTHREAD@
+MPD_PGEN = @MPD_PGEN@
+MPD_PUSE = @MPD_PUSE@
+MPD_PREC = @MPD_PREC@
+
+MPD_CXX = $(strip $(CXX) $(MPD_PTHREAD))
+MPD_LDXX = $(strip $(LDXX) $(MPD_PTHREAD))
+
+FILTER_FOR_STATIC = @FILTER_FOR_STATIC@
+
+CONFIGURE_CXXFLAGS = @CONFIGURE_CXXFLAGS@
+MPD_CXXFLAGS_COMMON = $(strip $(filter-out $(CXXFLAGS),$(CONFIGURE_CXXFLAGS)) $(CXXFLAGS))
+MPD_CXXFLAGS_SHARED = $(strip $(filter-out $(FPIC),$(MPD_CXXFLAGS_COMMON)) $(FPIC))
+MPD_CXXFLAGS = $(strip $(filter-out $(FILTER_FOR_STATIC),$(MPD_CXXFLAGS_COMMON)))
+
+CONFIGURE_LDXXFLAGS = @CONFIGURE_LDXXFLAGS@
+MPD_LDXXFLAGS = $(strip $(filter-out $(LDXXFLAGS),$(CONFIGURE_LDXXFLAGS)) $(LDXXFLAGS))
+
+LINK_LIBSTATIC = $(strip $(LINK_STATIC) $(LIBSTATIC_CXX) ../libmpdec/$(LIBSTATIC) $(LINK_DYNAMIC))
+
+ifeq ($(MAKECMDGOALS), profile_gen)
+  MPD_CXXFLAGS_COMMON += $(MPD_PGEN)
+  MPD_LDXXFLAGS += $(MPD_PGEN)
+endif
+ifeq ($(MAKECMDGOALS), profile_use)
+  MPD_CXXFLAGS_COMMON += $(MPD_PUSE)
+  MPD_LDXXFLAGS += $(MPD_PUSE)
+endif
+
+
+MPD_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+    MPD_TARGETS += $(LIBSTATIC_CXX)
+endif
+ifeq ($(ENABLE_SHARED), yes)
+    MPD_TARGETS += $(LIBSHARED_CXX)
+endif
+
+
+default: $(MPD_TARGETS)
+
+
+OBJS := decimal.o
+SHARED_OBJS := .objs/decimal.o
+
+
+ifeq ($(LIBSHARED_USE_AR), yes)
+AR_STATIC_OBJS := $(OBJS:.o=$(OBJECT_SUFFIX))
+AR_SHARED_OBJS := $(LIBSHARED:.o=$(OBJECT_SUFFIX))
+%$(OBJECT_SUFFIX): %.o
+       $(call COPY,$<,$@)
+
+# Disabled: C++ symbol extraction is not reliable.
+$(LIBSHARED_CXX):
+
+$(LIBSTATIC_CXX): Makefile $(AR_STATIC_OBJS)
+       $(AR) rc $(LIBSTATIC_CXX) $(AR_STATIC_OBJS)
+       $(RANLIB) $(LIBSTATIC_CXX)
+else
+$(LIBSTATIC_CXX): Makefile $(OBJS)
+       $(AR) rc $(LIBSTATIC_CXX) $(OBJS)
+       $(RANLIB) $(LIBSTATIC_CXX)
+
+$(LIBSHARED_CXX): Makefile $(SHARED_OBJS)
+ifeq ($(ENABLE_MINGW), yes)
+       $(LDXX) $(MPD_LDXXFLAGS) -o $(LIBSHARED_CXX) $(SHARED_OBJS) ../libmpdec/$(LIBIMPORT) -lm
+else
+       $(MPD_LDXX) -L../libmpdec $(MPD_LDXXFLAGS) -o $(LIBSHARED_CXX) $(SHARED_OBJS) -lmpdec -lm
+       ln -sf $(LIBSHARED_CXX) $(LIBNAME_CXX)
+       ln -sf $(LIBSHARED_CXX) $(LIBSONAME_CXX)
+endif
+endif
+
+
+decimal.o:\
+Makefile decimal.cc ../libmpdec/mpdecimal.h decimal.hh
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS) -c decimal.cc
+
+.objs/decimal.o:\
+Makefile decimal.cc ../libmpdec/mpdecimal.h decimal.hh
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS_SHARED) -c decimal.cc -o .objs/decimal.o
+
+
+bench: Makefile bench.cc ../libmpdec/mpdecimal.h decimal.hh $(LIBSTATIC_CXX)
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS) -o bench bench.cc $(LINK_LIBSTATIC) -lm
+
+bench_full: Makefile bench_full.cc ../libmpdec/mpdecimal.h decimal.hh $(LIBSTATIC_CXX)
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS) -o bench_full bench_full.cc $(LINK_LIBSTATIC) -lm
+
+bench_shared: Makefile bench.cc ../libmpdec/mpdecimal.h decimal.hh $(LIBSHARED_CXX)
+       $(MPD_CXX) -I. -I../libmpdec -L. -L../libmpdec $(MPD_CXXFLAGS_COMMON) -o bench_shared bench.cc -lm -lmpdec++ -lmpdec
+
+bench_full_shared: Makefile bench_full.cc ../libmpdec/mpdecimal.h decimal.hh $(LIBSHARED_CXX)
+       $(MPD_CXX) -I. -I../libmpdec -L. -L../libmpdec $(MPD_CXXFLAGS_COMMON) -o bench_full_shared bench_full.cc -lm -lmpdec++ -lmpdec
+
+
+factorial: Makefile examples/factorial.cc $(LIBSTATIC_CXX)
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS) -o factorial examples/factorial.cc $(LINK_LIBSTATIC) -lm
+
+pi: Makefile examples/pi.cc $(LIBSTATIC_CXX)
+       $(MPD_CXX) -I. -I../libmpdec $(MPD_CXXFLAGS) -o pi examples/pi.cc $(LINK_LIBSTATIC) -lm
+
+examples: factorial pi
+
+
+GEN_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+GEN_TARGETS += bench_full
+endif
+ifeq ($(ENABLE_SHARED), yes)
+GEN_TARGETS += bench_shared
+endif
+
+profile_gen: $(GEN_TARGETS)
+       ./.profile/train.sh $(MPD_PREC)
+
+profile_clean:
+       rm -f *.o *.so *.gch
+       rm -f bench bench_shared bench_full bench_full_shared factorial pi
+       rm -f $(LIBSTATIC_CXX) $(LIBNAME_CXX) $(LIBSONAME_CXX) $(LIBSHARED_CXX) $(LIBIMPORT_CXX)
+       cd .objs && rm -f *.o *.so *.gch
+
+profile_use: default
+
+profile:
+       $(MAKE) clean
+       $(MAKE) profile_gen
+       $(MAKE) profile_clean
+       $(MAKE) profile_use
+
+
+check: default
+       cd ../tests++ && $(MAKE) && ./runshort.sh
+
+check_local: default
+       cd ../tests++ && $(MAKE) && ./runshort.sh --local
+
+check_alloc: default
+       cd ../tests++ && $(MAKE) && ./runshort_alloc.sh
+
+
+clean:
+       rm -f *.o *.so *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock
+       rm -f bench bench_shared bench_full bench_full_shared factorial pi
+       rm -f $(LIBSTATIC_CXX) $(LIBNAME_CXX) $(LIBSONAME_CXX) $(LIBSHARED_CXX) $(LIBIMPORT_CXX)
+       cd .objs && rm -f *.o *.so *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock
+
+distclean: clean
+       rm -f Makefile .pc/libmpdec++.pc
diff --git a/libmpdec++/Makefile.vc b/libmpdec++/Makefile.vc
new file mode 100644 (file)
index 0000000..33a4126
--- /dev/null
@@ -0,0 +1,99 @@
+
+# ======================================================================
+#                Visual C (nmake) Makefile for libmpdec++
+# ======================================================================
+
+SRCDIR = ..\libmpdec
+
+LIBSTATIC = libmpdec-4.0.1.lib
+LIBIMPORT = libmpdec-4.0.1.dll.lib
+LIBSHARED = libmpdec-4.0.1.dll
+
+LIBSTATIC_CXX = libmpdec++-4.0.1.lib
+LIBIMPORT_CXX = libmpdec++-4.0.1.dll.lib
+LIBSHARED_CXX = libmpdec++-4.0.1.dll
+
+!if "$(DEBUG)" == "1"
+OPT = /MTd /Od /Zi /EHsc
+OPT_SHARED = /MDd /Od /Zi /EHsc
+!else
+OPT = /MT /O2 /GS /EHsc /DNDEBUG
+OPT_SHARED = /MD /O2 /GS /EHsc /DNDEBUG
+!endif
+
+!if "$(CC)" == "clang-cl"
+WARN = /W4 -Wno-undefined-inline
+!else
+WARN = /W4
+!endif
+
+MPD_CXXFLAGS = $(WARN) /nologo $(OPT)
+MPD_CXXFLAGS_SHARED = /DBUILD_LIBMPDECXX $(WARN) /nologo $(OPT_SHARED) $(PGOFLAGS)
+MPD_BIN_CXXFLAGS_SHARED = $(WARN) /nologo $(OPT_SHARED) $(PGOFLAGS)
+
+MPD_LDXXFLAGS= /DLL /MANIFEST $(LDXXFLAGS)
+
+
+default: $(LIBSTATIC_CXX) $(LIBSHARED_CXX)
+
+
+OBJS = decimal.obj
+
+SHARED_OBJS = .objs\decimal.obj
+
+
+$(LIBSTATIC_CXX): Makefile $(OBJS)
+       -@if exist $@ del $(LIBSTATIC_CXX)
+       lib /out:$(LIBSTATIC_CXX) $(OBJS)
+
+$(LIBSHARED_CXX): Makefile $(SHARED_OBJS)
+       -@if exist $@ del $(LIBSHARED_CXX)
+       link $(MPD_LDXXFLAGS) /out:$(LIBSHARED_CXX) /implib:$(LIBIMPORT_CXX) /LIBPATH:$(SRCDIR) $(SHARED_OBJS) $(LIBIMPORT)
+       mt -manifest $(LIBSHARED_CXX).manifest -outputresource:$(LIBSHARED_CXX);2
+
+
+decimal.obj:\
+Makefile decimal.cc ..\libmpdec\mpdecimal.h decimal.hh
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_CXXFLAGS) -c decimal.cc
+
+.objs\decimal.obj:\
+Makefile decimal.cc ..\libmpdec\mpdecimal.h decimal.hh
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_CXXFLAGS_SHARED) -c decimal.cc /Fo.objs\decimal.obj
+
+
+FORCE:
+
+bench: FORCE
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_CXXFLAGS) /O2 /EHsc /Fo:bench bench.cc $(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+
+bench_shared: FORCE
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_BIN_CXXFLAGS_SHARED) /O2 /EHsc /Fo:bench_shared bench.cc $(LIBIMPORT_CXX) $(SRCDIR)\$(LIBIMPORT)
+
+examples: FORCE
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_CXXFLAGS) /O2 /EHsc /Fo:factorial examples/factorial.cc $(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+       $(CXX) "-I." "-I$(SRCDIR)" $(MPD_CXXFLAGS) /O2 /EHsc /Fo:pi examples/pi.cc $(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+
+
+clean: FORCE
+       -@if exist *.obj del *.obj
+       -@cd .objs
+       -@if exist *.obj del *.obj
+       -@cd ..
+       -@if exist *.dll del *.dll
+       -@if exist *.exp del *.exp
+       -@if exist *.lib del *.lib
+       -@if exist *.ilk del *.ilk
+       -@if exist *.pdb del *.pdb
+       -@if exist *.pgc del *.pgc
+       -@if exist *.pgd del *.pgd
+       -@if exist *.manifest del *.manifest
+       -@if exist *.exe del *.exe
+       -@cd ..\tests++
+       -@if exist Makefile nmake clean
+
+distclean: FORCE
+       nmake clean
+       -@if exist Makefile del Makefile
+       -@cd ..\tests
+       -@if exist Makefile nmake distclean
+
diff --git a/libmpdec++/bench.cc b/libmpdec++/bench.cc
new file mode 100644 (file)
index 0000000..cd4350b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+#include <cassert>
+#include <ctime>
+
+#include "mpdecimal.h"
+#include "decimal.hh"
+
+
+using decimal::Decimal;
+using decimal::Context;
+using decimal::context;
+
+
+/* Nonsense version of escape-time algorithm for calculating a Mandelbrot
+ * set. Just for benchmarking. */
+void
+color_point(Decimal& x0, const Decimal& y0, long maxiter)
+{
+    Decimal x = 0;
+    Decimal y = 0;
+
+    Decimal sq_x = 0;
+    Decimal sq_y = 0;
+
+    Decimal two{2};
+
+    for (long i = 0; i < maxiter; i++) {
+        y = x * y;
+        y = y * two;
+        y = y + y0;
+
+        x = sq_x - sq_y;
+        x = x + x0;
+
+        sq_x = x * x;
+        sq_y = y * y;
+    }
+
+    x0 = x;
+}
+
+int
+main(int argc, char **argv)
+{
+    const double clocks_per_sec = CLOCKS_PER_SEC;
+    clock_t start_clock, end_clock;
+    uint32_t prec;
+    long iter;
+
+    assert(MPD_MINALLOC == 4);
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: bench prec iter\n");
+        exit(1);
+    }
+    prec = strtoul(argv[1], NULL, 10);
+    iter = strtol(argv[2], NULL, 10);
+
+    context.prec(prec);
+
+    Decimal x0{"0.222"};
+    Decimal y0{"0.333"};
+
+    start_clock = clock();
+    color_point(x0, y0, iter);
+    end_clock = clock();
+
+    std::cout << x0 << std::endl;
+
+    fprintf(stderr, "time: %f\n\n", (end_clock-start_clock)/clocks_per_sec);
+
+    return 0;
+}
+
diff --git a/libmpdec++/bench_full.cc b/libmpdec++/bench_full.cc
new file mode 100644 (file)
index 0000000..6f94908
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+
+#include "mpdecimal.h"
+#include "decimal.hh"
+
+
+using decimal::Decimal;
+using decimal::Context;
+using decimal::context;
+
+
+/*
+ * Example from: http://en.wikipedia.org/wiki/Mandelbrot_set
+ *
+ * Escape time algorithm for drawing the set:
+ *
+ * Point x0, y0 is deemed to be in the Mandelbrot set if the return
+ * value is maxiter. Lower return values indicate how quickly points
+ * escaped and can be used for coloring.
+ */
+int
+color_point(const Decimal& x0, const Decimal& y0, const int maxiter)
+{
+    int i = 0;
+
+    Decimal x = 0;
+    Decimal y = 0;
+    Decimal sq_x = 0;
+    Decimal sq_y = 0;
+
+    const Decimal two = 2;
+    const Decimal four = 4;
+    Decimal c = 0;
+
+    for (i = 0; i < maxiter && c <= four; i++) {
+        y = x * y;
+        y *= two;
+        y += y0;
+
+        x = sq_x - sq_y;
+        x += x0;
+
+        sq_x = x * x;
+        sq_y = y * y;
+        c = sq_x + sq_y;
+    }
+
+    return i;
+}
+
+int
+main(int argc, char **argv)
+{
+    const double clocks_per_sec = CLOCKS_PER_SEC;
+    clock_t start_clock, end_clock;
+    uint32_t prec = 19;
+    int iter = 1000;
+    int points[40][80];
+    int i, j;
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: ./bench prec iter\n");
+        exit(1);
+    }
+    prec = strtoul(argv[1], NULL, 10);
+    iter = strtol(argv[2], NULL, 10);
+
+    context.prec(prec);
+
+    Decimal x0;
+    Decimal sqrt_2 = Decimal(2).sqrt();
+    Decimal xstep = sqrt_2 / 40;
+    Decimal ystep = sqrt_2 / 20;
+
+    start_clock = clock();
+    Decimal y0 = sqrt_2;
+    for (i = 0; i < 40; i++) {
+        x0 = -sqrt_2;
+        for (j = 0; j < 80; j++) {
+            points[i][j] = color_point(x0, y0, iter);
+            x0 += xstep;
+        }
+        y0 -= ystep;
+    }
+    end_clock = clock();
+
+#ifdef BENCH_VERBOSE
+    for (i = 0; i < 40; i++) {
+        for (j = 0; j < 80; j++) {
+            if (points[i][j] == iter) {
+                putchar('*');
+            }
+            else if (points[i][j] >= 10) {
+                putchar('+');
+            }
+            else if (points[i][j] >= 5) {
+                putchar('.');
+            }
+            else {
+                putchar(' ');
+            }
+        }
+        putchar('\n');
+    }
+    putchar('\n');
+#else
+    (void)points; /* suppress gcc warning */
+#endif
+
+    printf("time: %f\n\n", (end_clock-start_clock)/clocks_per_sec);
+
+    return 0;
+}
+
diff --git a/libmpdec++/decimal.cc b/libmpdec++/decimal.cc
new file mode 100644 (file)
index 0000000..292d678
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <cstdint>
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include "mpdecimal.h"
+#include "decimal.hh"
+
+
+/*****************************************************************************/
+/*                               Library init                                */
+/*****************************************************************************/
+
+namespace {
+class LibraryInit {
+ public:
+  LibraryInit() { mpd_setminalloc(decimal::MINALLOC); }
+};
+
+const LibraryInit init;
+}  // namespace
+
+
+/*****************************************************************************/
+/*                                  Context                                  */
+/*****************************************************************************/
+
+namespace {
+
+typedef struct {
+  const uint32_t flag;
+  const char *name;
+  const char *fqname;
+  void (* const raise)(const std::string& msg);
+} cmap;
+
+template<typename T>
+void
+raise(const std::string& msg)
+{
+  throw T(msg);
+}
+
+const cmap signal_map[] = {
+  { MPD_IEEE_Invalid_operation, "IEEEInvalidOperation", "decimal::IEEEInvalidOperation", raise<decimal::IEEEInvalidOperation> },
+  { MPD_Division_by_zero, "DivisionByZero", "decimal::DivisionByZero", raise<decimal::DivisionByZero> },
+  { MPD_Overflow, "Overflow", "decimal::Overflow", raise<decimal::Overflow> },
+  { MPD_Underflow, "Underflow", "decimal::Underflow", raise<decimal::Underflow> },
+  { MPD_Subnormal, "Subnormal", "decimal::Subnormal", raise<decimal::Subnormal> },
+  { MPD_Inexact, "Inexact", "decimal::Inexact", raise<decimal::Inexact> },
+  { MPD_Rounded, "Rounded", "decimal::Rounded", raise<decimal::Rounded> },
+  { MPD_Clamped, "Clamped", "decimal::Clamped", raise<decimal::Clamped> },
+  { UINT32_MAX, nullptr, nullptr, nullptr }
+};
+
+const cmap cond_map[] = {
+  { MPD_Invalid_operation, "InvalidOperation", "decimal::InvalidOperation", raise<decimal::InvalidOperation> },
+  { MPD_Conversion_syntax, "ConversionSyntax", "decimal::ConversionSyntax", raise<decimal::ConversionSyntax> },
+  { MPD_Division_impossible, "DivisionImpossible", "decimal::DivisionImpossible", raise<decimal::DivisionImpossible> },
+  { MPD_Division_undefined, "DivisionUndefined", "decimal::DivisionUndefined", raise<decimal::DivisionUndefined> },
+  { UINT32_MAX, nullptr, nullptr, nullptr }
+};
+
+std::string
+signals(const uint32_t flags)
+{
+  std::string s;
+  s.reserve(MPD_MAX_SIGNAL_LIST);
+
+  s += "[";
+  for (const cmap *c = signal_map; c->flag != UINT32_MAX;  c++) {
+    if (flags & c->flag) {
+      if (s != "[") {
+        s += ", ";
+      }
+      s += c->name;
+    }
+  }
+
+  s += "]";
+
+  return s;
+}
+
+std::string
+flags(const uint32_t flags)
+{
+  std::string s;
+  s.reserve(MPD_MAX_FLAG_LIST);
+
+  s += "[";
+
+  for (const cmap *c = cond_map; c->flag != UINT32_MAX;  c++) {
+    if (flags & c->flag) {
+      if (s != "[") {
+        s += ", ";
+      }
+      s += c->name;
+    }
+  }
+
+  for (const cmap *c = signal_map+1; c->flag != UINT32_MAX;  c++) {
+    if (flags & c->flag) {
+      if (s != "[") {
+        s += ", ";
+      }
+      s += c->name;
+    }
+  }
+
+  s += "]";
+
+  return s;
+}
+
+/* Context for exact calculations with (practically) unbounded precision. */
+const decimal::Context maxcontext {
+  MPD_MAX_PREC,
+  MPD_MAX_EMAX,
+  MPD_MIN_EMIN,
+  MPD_ROUND_HALF_EVEN,
+  MPD_IEEE_Invalid_operation,
+  0,
+  0
+};
+
+}  // namespace
+
+
+/*****************************************************************************/
+/*                                Context API                                */
+/*****************************************************************************/
+
+namespace decimal {
+
+/* Used for default initialization of new contexts such as TLS contexts. */
+Context context_template {
+  16,                                                            /* prec */
+  999999,                                                        /* emax */
+  -999999,                                                       /* emin */
+  MPD_ROUND_HALF_EVEN,                                           /* rounding */
+  MPD_IEEE_Invalid_operation|MPD_Division_by_zero|MPD_Overflow,  /* traps */
+  0,                                                             /* clamp */
+  1                                                              /* allcr */
+};
+
+#if defined(__OpenBSD__) || defined(__sun) || defined(_MSC_VER) && defined(_DLL)
+Context& getcontext() {
+  static thread_local Context _context{context_template};
+  return _context;
+}
+#else
+thread_local Context context{context_template};
+#endif
+
+/* Factory function for creating a context for maximum unrounded arithmetic. */
+Context
+MaxContext()
+{
+    return Context(maxcontext);
+}
+
+/* Factory function for creating IEEE interchange format contexts. */
+Context
+IEEEContext(int bits)
+{
+  mpd_context_t ctx;
+
+  if (mpd_ieee_context(&ctx, bits) < 0) {
+    throw ValueError("argument must be a multiple of 32, with a maximum of " +
+                     std::to_string(MPD_IEEE_CONTEXT_MAX_BITS));
+  }
+
+  return Context(ctx);
+}
+
+void
+Context::raiseit(const uint32_t status)
+{
+  if (status & MPD_Malloc_error) {
+    throw MallocError("out of memory");
+  }
+
+  const std::string msg = flags(status);
+  for (const cmap *c = cond_map; c->flag != UINT32_MAX;  c++) {
+    if (status & c->flag) {
+      c->raise(msg);
+    }
+  }
+
+  for (const cmap *c = signal_map+1; c->flag != UINT32_MAX;  c++) {
+    if (status & c->flag) {
+      c->raise(msg);
+    }
+  }
+
+  throw RuntimeError("internal_error: unknown status flag");
+}
+
+std::string
+Context::repr() const
+{
+  const int rounding = round();
+  std::ostringstream ss;
+
+  if (rounding < 0 || rounding >= MPD_ROUND_GUARD) {
+    throw RuntimeError("internal_error: invalid rounding mode");
+  }
+
+  const char *round_str = mpd_round_string[rounding];
+
+  ss << "Context(prec=" << prec() << ", " <<
+                "emax=" << emax() << ", " <<
+                "emin=" << emin() << ", " <<
+                "round=" << round_str << ", " <<
+                "clamp=" << clamp() << ", " <<
+                "traps=" << signals(traps()) << ", " <<
+                "status=" << signals(status()) <<
+        ")";
+
+  return ss.str();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Context& c)
+{
+  os << c.repr();
+  return os;
+}
+
+
+/*****************************************************************************/
+/*                                Decimal API                                */
+/*****************************************************************************/
+
+Decimal
+Decimal::exact(const char *const s, Context& c)
+{
+  Decimal result;
+  uint32_t status = 0;
+
+  if (s == nullptr) {
+    throw ValueError("Decimal::exact: string argument is NULL");
+  }
+
+  mpd_qset_string_exact(result.get(), s, &status);
+  c.raise(status);
+  return result;
+}
+
+Decimal
+Decimal::exact(const std::string& s, Context& c)
+{
+  return Decimal::exact(s.c_str(), c);
+}
+
+Decimal
+Decimal::ln10(int64_t n, Context& c)
+{
+  Decimal result;
+  uint32_t status = 0;
+
+  if (n < 1 || n > MPD_MAX_PREC) {
+    throw ValueError("Decimal::ln10: prec argument must in [1, MAX_PREC]");
+  }
+
+  mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+  mpd_qln10(result.get(), nn, &status);
+
+  c.raise(status);
+  return result;
+}
+
+int32_t
+Decimal::radix()
+{
+  return 10;
+}
+std::string
+Decimal::repr(bool capitals) const
+{
+  std::string s = to_sci(capitals);
+
+  return "Decimal(\"" + s + "\")";
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Decimal& dec)
+{
+  os << dec.to_sci();
+
+  return os;
+}
+
+}  // namespace decimal
diff --git a/libmpdec++/decimal.hh b/libmpdec++/decimal.hh
new file mode 100644 (file)
index 0000000..9f847fc
--- /dev/null
@@ -0,0 +1,1289 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDECXX_DECIMAL_HH_
+#define LIBMPDECXX_DECIMAL_HH_
+
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+#include <atomic>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include <mpdecimal.h>
+
+#undef iscanonical /* math.h */
+#undef isfinite    /* math.h */
+#undef isinf       /* math.h */
+#undef isnan       /* math.h */
+#undef isnormal    /* math.h */
+#undef issubnormal /* math.h */
+#undef iszero      /* math.h */
+#undef isspecial   /* ctype.h */
+
+#undef IMPORTEXPORT
+#ifdef _MSC_VER
+  #if defined (BUILD_LIBMPDECXX)
+    #define IMPORTEXPORT __declspec(dllexport)
+  #elif defined(_DLL)
+    #define IMPORTEXPORT __declspec(dllimport)
+  #else
+    #define IMPORTEXPORT
+  #endif
+  #define ALWAYS_INLINE __forceinline
+#else
+  #define IMPORTEXPORT
+  #define ALWAYS_INLINE inline
+#endif
+
+
+namespace decimal {
+
+/******************************************************************************/
+/*                          Constants from libmpdec                           */
+/******************************************************************************/
+
+enum round {
+  ROUND_UP = MPD_ROUND_UP,               /* round away from 0               */
+  ROUND_DOWN = MPD_ROUND_DOWN,           /* round toward 0 (truncate)       */
+  ROUND_CEILING = MPD_ROUND_CEILING,     /* round toward +infinity          */
+  ROUND_FLOOR = MPD_ROUND_FLOOR,         /* round toward -infinity          */
+  ROUND_HALF_UP = MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
+  ROUND_HALF_DOWN = MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down             */
+  ROUND_HALF_EVEN = MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even          */
+  ROUND_05UP = MPD_ROUND_05UP,           /* round zero or five away from 0  */
+  ROUND_TRUNC = MPD_ROUND_TRUNC,         /* truncate, but set infinity      */
+  ROUND_GUARD = MPD_ROUND_GUARD
+};
+
+/*
+ * Aliases for a spelling that is consistent with the exceptions below.
+ * Use whichever form you prefer.
+ */
+constexpr uint32_t DecClamped = MPD_Clamped;
+constexpr uint32_t DecConversionSyntax = MPD_Conversion_syntax;
+constexpr uint32_t DecDivisionByZero = MPD_Division_by_zero;
+constexpr uint32_t DecDivisionImpossible = MPD_Division_impossible;
+constexpr uint32_t DecDivisionUndefined = MPD_Division_undefined;
+constexpr uint32_t DecFpuError = MPD_Fpu_error; /* unused */
+constexpr uint32_t DecInexact = MPD_Inexact;
+constexpr uint32_t DecInvalidContext = MPD_Invalid_context;
+constexpr uint32_t DecInvalidOperation = MPD_Invalid_operation;
+constexpr uint32_t DecMallocError = MPD_Malloc_error;
+constexpr uint32_t DecNotImplemented = MPD_Not_implemented; /* unused */
+constexpr uint32_t DecOverflow = MPD_Overflow;
+constexpr uint32_t DecRounded = MPD_Rounded;
+constexpr uint32_t DecSubnormal = MPD_Subnormal;
+constexpr uint32_t DecUnderflow = MPD_Underflow;
+
+/* Flag sets */
+constexpr uint32_t DecIEEEInvalidOperation = MPD_IEEE_Invalid_operation;
+                                            /* DecConversionSyntax */
+                                            /* DecDivisionImpossible */
+                                            /* DecDivisionUndefined */
+                                            /* DecFpuError */
+                                            /* DecInvalidContext */
+                                            /* DecInvalidOperation */
+                                            /* DecMallocError */
+
+constexpr uint32_t DecErrors = MPD_Errors;
+                               /* DecIEEEInvalidOperation */
+                               /* DecDivisionByZero */
+
+constexpr uint32_t DecMaxStatus = MPD_Max_status;
+                                  /* All flags */
+
+/* IEEEContext(): common arguments */
+constexpr int DECIMAL32 = MPD_DECIMAL32;
+constexpr int DECIMAL64 = MPD_DECIMAL64;
+constexpr int DECIMAL128 = MPD_DECIMAL128;
+
+/* IEEEContext(): maximum argument value */
+constexpr int IEEE_CONTEXT_MAX_BITS = MPD_IEEE_CONTEXT_MAX_BITS;
+
+
+/******************************************************************************/
+/*                             Decimal Exceptions                             */
+/******************************************************************************/
+
+class DecimalException : public std::runtime_error {
+  using std::runtime_error::runtime_error;
+};
+
+/* Signals */
+class IEEEInvalidOperation : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class DivisionByZero : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Overflow : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Underflow : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Subnormal : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Inexact : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Rounded : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class Clamped : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+/* Conditions */
+class InvalidOperation : public IEEEInvalidOperation {
+  using IEEEInvalidOperation::IEEEInvalidOperation;
+};
+
+class ConversionSyntax : public IEEEInvalidOperation {
+  using IEEEInvalidOperation::IEEEInvalidOperation;
+};
+
+class DivisionImpossible : public IEEEInvalidOperation {
+  using IEEEInvalidOperation::IEEEInvalidOperation;
+};
+
+class DivisionUndefined : public IEEEInvalidOperation {
+  using IEEEInvalidOperation::IEEEInvalidOperation;
+};
+
+
+/******************************************************************************/
+/*                               Other Exceptions                             */
+/******************************************************************************/
+
+class MallocError : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class RuntimeError : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+class ValueError : public DecimalException {
+  using DecimalException::DecimalException;
+};
+
+
+/******************************************************************************/
+/*                              Context object                                */
+/******************************************************************************/
+
+class Context;
+
+IMPORTEXPORT extern Context context_template;
+#if defined(__OpenBSD__) || defined(__sun) || defined(_MSC_VER) && defined(_DLL)
+IMPORTEXPORT Context& getcontext();
+static thread_local Context& context{getcontext()};
+#else
+extern thread_local Context context;
+#endif
+
+class Context {
+ private:
+  mpd_context_t ctx;
+  IMPORTEXPORT static void raiseit(uint32_t status);
+
+ public:
+  /***********************************************************************/
+  /*                             Constructors                            */
+  /***********************************************************************/
+  Context(const Context& c) = default;
+  Context(Context&& c) = default;
+
+  explicit Context(const mpd_context_t &m) noexcept : ctx(m) {}
+
+  explicit Context(mpd_ssize_t prec=context_template.prec(),
+                   mpd_ssize_t emax=context_template.emax(),
+                   mpd_ssize_t emin=context_template.emin(),
+                   int round=context_template.round(),
+                   uint32_t traps=context_template.traps(),
+                   int clamp=context_template.clamp(),
+                   int allcr=context_template.allcr()) {
+    this->prec(prec);
+    this->emax(emax);
+    this->emin(emin);
+    this->traps(traps);
+    this->round(round);
+    this->clamp(clamp);
+    this->allcr(allcr);
+    this->ctx.status = 0;
+    this->ctx.newtrap = 0;
+  }
+
+  /***********************************************************************/
+  /*                             Destructor                              */
+  /***********************************************************************/
+  ~Context() noexcept = default;
+
+  /***********************************************************************/
+  /*                         Assignment operators                        */
+  /***********************************************************************/
+  Context& operator= (const Context& c) = default;
+  Context& operator= (Context&& c) = default;
+
+  /***********************************************************************/
+  /*                         Comparison operators                        */
+  /***********************************************************************/
+  bool operator== (const Context& other) const noexcept {
+    return ctx.prec == other.ctx.prec &&
+           ctx.emax == other.ctx.emax &&
+           ctx.emin == other.ctx.emin &&
+           ctx.traps == other.ctx.traps &&
+           ctx.status == other.ctx.status &&
+           ctx.round == other.ctx.round &&
+           ctx.clamp == other.ctx.clamp &&
+           ctx.allcr == other.ctx.allcr &&
+           ctx.newtrap == other.ctx.newtrap;
+  }
+
+  bool operator!= (const Context& other) const noexcept {
+    return !(*this == other);
+  }
+
+  /***********************************************************************/
+  /*                              Accessors                              */
+  /***********************************************************************/
+  /* Get pointers to the full context */
+  ALWAYS_INLINE mpd_context_t *get() { return &ctx; }
+  ALWAYS_INLINE const mpd_context_t *getconst() const { return &ctx; }
+
+  /* Get individual fields */
+  ALWAYS_INLINE mpd_ssize_t prec() const { return ctx.prec; }
+  ALWAYS_INLINE mpd_ssize_t emax() const { return ctx.emax; }
+  ALWAYS_INLINE mpd_ssize_t emin() const { return ctx.emin; }
+  ALWAYS_INLINE int round() const { return ctx.round; }
+  ALWAYS_INLINE uint32_t status() const { return ctx.status; }
+  ALWAYS_INLINE uint32_t traps() const { return ctx.traps; }
+  ALWAYS_INLINE int clamp() const { return ctx.clamp; }
+  ALWAYS_INLINE int allcr() const { return ctx.allcr; }
+  ALWAYS_INLINE int64_t etiny() const { return mpd_etiny(&ctx); }
+  ALWAYS_INLINE int64_t etop() const { return mpd_etop(&ctx); }
+
+  /* Set individual fields */
+  ALWAYS_INLINE void prec(mpd_ssize_t v) {
+    if (!mpd_qsetprec(&ctx, v)) {
+      throw ValueError("valid range for prec is [1, MAX_PREC]");
+    }
+  }
+
+  ALWAYS_INLINE void emax(mpd_ssize_t v) {
+    if (!mpd_qsetemax(&ctx, v)) {
+      throw ValueError("valid range for emax is [0, MAX_EMAX]");
+    }
+  }
+
+  ALWAYS_INLINE void emin(mpd_ssize_t v) {
+    if (!mpd_qsetemin(&ctx, v)) {
+      throw ValueError("valid range for emin is [MIN_EMIN, 0]");
+    }
+  }
+
+  ALWAYS_INLINE void round(int v) {
+    if (!mpd_qsetround(&ctx, v)) {
+      throw ValueError("valid values for rounding are:\n"
+                       "  [ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n"
+                       "   ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n"
+                       "   ROUND_05UP]");
+    }
+  }
+
+  ALWAYS_INLINE void status(uint32_t v) {
+    if (!mpd_qsetstatus(&ctx, v)) {
+      throw ValueError("invalid status flag");
+    }
+  }
+
+  ALWAYS_INLINE void traps(uint32_t v) {
+    if (!mpd_qsettraps(&ctx, v)) {
+      throw ValueError("invalid status flag");
+    }
+  }
+
+  ALWAYS_INLINE void clamp(int v) {
+    if (!mpd_qsetclamp(&ctx, v)) {
+      throw ValueError("invalid value for clamp");
+    }
+  }
+
+  ALWAYS_INLINE void allcr(int v) {
+    if (!mpd_qsetcr(&ctx, v)) {
+      throw ValueError("invalid value for allcr");
+    }
+  }
+
+  /***********************************************************************/
+  /*                     Status and exception handling                   */
+  /***********************************************************************/
+  /* Add flags to status and raise an exception if a relevant trap is active */
+  ALWAYS_INLINE void raise(uint32_t flags) {
+    ctx.status |= (flags & ~MPD_Malloc_error);
+    const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
+    if (active_traps) {
+      raiseit(active_traps);
+    }
+  }
+
+  /* Same, but with cleanup for constructors */
+  ALWAYS_INLINE void raise(uint32_t flags, mpd_t *a, bool isstatic) {
+    ctx.status |= (flags & ~MPD_Malloc_error);
+    const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
+    if (active_traps) {
+      if (!isstatic) {
+        mpd_del(a);
+      }
+      raiseit(active_traps);
+    }
+  }
+
+  /* Add selected status flags */
+  ALWAYS_INLINE void add_status(uint32_t flags) {
+    if (flags > MPD_Max_status) {
+      throw ValueError("invalid flags");
+    }
+    ctx.status |= flags;
+  }
+
+  /* Clear all status flags */
+  ALWAYS_INLINE void clear_status() {
+    ctx.status = 0;
+  }
+
+  /* Clear selected status flags */
+  ALWAYS_INLINE void clear_status(uint32_t flags) {
+    if (flags > MPD_Max_status) {
+      throw ValueError("invalid flags");
+    }
+    ctx.status &= ~flags;
+  }
+
+  /* Add selected traps */
+  ALWAYS_INLINE void add_traps(uint32_t flags) {
+    if (flags > MPD_Max_status) {
+      throw ValueError("invalid flags");
+    }
+    ctx.traps |= flags;
+  }
+
+  /* Clear all traps */
+  ALWAYS_INLINE void clear_traps() {
+    ctx.traps = 0;
+  }
+
+  /* Clear selected traps */
+  ALWAYS_INLINE void clear_traps(uint32_t flags) {
+    if (flags > MPD_Max_status) {
+      throw ValueError("invalid flags");
+    }
+    ctx.traps &= ~flags;
+  }
+
+  /***********************************************************************/
+  /*                          String conversion                          */
+  /***********************************************************************/
+  IMPORTEXPORT std::string repr() const;
+  IMPORTEXPORT friend std::ostream& operator<<(std::ostream& os, const Context& c);
+};
+
+IMPORTEXPORT Context MaxContext();
+IMPORTEXPORT Context IEEEContext(int bits);
+
+
+/******************************************************************************/
+/*                                    Util                                    */
+/******************************************************************************/
+
+namespace util {
+
+template <typename dest_t, typename src_t>
+inline dest_t
+safe_downcast(src_t v) {
+  if (v < std::numeric_limits<dest_t>::min() ||
+      v > std::numeric_limits<dest_t>::max()) {
+    throw ValueError("cast changes the original value");
+  }
+
+  return static_cast<dest_t>(v);
+}
+
+inline std::shared_ptr<const char>
+shared_cp(const char *cp) {
+  if (cp == nullptr) {
+    throw RuntimeError("util::shared_cp: invalid nullptr argument");
+  }
+
+  return std::shared_ptr<const char>(cp, [](const char *s){ mpd_free(const_cast<char *>(s)); });
+}
+
+inline std::string
+string_from_cp(const char *cp) {
+  const auto p = shared_cp(cp);
+  return std::string(p.get());
+}
+
+template <typename T>
+struct int64_compat {
+#define INT64_SUBSET(T) \
+  (INT64_MIN <= std::numeric_limits<T>::min() && std::numeric_limits<T>::max() <= INT64_MAX)
+
+  static const bool value = std::is_same<T, int8_t>::value ||
+                            std::is_same<T, int16_t>::value ||
+                            std::is_same<T, int32_t>::value ||
+                            std::is_same<T, int64_t>::value ||
+                            (std::is_same<T, signed char>::value && INT64_SUBSET(signed char)) ||
+                            (std::is_same<T, short>::value && INT64_SUBSET(short)) ||
+                            (std::is_same<T, int>::value && INT64_SUBSET(int)) ||
+                            (std::is_same<T, long>::value && INT64_SUBSET(long)) ||
+                            (std::is_same<T, long long>::value && INT64_SUBSET(long long));
+};
+
+template <typename T>
+struct uint64_compat {
+#define UINT64_SUBSET(T) (std::numeric_limits<T>::max() <= UINT64_MAX)
+
+  static const bool value = std::is_same<T, uint8_t>::value ||
+                            std::is_same<T, uint16_t>::value ||
+                            std::is_same<T, uint32_t>::value ||
+                            std::is_same<T, uint64_t>::value ||
+                            (std::is_same<T, unsigned char>::value && UINT64_SUBSET(unsigned char)) ||
+                            (std::is_same<T, unsigned short>::value && UINT64_SUBSET(unsigned short)) ||
+                            (std::is_same<T, unsigned int>::value && UINT64_SUBSET(unsigned int)) ||
+                            (std::is_same<T, unsigned long>::value && UINT64_SUBSET(unsigned long)) ||
+                            (std::is_same<T, unsigned long long>::value && UINT64_SUBSET(unsigned long long));
+};
+
+#define ENABLE_IF_SIGNED(T) \
+  template<typename T,      \
+           typename = typename std::enable_if<util::int64_compat<T>::value>::type>
+
+#define ENABLE_IF_UNSIGNED(T) \
+  template<typename T,        \
+           typename = typename std::enable_if<util::uint64_compat<T>::value>::type, typename = void>
+
+#define ENABLE_IF_INTEGRAL(T) \
+  template<typename T,        \
+           typename = typename std::enable_if<util::int64_compat<T>::value || util::uint64_compat<T>::value>::type>
+
+#define ASSERT_SIGNED(T) \
+  static_assert(util::int64_compat<T>::value, \
+               "internal error: selected type is not int64 compatible")
+
+#define ASSERT_UNSIGNED(T) \
+  static_assert(util::uint64_compat<T>::value, \
+               "internal error: selected type is not uint64 compatible")
+
+#define ASSERT_INTEGRAL(T) \
+  static_assert(util::int64_compat<T>::value || util::uint64_compat<T>::value, \
+               "internal error: selected type is not a suitable integer type")
+}  // namespace util
+
+
+/******************************************************************************/
+/*                              Decimal object                                */
+/******************************************************************************/
+
+constexpr mpd_ssize_t MINALLOC = 4;
+
+class Decimal {
+ private:
+  mpd_uint_t data[MINALLOC] = {0};
+
+  mpd_t value {
+    MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
+    0,                                   /* exp */
+    0,                                   /* digits */
+    0,                                   /* len */
+    MINALLOC,                            /* alloc */
+    data                                 /* data */
+  };
+
+  /* mpd_t accessors */
+  ALWAYS_INLINE bool isstatic() const { return value.data == data; }
+
+  /* Reset rhs to snan after moving data to lhs */
+  ALWAYS_INLINE void reset() {
+    value = {
+      MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
+      0,                                   /* exp */
+      0,                                   /* digits */
+      0,                                   /* len */
+      MINALLOC,                            /* alloc */
+      data                                 /* data */
+    };
+  }
+
+  /* Copy flags, preserving memory attributes of result */
+  ALWAYS_INLINE uint8_t
+  copy_flags(const uint8_t rflags, const uint8_t aflags) {
+      return (rflags & (MPD_STATIC|MPD_DATAFLAGS)) |
+             (aflags & ~(MPD_STATIC|MPD_DATAFLAGS));
+  }
+
+  ALWAYS_INLINE void
+  copy_value(const mpd_t *const src, const bool fastcopy) {
+    assert(mpd_isstatic(&value));
+    assert(mpd_isstatic(src));
+    assert(value.alloc >= MINALLOC);
+    assert(src->alloc >= MINALLOC);
+    assert(MPD_MINALLOC == MINALLOC);
+    if (fastcopy) {
+      value.data[0] = src->data[0];
+      value.data[1] = src->data[1];
+      value.data[2] = src->data[2];
+      value.data[3] = src->data[3];
+      value.flags = copy_flags(value.flags, src->flags);
+      value.exp = src->exp;
+      value.digits = src->digits;
+      value.len = src->len;
+    } else {
+      if (!mpd_qcopy_cxx(&value, src)) {
+        context.raise(MPD_Malloc_error);
+      }
+    }
+  }
+
+  ALWAYS_INLINE void
+  move_value(const mpd_t *const src, const bool fastcopy) {
+    assert(mpd_isstatic(&value));
+    assert(mpd_isstatic(src));
+    assert(value.alloc >= MINALLOC);
+    assert(src->alloc >= MINALLOC);
+    assert(MPD_MINALLOC == MINALLOC);
+    if (fastcopy) {
+      value.data[0] = src->data[0];
+      value.data[1] = src->data[1];
+      value.data[2] = src->data[2];
+      value.data[3] = src->data[3];
+      value.flags = copy_flags(value.flags, src->flags);
+      value.exp = src->exp;
+      value.digits = src->digits;
+      value.len = src->len;
+    }
+    else {
+      assert(mpd_isdynamic_data(src));
+      if (mpd_isdynamic_data(&value)) {
+        mpd_free(value.data);
+      }
+      value = *src;
+      assert(mpd_isstatic(&value));
+      assert(mpd_isdynamic_data(&value));
+    }
+  }
+
+  ALWAYS_INLINE Decimal unary_func_status(
+    int (* func)(mpd_t *, const mpd_t *, uint32_t *)) const {
+      Decimal result;
+      uint32_t status = 0;
+      if (!func(result.get(), getconst(), &status)) {
+          throw MallocError("out of memory");
+      }
+      return result;
+  }
+
+  ALWAYS_INLINE Decimal unary_func(
+    void (* func)(mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
+    Context& c=context) const {
+      Decimal result;
+      uint32_t status = 0;
+      func(result.get(), getconst(), c.getconst(), &status);
+      c.raise(status);
+      return result;
+  }
+
+  ALWAYS_INLINE Decimal binary_func_noctx(
+    int (* func)(mpd_t *, const mpd_t *, const mpd_t *),
+    const Decimal& other) const {
+      Decimal result;
+      (void)func(result.get(), getconst(), other.getconst());
+      return result;
+  }
+
+  ALWAYS_INLINE Decimal int_binary_func(
+    int (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
+    const Decimal& other,
+    Context& c=context) const {
+      Decimal result;
+      uint32_t status = 0;
+      (void)func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
+      c.raise(status);
+      return result;
+  }
+
+  ALWAYS_INLINE Decimal binary_func(
+    void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
+    const Decimal& other,
+    Context& c=context) const {
+      Decimal result;
+      uint32_t status = 0;
+      func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
+      c.raise(status);
+      return result;
+  }
+
+  ALWAYS_INLINE Decimal& inplace_binary_func(
+    void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
+    const Decimal& other,
+    Context& c=context) {
+      uint32_t status = 0;
+      func(get(), getconst(), other.getconst(), c.getconst(), &status);
+      c.raise(status);
+      return *this;
+  }
+
+  ALWAYS_INLINE Decimal inplace_shiftl(const int64_t n, Context& c=context) {
+    uint32_t status = 0;
+    mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+    mpd_qshiftl(get(), getconst(), nn, &status);
+    c.raise(status);
+    return *this;
+  }
+
+  ALWAYS_INLINE Decimal inplace_shiftr(const int64_t n, Context& c=context) {
+    uint32_t status = 0;
+    mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+    mpd_qshiftr(get(), getconst(), nn, &status);
+    c.raise(status);
+    return *this;
+  }
+
+ public:
+  /***********************************************************************/
+  /*               Exact conversions regardless of context               */
+  /***********************************************************************/
+
+  /* Implicit */
+  Decimal() noexcept = default;
+  Decimal(const Decimal& other) { *this = other; }
+  Decimal(Decimal&& other) noexcept { *this = std::move(other); }
+
+  ENABLE_IF_SIGNED(T)
+  Decimal(const T& other) {
+    ASSERT_SIGNED(T);
+    uint32_t status = 0;
+    mpd_qset_i64_exact(&value, other, &status);
+    context.raise(status, &value, isstatic());
+  }
+
+  ENABLE_IF_UNSIGNED(T)
+  Decimal(const T& other) {
+    ASSERT_UNSIGNED(T);
+    uint32_t status = 0;
+    mpd_qset_u64_exact(&value, other, &status);
+    context.raise(status, &value, isstatic());
+  }
+
+  /* Explicit */
+  explicit Decimal(const char * const s) {
+    uint32_t status = 0;
+    if (s == nullptr) {
+      throw ValueError("Decimal: string argument in constructor is NULL");
+    }
+    mpd_qset_string_exact(&value, s, &status);
+    context.raise(status, &value, isstatic());
+  }
+
+  explicit Decimal(const std::string& s) {
+    uint32_t status = 0;
+    mpd_qset_string_exact(&value, s.c_str(), &status);
+    context.raise(status, &value, isstatic());
+  }
+
+  explicit Decimal(const mpd_uint128_triple_t& triple) {
+    uint32_t status = 0;
+    if (mpd_from_uint128_triple(&value, &triple, &status) < 0) {
+      context.raise(status, &value, isstatic());
+    }
+  }
+
+  /***********************************************************************/
+  /*                Inexact conversions that use a context               */
+  /***********************************************************************/
+  explicit Decimal(const Decimal& other, Context& c) {
+    const mpd_context_t *ctx = c.getconst();
+
+    *this = other;
+
+    if (mpd_isnan(&value) && value.digits > ctx->prec - ctx->clamp) {
+      /* Special case:  too many NaN payload digits */
+      mpd_setspecial(&value, MPD_POS, MPD_NAN);
+      c.raise(MPD_Conversion_syntax, &value, isstatic());
+    }
+    else {
+      uint32_t status = 0;
+      mpd_qfinalize(&value, ctx, &status);
+      c.raise(status, &value, isstatic());
+    }
+  }
+
+  ENABLE_IF_SIGNED(T)
+  explicit Decimal(const T& other, Context& c) {
+    ASSERT_SIGNED(T);
+    uint32_t status = 0;
+    mpd_qset_i64(&value, other, c.getconst(), &status);
+    c.raise(status, &value, isstatic());
+  }
+
+  ENABLE_IF_UNSIGNED(T)
+  explicit Decimal(const T& other, Context& c) {
+    ASSERT_UNSIGNED(T);
+    uint32_t status = 0;
+    mpd_qset_u64(&value, other, c.getconst(), &status);
+    c.raise(status, &value, isstatic());
+  }
+
+  explicit Decimal(const char * const s, Context& c) {
+    uint32_t status = 0;
+    if (s == nullptr) {
+      throw ValueError("Decimal: string argument in constructor is NULL");
+    }
+    mpd_qset_string(&value, s, c.getconst(), &status);
+    c.raise(status, &value, isstatic());
+  }
+
+  explicit Decimal(const std::string& s, Context& c) {
+    uint32_t status = 0;
+    mpd_qset_string(&value, s.c_str(), c.getconst(), &status);
+    c.raise(status, &value, isstatic());
+  }
+
+  /***********************************************************************/
+  /*                              Accessors                              */
+  /***********************************************************************/
+  ALWAYS_INLINE mpd_t *get() { return &value; }
+  ALWAYS_INLINE const mpd_t *getconst() const { return &value; }
+  ALWAYS_INLINE int sign() const { return mpd_isnegative(&value) ? -1 : 1; }
+
+  ALWAYS_INLINE Decimal coeff() const {
+    if (isspecial()) {
+      throw ValueError("coefficient is undefined for special values");
+    }
+
+    Decimal result = *this;
+    mpd_set_positive(&result.value);
+    result.value.exp = 0;
+    return result;
+  }
+
+  ALWAYS_INLINE int64_t exponent() const {
+    if (isspecial()) {
+      throw ValueError("exponent is undefined for special values");
+    }
+
+    return value.exp;
+  }
+
+  ALWAYS_INLINE Decimal payload() const {
+    if (!isnan()) {
+      throw ValueError("payload is only defined for NaNs");
+    }
+    if (value.len == 0) {
+      return Decimal(0);
+    }
+
+    Decimal result = *this;
+    mpd_set_flags(&result.value, 0);
+    result.value.exp = 0;
+    return result;
+  }
+
+  /***********************************************************************/
+  /*                             Destructor                              */
+  /***********************************************************************/
+  ~Decimal() { if (value.data != data) mpd_del(&value); }
+
+  /***********************************************************************/
+  /*                         Assignment operators                        */
+  /***********************************************************************/
+  ALWAYS_INLINE Decimal& operator= (const Decimal& other) {
+    copy_value(other.getconst(), other.isstatic());
+    return *this;
+  }
+
+  ALWAYS_INLINE Decimal& operator= (Decimal&& other) noexcept {
+    if (this != &other) {
+      move_value(other.getconst(), other.isstatic());
+      other.reset();
+    }
+    return *this;
+  }
+
+  ALWAYS_INLINE Decimal& operator+= (const Decimal& other) { return inplace_binary_func(mpd_qadd, other); }
+  ALWAYS_INLINE Decimal& operator-= (const Decimal& other) { return inplace_binary_func(mpd_qsub, other); }
+  ALWAYS_INLINE Decimal& operator*= (const Decimal& other) { return inplace_binary_func(mpd_qmul, other); }
+  ALWAYS_INLINE Decimal& operator/= (const Decimal& other) { return inplace_binary_func(mpd_qdiv, other); }
+  ALWAYS_INLINE Decimal& operator%= (const Decimal& other) { return inplace_binary_func(mpd_qrem, other); }
+
+  /***********************************************************************/
+  /*                         Comparison operators                        */
+  /***********************************************************************/
+  ALWAYS_INLINE bool operator== (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      if (issnan() || other.issnan()) {
+        context.raise(status);
+      }
+      return false;
+    }
+    return r == 0;
+  }
+
+  ALWAYS_INLINE bool operator!= (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      if (issnan() || other.issnan()) {
+          context.raise(status);
+      }
+      return true;
+    }
+    return r != 0;
+  }
+
+  ALWAYS_INLINE bool operator< (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      context.raise(status);
+      return false;
+    }
+    return r < 0;
+  }
+
+  ALWAYS_INLINE bool operator<= (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      context.raise(status);
+      return false;
+    }
+    return r <= 0;
+  }
+
+  ALWAYS_INLINE bool operator>= (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      context.raise(status);
+      return false;
+    }
+    return r >= 0;
+  }
+
+  ALWAYS_INLINE bool operator> (const Decimal& other) const {
+    uint32_t status = 0;
+    const int r = mpd_qcmp(getconst(), other.getconst(), &status);
+    if (r == INT_MAX) {
+      context.raise(status);
+      return false;
+    }
+    return r > 0;
+  }
+
+  /***********************************************************************/
+  /*                      Unary arithmetic operators                     */
+  /***********************************************************************/
+  ALWAYS_INLINE Decimal operator- () const { return unary_func(mpd_qminus); }
+  ALWAYS_INLINE Decimal operator+ () const { return unary_func(mpd_qplus); }
+
+  /***********************************************************************/
+  /*                      Binary arithmetic operators                    */
+  /***********************************************************************/
+  ALWAYS_INLINE Decimal operator+ (const Decimal& other) const { return binary_func(mpd_qadd, other); }
+  ALWAYS_INLINE Decimal operator- (const Decimal& other) const { return binary_func(mpd_qsub, other); }
+  ALWAYS_INLINE Decimal operator* (const Decimal& other) const { return binary_func(mpd_qmul, other); }
+  ALWAYS_INLINE Decimal operator/ (const Decimal& other) const { return binary_func(mpd_qdiv, other); }
+  ALWAYS_INLINE Decimal operator% (const Decimal& other) const { return binary_func(mpd_qrem, other); }
+
+  /***********************************************************************/
+  /*                             Predicates                              */
+  /***********************************************************************/
+  /* Predicates, no context arg */
+  ALWAYS_INLINE bool iscanonical() const { return mpd_iscanonical(getconst()); }
+  ALWAYS_INLINE bool isfinite() const { return mpd_isfinite(getconst()); }
+  ALWAYS_INLINE bool isinfinite() const { return mpd_isinfinite(getconst()); }
+  ALWAYS_INLINE bool isspecial() const { return mpd_isspecial(getconst()); }
+  ALWAYS_INLINE bool isnan() const { return mpd_isnan(getconst()); }
+  ALWAYS_INLINE bool isqnan() const { return mpd_isqnan(getconst()); }
+  ALWAYS_INLINE bool issnan() const { return mpd_issnan(getconst()); }
+  ALWAYS_INLINE bool issigned() const { return mpd_issigned(getconst()); }
+  ALWAYS_INLINE bool iszero() const { return mpd_iszero(getconst()); }
+  ALWAYS_INLINE bool isinteger() const { return mpd_isinteger(getconst()); }
+
+  /* Predicates, optional context arg */
+  ALWAYS_INLINE bool isnormal(const Context& c=context) const { return mpd_isnormal(getconst(), c.getconst()); }
+  ALWAYS_INLINE bool issubnormal(const Context& c=context) const { return mpd_issubnormal(getconst(), c.getconst()); }
+
+  /***********************************************************************/
+  /*                           Unary functions                           */
+  /***********************************************************************/
+  /* Unary functions, no context arg */
+  ALWAYS_INLINE int64_t adjexp() const {
+    if (isspecial()) {
+      throw ValueError("adjusted exponent undefined for special values");
+    }
+    return mpd_adjexp(getconst());
+  }
+
+  ALWAYS_INLINE Decimal canonical() const { return *this; }
+  ALWAYS_INLINE Decimal copy() const { return unary_func_status(mpd_qcopy); }
+  ALWAYS_INLINE Decimal copy_abs() const { return unary_func_status(mpd_qcopy_abs); }
+  ALWAYS_INLINE Decimal copy_negate() const { return unary_func_status(mpd_qcopy_negate); }
+
+  /* Unary functions, optional context arg */
+  ALWAYS_INLINE std::string number_class(Context& c=context) const { return mpd_class(getconst(), c.getconst()); }
+
+  ALWAYS_INLINE Decimal abs(Context& c=context) const { return unary_func(mpd_qabs, c); }
+  ALWAYS_INLINE Decimal ceil(Context& c=context) const { return unary_func(mpd_qceil, c); }
+  ALWAYS_INLINE Decimal exp(Context& c=context) const { return unary_func(mpd_qexp, c); }
+  ALWAYS_INLINE Decimal floor(Context& c=context) const { return unary_func(mpd_qfloor, c); }
+  ALWAYS_INLINE Decimal invroot(Context& c=context) const { return unary_func(mpd_qinvroot, c); }
+  ALWAYS_INLINE Decimal logical_invert(Context& c=context) const { return unary_func(mpd_qinvert, c); }
+  ALWAYS_INLINE Decimal ln(Context& c=context) const { return unary_func(mpd_qln, c); }
+  ALWAYS_INLINE Decimal log10(Context& c=context) const { return unary_func(mpd_qlog10, c); }
+  ALWAYS_INLINE Decimal logb(Context& c=context) const { return unary_func(mpd_qlogb, c); }
+  ALWAYS_INLINE Decimal minus(Context& c=context) const { return unary_func(mpd_qminus, c); }
+  ALWAYS_INLINE Decimal next_minus(Context& c=context) const { return unary_func(mpd_qnext_minus, c); }
+  ALWAYS_INLINE Decimal next_plus(Context& c=context) const { return unary_func(mpd_qnext_plus, c); }
+  ALWAYS_INLINE Decimal plus(Context& c=context) const { return unary_func(mpd_qplus, c); }
+  ALWAYS_INLINE Decimal reduce(Context& c=context) const { return unary_func(mpd_qreduce, c); }
+  ALWAYS_INLINE Decimal to_integral(Context& c=context) const { return unary_func(mpd_qround_to_int, c); }
+  ALWAYS_INLINE Decimal to_integral_exact(Context& c=context) const { return unary_func(mpd_qround_to_intx, c); }
+  ALWAYS_INLINE Decimal sqrt(Context& c=context) const { return unary_func(mpd_qsqrt, c); }
+  ALWAYS_INLINE Decimal trunc(Context& c=context) const { return unary_func(mpd_qtrunc, c); }
+
+  /***********************************************************************/
+  /*                           Binary functions                          */
+  /***********************************************************************/
+  /* Binary functions, no context arg */
+  ALWAYS_INLINE Decimal compare_total(const Decimal& other) const { return binary_func_noctx(mpd_compare_total, other); }
+  ALWAYS_INLINE Decimal compare_total_mag(const Decimal& other) const { return binary_func_noctx(mpd_compare_total_mag, other); }
+
+  /* Binary arithmetic functions, optional context arg */
+  ALWAYS_INLINE Decimal add(const Decimal& other, Context& c=context) const { return binary_func(mpd_qadd, other, c); }
+  ALWAYS_INLINE Decimal div(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdiv, other, c); }
+  ALWAYS_INLINE Decimal divint(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdivint, other, c); }
+  ALWAYS_INLINE Decimal compare(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare, other, c); }
+  ALWAYS_INLINE Decimal compare_signal(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare_signal, other, c); }
+  ALWAYS_INLINE Decimal logical_and(const Decimal& other, Context& c=context) const { return binary_func(mpd_qand, other, c); }
+  ALWAYS_INLINE Decimal logical_or(const Decimal& other, Context& c=context) const { return binary_func(mpd_qor, other, c); }
+  ALWAYS_INLINE Decimal logical_xor(const Decimal& other, Context& c=context) const { return binary_func(mpd_qxor, other, c); }
+  ALWAYS_INLINE Decimal max(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax, other, c); }
+  ALWAYS_INLINE Decimal max_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax_mag, other, c); }
+  ALWAYS_INLINE Decimal min(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin, other, c); }
+  ALWAYS_INLINE Decimal min_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin_mag, other, c); }
+  ALWAYS_INLINE Decimal mul(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmul, other, c); }
+  ALWAYS_INLINE Decimal next_toward(const Decimal& other, Context& c=context) const { return binary_func(mpd_qnext_toward, other, c); }
+  ALWAYS_INLINE Decimal pow(const Decimal& other, Context& c=context) const { return binary_func(mpd_qpow, other, c); }
+  ALWAYS_INLINE Decimal quantize(const Decimal& other, Context& c=context) const { return binary_func(mpd_qquantize, other, c); }
+  ALWAYS_INLINE Decimal rem(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem, other, c); }
+  ALWAYS_INLINE Decimal rem_near(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem_near, other, c); }
+  ALWAYS_INLINE Decimal rotate(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrotate, other, c); }
+  ALWAYS_INLINE Decimal scaleb(const Decimal& other, Context& c=context) const { return binary_func(mpd_qscaleb, other, c); }
+  ALWAYS_INLINE Decimal shift(const Decimal& other, Context& c=context) const { return binary_func(mpd_qshift, other, c); }
+  ALWAYS_INLINE Decimal sub(const Decimal& other, Context& c=context) const { return binary_func(mpd_qsub, other, c); }
+
+  /* Binary arithmetic function, two return values */
+  ALWAYS_INLINE std::pair<Decimal, Decimal> divmod(const Decimal& other, Context& c=context) const {
+    std::pair<Decimal, Decimal> result;
+    uint32_t status = 0;
+    mpd_qdivmod(result.first.get(), result.second.get(), getconst(), other.getconst(), c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  /***********************************************************************/
+  /*                          Ternary functions                          */
+  /***********************************************************************/
+  ALWAYS_INLINE Decimal fma(const Decimal& other, const Decimal& third, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    mpd_qfma(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  ALWAYS_INLINE Decimal powmod(const Decimal& other, const Decimal& third, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    mpd_qpowmod(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  /***********************************************************************/
+  /*                         Irregular functions                         */
+  /***********************************************************************/
+  ALWAYS_INLINE Decimal apply(Context& c=context) const {
+    Decimal result = *this;
+    uint32_t status = 0;
+
+    mpd_qfinalize(result.get(), c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  ALWAYS_INLINE int cmp(const Decimal& other) const {
+    uint32_t status = 0;
+    return mpd_qcmp(getconst(), other.getconst(), &status);
+  }
+
+  ALWAYS_INLINE int cmp_total(const Decimal& other) const {
+    return mpd_cmp_total(getconst(), other.getconst());
+  }
+
+  ALWAYS_INLINE int cmp_total_mag(const Decimal& other) const {
+    return mpd_cmp_total_mag(getconst(), other.getconst());
+  }
+
+  ALWAYS_INLINE Decimal copy_sign(const Decimal& other) const {
+    Decimal result;
+    uint32_t status = 0;
+    if (!mpd_qcopy_sign(result.get(), getconst(), other.getconst(), &status)) {
+      throw MallocError("out of memory");
+    }
+    return result;
+  }
+
+  ALWAYS_INLINE Decimal rescale(const int64_t exp, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    mpd_ssize_t xexp = util::safe_downcast<mpd_ssize_t, int64_t>(exp);
+    mpd_qrescale(result.get(), getconst(), xexp, c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  ALWAYS_INLINE bool same_quantum(const Decimal& other) const {
+    return mpd_same_quantum(getconst(), other.getconst());
+  }
+
+  ALWAYS_INLINE Decimal shiftn(const int64_t n, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+    mpd_qshiftn(result.get(), getconst(), nn, c.getconst(), &status);
+    c.raise(status);
+    return result;
+  }
+
+  ALWAYS_INLINE Decimal shiftl(const int64_t n, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    if (isspecial()) {
+      throw ValueError("shiftl: cannot handle special numbers");
+    }
+    if (n < 0 || n > MPD_MAX_PREC - getconst()->digits) {
+      throw ValueError("shiftl: shift is negative or too large");
+    }
+    mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+    mpd_qshiftl(result.get(), getconst(), nn, &status);
+    c.raise(status);
+    return result;
+  }
+
+  ALWAYS_INLINE Decimal shiftr(const int64_t n, Context& c=context) const {
+    Decimal result;
+    uint32_t status = 0;
+    if (isspecial()) {
+      throw ValueError("shiftr: cannot handle special numbers");
+    }
+    if (n < 0) {
+      throw ValueError("shiftr: shift is negative");
+    }
+    mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
+    mpd_qshiftr(result.get(), getconst(), nn, &status);
+    c.raise(status);
+    return result;
+  }
+
+  IMPORTEXPORT static Decimal exact(const char *s, Context& c);
+  IMPORTEXPORT static Decimal exact(const std::string& s, Context& c);
+  IMPORTEXPORT static Decimal ln10(int64_t n, Context& c=context);
+  IMPORTEXPORT static int32_t radix();
+
+  /***********************************************************************/
+  /*                          Integer conversion                         */
+  /***********************************************************************/
+  ALWAYS_INLINE int64_t i64() const {
+    uint32_t status = 0;
+    const int64_t result = mpd_qget_i64(getconst(), &status);
+    if (status) {
+      throw ValueError("cannot convert to int64_t");
+    }
+    return result;
+  }
+
+  ALWAYS_INLINE int32_t i32() const {
+    uint32_t status = 0;
+    const int32_t result = mpd_qget_i32(getconst(), &status);
+    if (status) {
+      throw ValueError("cannot convert to int32_t");
+    }
+    return result;
+  }
+
+  ALWAYS_INLINE uint64_t u64() const {
+    uint32_t status = 0;
+    const uint64_t result = mpd_qget_u64(getconst(), &status);
+    if (status) {
+      throw ValueError("cannot convert to uint64_t");
+    }
+    return result;
+  }
+
+  ALWAYS_INLINE uint32_t u32() const {
+    uint32_t status = 0;
+    const uint32_t result = mpd_qget_u32(getconst(), &status);
+    if (status) {
+      throw ValueError("cannot convert to uint32_t");
+    }
+    return result;
+  }
+
+  /***********************************************************************/
+  /*                           Triple conversion                         */
+  /***********************************************************************/
+  ALWAYS_INLINE mpd_uint128_triple_t as_uint128_triple() const {
+    return mpd_as_uint128_triple(getconst());
+  }
+
+  /***********************************************************************/
+  /*                          String conversion                          */
+  /***********************************************************************/
+  /* String representations */
+  IMPORTEXPORT std::string repr(bool capitals=true) const;
+
+  inline std::string to_sci(bool capitals=true) const {
+    const char *cp = mpd_to_sci(getconst(), capitals);
+    if (cp == nullptr) {
+      throw MallocError("out of memory");
+    }
+
+    return util::string_from_cp(cp);
+  }
+
+  inline std::string to_eng(bool capitals=true) const {
+    const char *cp = mpd_to_eng(getconst(), capitals);
+    if (cp == nullptr) {
+      throw MallocError("out of memory");
+    }
+
+    return util::string_from_cp(cp);
+  }
+
+  inline std::string format(const char *fmt, const Context& c=context) const {
+    uint32_t status = 0;
+    mpd_context_t ctx;
+
+    if (fmt == nullptr) {
+      throw ValueError("Decimal.format: fmt argument is NULL");
+    }
+
+    mpd_maxcontext(&ctx);
+    ctx.round = c.getconst()->round;
+    ctx.traps = 0;
+
+    const char *cp = mpd_qformat(getconst(), fmt, &ctx, &status);
+    if (cp == nullptr) {
+      if (status & MPD_Malloc_error) {
+        throw MallocError("out of memory");
+      }
+      else if (status & MPD_Invalid_operation) {
+        throw ValueError("invalid format string");
+      }
+      else {
+        throw RuntimeError("internal error: unexpected status");
+      }
+    }
+
+    return util::string_from_cp(cp);
+  }
+
+  inline std::string format(const std::string& s, const Context& c=context) const {
+    return format(s.c_str(), c);
+  }
+
+  IMPORTEXPORT friend std::ostream& operator<< (std::ostream& os, const Decimal& dec);
+};
+
+/***********************************************************************/
+/*                      Reverse comparison operators                   */
+/***********************************************************************/
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator==(const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) == self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator!= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) != self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator< (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) < self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator<= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) <= self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator>= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) >= self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator> (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) > self; }
+
+/***********************************************************************/
+/*                      Reverse arithmetic operators                   */
+/***********************************************************************/
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator+ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) + self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator- (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) - self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator* (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) * self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator/ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) / self; }
+ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator% (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) % self; }
+
+
+#undef IMPORTEXPORT
+#undef ALWAYS_INLINE
+#undef INT64_SUBSET
+#undef UINT64_SUBSET
+#undef ENABLE_IF_SIGNED
+#undef ENABLE_IF_UNSIGNED
+#undef ENABLE_IF_INTEGRAL
+#undef ASSERT_SIGNED
+#undef ASSERT_UNSIGNED
+#undef ASSERT_INTEGRAL
+}  // namespace decimal
+
+
+#endif  // LIBMPDECXX_DECIMAL_HH_
diff --git a/libmpdec++/examples/factorial.cc b/libmpdec++/examples/factorial.cc
new file mode 100644 (file)
index 0000000..dcbb74f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <iostream>
+
+#include "decimal.hh"
+
+
+using decimal::Decimal;
+using decimal::MaxContext;
+using decimal::context;
+
+using decimal::ConversionSyntax;
+using decimal::InvalidOperation;
+using decimal::MallocError;
+
+using decimal::DecRounded;
+using decimal::DecInexact;
+
+
+Decimal
+f(const Decimal& n, const Decimal& m)
+{
+    if (n > m) {
+        return f(m, n);
+    }
+    else if (m == 0) {
+        return 1;
+    }
+    else if (n == m) {
+        return n;
+    }
+    else {
+        return f(n, (n + m).divint(2)) * f((n + m).divint(2) + 1, m);
+    }
+}
+
+Decimal
+factorial(const Decimal& n)
+{
+    context = MaxContext();
+
+    // DecRounded can be skipped if integer results with non-zero
+    // exponents are allowed.
+    context.add_traps(DecInexact|DecRounded);
+
+    return f(n, Decimal(0));
+}
+
+void
+err_exit(const std::string& msg)
+{
+    std::cerr << msg << std::endl;
+    std::exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+    Decimal n;
+
+    if (argc != 2) {
+        err_exit("usage: ./factorial n");
+    }
+
+    try {
+        n = Decimal(argv[1]);
+    }
+    catch (ConversionSyntax&) {
+        err_exit("invalid decimal string");
+    }
+    catch (InvalidOperation&) {
+        err_exit("value exceeds internal limits");
+    }
+    catch (MallocError&) {
+        err_exit("out of memory");
+    }
+
+    // The exponent could be nonzero, this is to avoid surprise at the result.
+    if (!n.isinteger() || n.exponent() != 0 || n < 0) {
+        err_exit("n must be a non-negative integer with exponent zero");
+    }
+
+    std::cout << factorial(n) << std::endl;
+
+    return 0;
+}
diff --git a/libmpdec++/examples/pi.cc b/libmpdec++/examples/pi.cc
new file mode 100644 (file)
index 0000000..228c982
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <cassert>
+#include <climits>
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+#include "decimal.hh"
+
+
+using decimal::Decimal;
+using decimal::context;
+
+
+/* The algorithm is not optimized for bignum arithmetic */
+Decimal
+pi(int prec)
+{
+    assert(1 <= prec && prec <= INT_MAX-2);
+    context.prec(prec + 2);
+
+    Decimal lasts = 0;
+    Decimal t = 3;
+    Decimal s = 3;
+    Decimal n = 1;
+    Decimal na = 0;
+    Decimal d = 0;
+    Decimal da = 24;
+
+    while (s != lasts) {
+        lasts = s;
+        n += na;
+        na += 8;
+        d += da;
+        da += 32;
+        t = (t * n) / d;
+        s += t;
+    }
+
+    context.prec(prec);
+    return +s;
+}
+
+void
+err_exit(const std::string& msg)
+{
+    std::cerr << msg << std::endl;
+    std::exit(1);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    size_t pos = 0;
+    int prec = 0;
+
+    if (argc != 2) {
+        err_exit("usage: ./pi prec");
+    }
+    std::string s = argv[1];
+
+    try {
+        prec = std::stoi(s, &pos);
+    } catch (const std::invalid_argument&) {
+        err_exit("not a number");
+    } catch (const std::out_of_range &) {
+        err_exit("out of range");
+    }
+
+    if (pos < s.size()) {
+        err_exit("trailing characters");
+    }
+    if (prec <= 0 || prec > 999999) {
+        err_exit("prec must be in [1, 999999]");
+    }
+
+    std::cout << pi(prec) << std::endl;
+
+    return 0;
+}
diff --git a/libmpdec/.objs/README.txt b/libmpdec/.objs/README.txt
new file mode 100644 (file)
index 0000000..a934a33
--- /dev/null
@@ -0,0 +1,8 @@
+
+Directory for shared object files
+=================================
+
+   symbols32.exp ==> 32-bit libmpdec API
+
+   symbols64.exp ==> 64-bit libmpdec API
+
diff --git a/libmpdec/.objs/symbols32.exp b/libmpdec/.objs/symbols32.exp
new file mode 100644 (file)
index 0000000..18e6eca
--- /dev/null
@@ -0,0 +1,343 @@
+*** libmpdec 32-bit symbols ***
+mpd_abs
+mpd_abs_uint
+mpd_add
+mpd_add_i32
+mpd_add_i64
+mpd_add_ssize
+mpd_addstatus_raise
+mpd_add_u32
+mpd_add_u64
+mpd_add_uint
+mpd_adjexp
+mpd_alloc
+mpd_and
+mpd_arith_sign
+mpd_as_uint128_triple
+mpd_basiccontext
+mpd_calloc
+mpd_callocfunc
+mpd_callocfunc_em
+mpd_canonical
+mpd_ceil
+mpd_check_nan
+mpd_check_nans
+mpd_clamp_string
+mpd_class
+mpd_clear_flags
+mpd_cmp
+mpd_cmp_total
+mpd_cmp_total_mag
+mpd_compare
+mpd_compare_signal
+mpd_compare_total
+mpd_compare_total_mag
+mpd_copy
+mpd_copy_abs
+mpd_copy_flags
+mpd_copy_negate
+mpd_copy_sign
+mpd_defaultcontext
+mpd_del
+mpd_dflt_traphandler
+mpd_digits_to_size
+mpd_div
+mpd_div_i32
+mpd_div_i64
+mpd_divint
+mpd_divmod
+mpd_div_ssize
+mpd_div_u32
+mpd_div_u64
+mpd_div_uint
+mpd_etiny
+mpd_etop
+mpd_exp
+mpd_exp_digits
+mpd_export_u16
+mpd_export_u32
+mpd_finalize
+mpd_floor
+mpd_fma
+mpd_format
+mpd_fprint
+mpd_free
+mpd_from_uint128_triple
+mpd_getclamp
+mpd_getcr
+mpd_getemax
+mpd_getemin
+mpd_get_i32
+mpd_get_i64
+mpd_getprec
+mpd_getround
+mpd_get_ssize
+mpd_getstatus
+mpd_gettraps
+mpd_get_u32
+mpd_get_u64
+mpd_get_uint
+mpd_ieee_context
+mpd_import_u16
+mpd_import_u32
+mpd_init
+mpd_invert
+mpd_invroot
+mpd_iscanonical
+mpd_isconst_data
+mpd_isdynamic
+mpd_isdynamic_data
+mpd_iseven
+mpd_isfinite
+mpd_isinfinite
+mpd_isinteger
+mpd_isnan
+mpd_isnegative
+mpd_isnormal
+mpd_isodd
+mpd_isoddcoeff
+mpd_isoddword
+mpd_ispositive
+mpd_isqnan
+mpd_isshared_data
+mpd_issigned
+mpd_issnan
+mpd_isspecial
+mpd_isstatic
+mpd_isstatic_data
+mpd_issubnormal
+mpd_iszero
+mpd_iszerocoeff
+mpd_ln
+mpd_log10
+mpd_logb
+mpd_lsd
+mpd_lsnprint_flags
+mpd_lsnprint_signals
+mpd_mallocfunc
+mpd_max
+mpd_maxcoeff
+mpd_maxcontext
+mpd_max_mag
+mpd_min
+mpd_minalloc
+MPD_MINALLOC
+mpd_min_mag
+mpd_minus
+mpd_msd
+mpd_msword
+mpd_mul
+mpd_mul_i32
+mpd_mul_i64
+mpd_mul_ssize
+mpd_mul_u32
+mpd_mul_u64
+mpd_mul_uint
+mpd_new
+mpd_next_minus
+mpd_next_plus
+mpd_next_toward
+mpd_or
+mpd_parse_fmt_str
+mpd_plus
+mpd_pow
+mpd_powmod
+mpd_print
+mpd_qabs
+mpd_qabs_uint
+mpd_qadd
+mpd_qadd_i32
+mpd_qadd_i64
+mpd_qadd_ssize
+mpd_qadd_u32
+mpd_qadd_u64
+mpd_qadd_uint
+mpd_qand
+mpd_qceil
+mpd_qcheck_nan
+mpd_qcheck_nans
+mpd_qcmp
+mpd_qcompare
+mpd_qcompare_signal
+mpd_qcopy
+mpd_qcopy_abs
+mpd_qcopy_cxx
+mpd_qcopy_negate
+mpd_qcopy_sign
+mpd_qdiv
+mpd_qdiv_i32
+mpd_qdiv_i64
+mpd_qdivint
+mpd_qdivmod
+mpd_qdiv_ssize
+mpd_qdiv_u32
+mpd_qdiv_u64
+mpd_qdiv_uint
+mpd_qexp
+mpd_qexport_u16
+mpd_qexport_u32
+mpd_qfinalize
+mpd_qfloor
+mpd_qfma
+mpd_qformat
+mpd_qformat_spec
+mpd_qget_i32
+mpd_qget_i64
+mpd_qget_ssize
+mpd_qget_u32
+mpd_qget_u64
+mpd_qget_uint
+mpd_qimport_u16
+mpd_qimport_u32
+mpd_qinvert
+mpd_qinvroot
+mpd_qln
+mpd_qln10
+mpd_qlog10
+mpd_qlogb
+mpd_qmax
+mpd_qmaxcoeff
+mpd_qmax_mag
+mpd_qmin
+mpd_qmin_mag
+mpd_qminus
+mpd_qmul
+mpd_qmul_i32
+mpd_qmul_i64
+mpd_qmul_ssize
+mpd_qmul_u32
+mpd_qmul_u64
+mpd_qmul_uint
+mpd_qncopy
+mpd_qnew
+mpd_qnew_size
+mpd_qnext_minus
+mpd_qnext_plus
+mpd_qnext_toward
+mpd_qor
+mpd_qplus
+mpd_qpow
+mpd_qpowmod
+mpd_qquantize
+mpd_qreduce
+mpd_qrem
+mpd_qrem_near
+mpd_qrescale
+mpd_qrescale_fmt
+mpd_qresize
+mpd_qresize_zero
+mpd_qrotate
+mpd_qround_to_int
+mpd_qround_to_intx
+mpd_qscaleb
+mpd_qsetclamp
+mpd_qsetcr
+mpd_qsetemax
+mpd_qsetemin
+mpd_qset_i32
+mpd_qset_i64
+mpd_qset_i64_exact
+mpd_qsetprec
+mpd_qsetround
+mpd_qset_ssize
+mpd_qsetstatus
+mpd_qset_string
+mpd_qset_string_exact
+mpd_qsettraps
+mpd_qset_u32
+mpd_qset_u64
+mpd_qset_u64_exact
+mpd_qset_uint
+mpd_qshift
+mpd_qshiftl
+mpd_qshiftn
+mpd_qshiftr
+mpd_qshiftr_inplace
+mpd_qsqrt
+mpd_qsset_i32
+mpd_qsset_ssize
+mpd_qsset_u32
+mpd_qsset_uint
+mpd_qsub
+mpd_qsub_i32
+mpd_qsub_i64
+mpd_qsub_ssize
+mpd_qsub_u32
+mpd_qsub_u64
+mpd_qsub_uint
+mpd_qtrunc
+mpd_quantize
+mpd_qxor
+mpd_radix
+mpd_realloc
+mpd_reallocfunc
+mpd_reduce
+mpd_rem
+mpd_rem_near
+mpd_rescale
+mpd_resize
+mpd_resize_zero
+mpd_rotate
+mpd_round_string
+mpd_round_to_int
+mpd_round_to_intx
+mpd_same_quantum
+mpd_scaleb
+mpd_set_const_data
+mpd_setdigits
+mpd_set_dynamic
+mpd_set_dynamic_data
+mpd_seterror
+mpd_set_flags
+mpd_set_i32
+mpd_set_i64
+mpd_set_infinity
+mpd_setminalloc
+mpd_set_negative
+mpd_set_positive
+mpd_set_qnan
+mpd_set_shared_data
+mpd_set_sign
+mpd_set_snan
+mpd_setspecial
+mpd_set_ssize
+mpd_set_static
+mpd_set_static_data
+mpd_set_string
+mpd_set_u32
+mpd_set_u64
+mpd_set_uint
+mpd_sh_alloc
+mpd_shift
+mpd_shiftl
+mpd_shiftn
+mpd_shiftr
+mpd_sign
+mpd_signcpy
+mpd_sizeinbase
+mpd_snprint_flags
+mpd_sqrt
+mpd_sset_i32
+mpd_sset_ssize
+mpd_sset_u32
+mpd_sset_uint
+mpd_sub
+mpd_sub_i32
+mpd_sub_i64
+mpd_sub_ssize
+mpd_sub_u32
+mpd_sub_u64
+mpd_sub_uint
+mpd_to_eng
+mpd_to_eng_size
+mpd_to_sci
+mpd_to_sci_size
+mpd_trail_zeros
+mpd_traphandler
+mpd_trunc
+mpd_uint_zero
+mpd_validate_lconv
+mpd_version
+mpd_word_digits
+mpd_xor
+mpd_zerocoeff
diff --git a/libmpdec/.objs/symbols64.exp b/libmpdec/.objs/symbols64.exp
new file mode 100644 (file)
index 0000000..5cb7514
--- /dev/null
@@ -0,0 +1,347 @@
+*** libmpdec 64-bit symbols ***
+mpd_abs
+mpd_abs_uint
+mpd_add
+mpd_add_i32
+mpd_add_i64
+mpd_add_ssize
+mpd_addstatus_raise
+mpd_add_u32
+mpd_add_u64
+mpd_add_uint
+mpd_adjexp
+mpd_alloc
+mpd_and
+mpd_arith_sign
+mpd_as_uint128_triple
+mpd_basiccontext
+mpd_calloc
+mpd_callocfunc
+mpd_callocfunc_em
+mpd_canonical
+mpd_ceil
+mpd_check_nan
+mpd_check_nans
+mpd_clamp_string
+mpd_class
+mpd_clear_flags
+mpd_cmp
+mpd_cmp_total
+mpd_cmp_total_mag
+mpd_compare
+mpd_compare_signal
+mpd_compare_total
+mpd_compare_total_mag
+mpd_copy
+mpd_copy_abs
+mpd_copy_flags
+mpd_copy_negate
+mpd_copy_sign
+mpd_defaultcontext
+mpd_del
+mpd_dflt_traphandler
+mpd_digits_to_size
+mpd_div
+mpd_div_i32
+mpd_div_i64
+mpd_divint
+mpd_divmod
+mpd_div_ssize
+mpd_div_u32
+mpd_div_u64
+mpd_div_uint
+mpd_etiny
+mpd_etop
+mpd_exp
+mpd_exp_digits
+mpd_export_u16
+mpd_export_u32
+mpd_finalize
+mpd_floor
+mpd_fma
+mpd_format
+mpd_fprint
+mpd_free
+mpd_from_uint128_triple
+mpd_getclamp
+mpd_getcr
+mpd_getemax
+mpd_getemin
+mpd_get_i32
+mpd_get_i64
+mpd_getprec
+mpd_getround
+mpd_get_ssize
+mpd_getstatus
+mpd_gettraps
+mpd_get_u32
+mpd_get_u64
+mpd_get_uint
+mpd_ieee_context
+mpd_import_u16
+mpd_import_u32
+mpd_init
+mpd_invert
+mpd_invroot
+mpd_iscanonical
+mpd_isconst_data
+mpd_isdynamic
+mpd_isdynamic_data
+mpd_iseven
+mpd_isfinite
+mpd_isinfinite
+mpd_isinteger
+mpd_isnan
+mpd_isnegative
+mpd_isnormal
+mpd_isodd
+mpd_isoddcoeff
+mpd_isoddword
+mpd_ispositive
+mpd_isqnan
+mpd_isshared_data
+mpd_issigned
+mpd_issnan
+mpd_isspecial
+mpd_isstatic
+mpd_isstatic_data
+mpd_issubnormal
+mpd_iszero
+mpd_iszerocoeff
+mpd_ln
+mpd_log10
+mpd_logb
+mpd_lsd
+mpd_lsnprint_flags
+mpd_lsnprint_signals
+mpd_mallocfunc
+mpd_max
+mpd_maxcoeff
+mpd_maxcontext
+mpd_max_mag
+mpd_min
+mpd_minalloc
+MPD_MINALLOC
+mpd_min_mag
+mpd_minus
+mpd_msd
+mpd_msword
+mpd_mul
+mpd_mul_i32
+mpd_mul_i64
+mpd_mul_ssize
+mpd_mul_u32
+mpd_mul_u64
+mpd_mul_uint
+mpd_new
+mpd_next_minus
+mpd_next_plus
+mpd_next_toward
+mpd_or
+mpd_parse_fmt_str
+mpd_plus
+mpd_pow
+mpd_powmod
+mpd_print
+mpd_qabs
+mpd_qabs_uint
+mpd_qadd
+mpd_qadd_i32
+mpd_qadd_i64
+mpd_qadd_ssize
+mpd_qadd_u32
+mpd_qadd_u64
+mpd_qadd_uint
+mpd_qand
+mpd_qceil
+mpd_qcheck_nan
+mpd_qcheck_nans
+mpd_qcmp
+mpd_qcompare
+mpd_qcompare_signal
+mpd_qcopy
+mpd_qcopy_abs
+mpd_qcopy_cxx
+mpd_qcopy_negate
+mpd_qcopy_sign
+mpd_qdiv
+mpd_qdiv_i32
+mpd_qdiv_i64
+mpd_qdivint
+mpd_qdivmod
+mpd_qdiv_ssize
+mpd_qdiv_u32
+mpd_qdiv_u64
+mpd_qdiv_uint
+mpd_qexp
+mpd_qexport_u16
+mpd_qexport_u32
+mpd_qfinalize
+mpd_qfloor
+mpd_qfma
+mpd_qformat
+mpd_qformat_spec
+mpd_qget_i32
+mpd_qget_i64
+mpd_qget_ssize
+mpd_qget_u32
+mpd_qget_u64
+mpd_qget_uint
+mpd_qimport_u16
+mpd_qimport_u32
+mpd_qinvert
+mpd_qinvroot
+mpd_qln
+mpd_qln10
+mpd_qlog10
+mpd_qlogb
+mpd_qmax
+mpd_qmaxcoeff
+mpd_qmax_mag
+mpd_qmin
+mpd_qmin_mag
+mpd_qminus
+mpd_qmul
+mpd_qmul_i32
+mpd_qmul_i64
+mpd_qmul_ssize
+mpd_qmul_u32
+mpd_qmul_u64
+mpd_qmul_uint
+mpd_qncopy
+mpd_qnew
+mpd_qnew_size
+mpd_qnext_minus
+mpd_qnext_plus
+mpd_qnext_toward
+mpd_qor
+mpd_qplus
+mpd_qpow
+mpd_qpowmod
+mpd_qquantize
+mpd_qreduce
+mpd_qrem
+mpd_qrem_near
+mpd_qrescale
+mpd_qrescale_fmt
+mpd_qresize
+mpd_qresize_zero
+mpd_qrotate
+mpd_qround_to_int
+mpd_qround_to_intx
+mpd_qscaleb
+mpd_qsetclamp
+mpd_qsetcr
+mpd_qsetemax
+mpd_qsetemin
+mpd_qset_i32
+mpd_qset_i64
+mpd_qset_i64_exact
+mpd_qsetprec
+mpd_qsetround
+mpd_qset_ssize
+mpd_qsetstatus
+mpd_qset_string
+mpd_qset_string_exact
+mpd_qsettraps
+mpd_qset_u32
+mpd_qset_u64
+mpd_qset_u64_exact
+mpd_qset_uint
+mpd_qshift
+mpd_qshiftl
+mpd_qshiftn
+mpd_qshiftr
+mpd_qshiftr_inplace
+mpd_qsqrt
+mpd_qsset_i32
+mpd_qsset_i64
+mpd_qsset_ssize
+mpd_qsset_u32
+mpd_qsset_u64
+mpd_qsset_uint
+mpd_qsub
+mpd_qsub_i32
+mpd_qsub_i64
+mpd_qsub_ssize
+mpd_qsub_u32
+mpd_qsub_u64
+mpd_qsub_uint
+mpd_qtrunc
+mpd_quantize
+mpd_qxor
+mpd_radix
+mpd_realloc
+mpd_reallocfunc
+mpd_reduce
+mpd_rem
+mpd_rem_near
+mpd_rescale
+mpd_resize
+mpd_resize_zero
+mpd_rotate
+mpd_round_string
+mpd_round_to_int
+mpd_round_to_intx
+mpd_same_quantum
+mpd_scaleb
+mpd_set_const_data
+mpd_setdigits
+mpd_set_dynamic
+mpd_set_dynamic_data
+mpd_seterror
+mpd_set_flags
+mpd_set_i32
+mpd_set_i64
+mpd_set_infinity
+mpd_setminalloc
+mpd_set_negative
+mpd_set_positive
+mpd_set_qnan
+mpd_set_shared_data
+mpd_set_sign
+mpd_set_snan
+mpd_setspecial
+mpd_set_ssize
+mpd_set_static
+mpd_set_static_data
+mpd_set_string
+mpd_set_u32
+mpd_set_u64
+mpd_set_uint
+mpd_sh_alloc
+mpd_shift
+mpd_shiftl
+mpd_shiftn
+mpd_shiftr
+mpd_sign
+mpd_signcpy
+mpd_sizeinbase
+mpd_snprint_flags
+mpd_sqrt
+mpd_sset_i32
+mpd_sset_i64
+mpd_sset_ssize
+mpd_sset_u32
+mpd_sset_u64
+mpd_sset_uint
+mpd_sub
+mpd_sub_i32
+mpd_sub_i64
+mpd_sub_ssize
+mpd_sub_u32
+mpd_sub_u64
+mpd_sub_uint
+mpd_to_eng
+mpd_to_eng_size
+mpd_to_sci
+mpd_to_sci_size
+mpd_trail_zeros
+mpd_traphandler
+mpd_trunc
+mpd_uint_zero
+mpd_validate_lconv
+mpd_version
+mpd_word_digits
+mpd_xor
+mpd_zerocoeff
diff --git a/libmpdec/.pc/libmpdec.pc.in b/libmpdec/.pc/libmpdec.pc.in
new file mode 100644 (file)
index 0000000..36b94ee
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libmpdec
+Description: C library for decimal floating point arithmetic
+Version: 4.0.1
+URL: https://www.bytereef.org
+Cflags: -I${includedir}
+Libs: -L${libdir} -lmpdec -lm
diff --git a/libmpdec/.profile/train.sh b/libmpdec/.profile/train.sh
new file mode 100755 (executable)
index 0000000..c88e118
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+PORTABLE_PWD=`pwd`
+
+LD_LIBRARY_PATH="$PORTABLE_PWD:$LD_LIBRARY_PATH"
+DYLD_LIBRARY_PATH="$PORTABLE_PWD:$DYLD_LIBRARY_PATH"
+LD_64_LIBRARY_PATH="$PORTABLE_PWD:$LD_64_LIBRARY_PATH"
+LD_32_LIBRARY_PATH="$PORTABLE_PWD:$LD_32_LIBRARY_PATH"
+LD_LIBRARY_PATH_64="$PORTABLE_PWD:$LD_LIBRARY_PATH_64"
+LD_LIBRARY_PATH_32="$PORTABLE_PWD:$LD_LIBRARY_PATH_32"
+PATH="$LD_LIBRARY_PATH:$PATH"
+export LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH
+export LD_64_LIBRARY_PATH
+export LD_32_LIBRARY_PATH
+export LD_LIBRARY_PATH_64
+export LD_LIBRARY_PATH_32
+
+MPD_PREC="$1"
+MPD_DPREC=`expr "$MPD_PREC" \* 2`
+
+if [ ! -f ./bench_full -a ! -f ./bench_shared ]; then
+    printf "\n./train.sh: error: no training files found\n\n"
+    exit 1
+fi
+
+if [ -f ./bench_full ]; then
+    ./bench_full "$MPD_PREC" 1000 > /dev/null 2>&1
+    ./bench_full "$MPD_DPREC" 1000 > /dev/null 2>&1
+fi
+
+if [ -f ./bench_shared ]; then
+    ./bench_shared "$MPD_PREC" 1000000 > /dev/null 2>&1
+    ./bench_shared "$MPD_DPREC" 1000000 > /dev/null 2>&1
+fi
diff --git a/libmpdec/Makefile.in b/libmpdec/Makefile.in
new file mode 100644 (file)
index 0000000..422d753
--- /dev/null
@@ -0,0 +1,319 @@
+
+# ==============================================================================
+#                          Unix Makefile for libmpdec
+# ==============================================================================
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+ENABLE_STATIC = @ENABLE_STATIC@
+ENABLE_SHARED = @ENABLE_SHARED@
+ENABLE_MINGW = @ENABLE_MINGW@
+
+FPIC = @FPIC@
+LIBSTATIC = @LIBSTATIC@
+LIBNAME = @LIBNAME@
+LIBSONAME = @LIBSONAME@
+LIBSHARED = @LIBSHARED@
+LIBIMPORT = @LIBIMPORT@
+LIBSHARED_USE_AR = @LIBSHARED_USE_AR@
+LINK_STATIC = @LINK_STATIC@
+LINK_DYNAMIC = @LINK_DYNAMIC@
+
+EXPORTSYMS = @EXPORTSYMS@
+OBJECT_SUFFIX = @OBJECT_SUFFIX@
+COPY = @cp -f $1 $2
+
+CC = @CC@
+LD = @LD@
+AR = @AR@
+RANLIB = @RANLIB@
+MPD_PGEN = @MPD_PGEN@
+MPD_PUSE = @MPD_PUSE@
+MPD_PREC = @MPD_PREC@
+
+# gcc produces a random false positive warning for LTO bytecode in libmpdec.a
+# (see: bench target).  Users of libmpdec.a should not get spurious warnings.
+FILTER_FOR_STATIC = @FILTER_FOR_STATIC@
+
+CONFIGURE_LIBMPDEC_CFLAGS = @CONFIGURE_LIBMPDEC_CFLAGS@
+MPD_CFLAGS_COMMON = $(strip $(filter-out $(CFLAGS),$(CONFIGURE_LIBMPDEC_CFLAGS)) $(CFLAGS))
+MPD_CFLAGS_SHARED = $(strip $(filter-out $(FPIC),$(MPD_CFLAGS_COMMON)) $(FPIC))
+MPD_CFLAGS = $(strip $(filter-out $(FILTER_FOR_STATIC),$(MPD_CFLAGS_COMMON)))
+
+CONFIGURE_CFLAGS = @CONFIGURE_CFLAGS@
+MPD_BIN_CFLAGS_SHARED = $(strip $(filter-out $(CFLAGS),$(CONFIGURE_CFLAGS)) $(CFLAGS))
+MPD_BIN_CFLAGS = $(strip $(filter-out $(FILTER_FOR_STATIC),$(MPD_BIN_CFLAGS_SHARED)))
+
+CONFIGURE_LDFLAGS = @CONFIGURE_LDFLAGS@
+MPD_LDFLAGS = $(strip $(filter-out $(LDFLAGS),$(CONFIGURE_LDFLAGS)) $(LDFLAGS))
+
+LINK_LIBSTATIC = $(strip $(LINK_STATIC) $(LIBSTATIC) $(LINK_DYNAMIC))
+
+ifeq ($(MAKECMDGOALS), profile_gen)
+  MPD_CFLAGS_COMMON += $(MPD_PGEN)
+  MPD_BIN_CFLAGS_SHARED += $(MPD_PGEN)
+  MPD_LDFLAGS += $(MPD_PGEN)
+endif
+ifeq ($(MAKECMDGOALS), profile_use)
+  MPD_CFLAGS_COMMON += $(MPD_PUSE)
+  MPD_BIN_CFLAGS_SHARED += $(MPD_PUSE)
+  MPD_LDFLAGS += $(MPD_PUSE)
+endif
+
+MPD_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+    MPD_TARGETS += $(LIBSTATIC)
+endif
+ifeq ($(ENABLE_SHARED), yes)
+    MPD_TARGETS += $(LIBSHARED)
+endif
+
+
+default: $(MPD_TARGETS)
+
+
+OBJS := basearith.o context.o constants.o convolute.o crt.o mpdecimal.o \
+        mpsignal.o difradix2.o fnt.o fourstep.o io.o mpalloc.o numbertheory.o \
+        sixstep.o transpose.o
+
+SHARED_OBJS := .objs/basearith.o .objs/context.o .objs/constants.o \
+               .objs/convolute.o .objs/crt.o .objs/mpdecimal.o .objs/mpsignal.o \
+               .objs/difradix2.o .objs/fnt.o .objs/fourstep.o .objs/io.o \
+               .objs/mpalloc.o .objs/numbertheory.o .objs/sixstep.o \
+               .objs/transpose.o
+
+
+ifeq ($(LIBSHARED_USE_AR), yes)
+AR_STATIC_OBJS := $(OBJS:.o=$(OBJECT_SUFFIX))
+AR_SHARED_OBJS := $(LIBSHARED:.o=$(OBJECT_SUFFIX))
+%$(OBJECT_SUFFIX): %.o
+       $(call COPY,$<,$@)
+
+$(LIBSHARED): Makefile $(SHARED_OBJS)
+       cp .objs/$(EXPORTSYMS) .objs/symbols.exp
+       $(LD) $(MPD_LDFLAGS) -o $(LIBSHARED) $(SHARED_OBJS) -lm
+
+$(LIBSTATIC): Makefile $(AR_STATIC_OBJS) $(AR_SHARED_OBJS)
+       $(AR) rc $(LIBSTATIC) $(AR_SHARED_OBJS) $(AR_STATIC_OBJS)
+       $(RANLIB) $(LIBSTATIC)
+else
+$(LIBSTATIC): Makefile $(OBJS)
+       $(AR) rc $(LIBSTATIC) $(OBJS)
+       $(RANLIB) $(LIBSTATIC)
+
+$(LIBSHARED): Makefile $(SHARED_OBJS)
+       $(LD) $(MPD_LDFLAGS) -o $(LIBSHARED) $(SHARED_OBJS) -lm
+ifeq ($(ENABLE_MINGW), no)
+       ln -sf $(LIBSHARED) $(LIBNAME)
+       ln -sf $(LIBSHARED) $(LIBSONAME)
+endif
+endif
+
+
+basearith.o:\
+Makefile basearith.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS) -c basearith.c
+
+.objs/basearith.o:\
+Makefile basearith.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c basearith.c -o .objs/basearith.o
+
+constants.o:\
+Makefile constants.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS) -c constants.c
+
+.objs/constants.o:\
+Makefile constants.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c constants.c -o .objs/constants.o
+
+context.o:\
+Makefile context.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS) -c context.c
+
+.objs/context.o:\
+Makefile context.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c context.c -o .objs/context.o
+
+convolute.o:\
+Makefile convolute.c mpdecimal.h bits.h constants.h convolute.h fnt.h \
+fourstep.h numbertheory.h sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c convolute.c
+
+.objs/convolute.o:\
+Makefile convolute.c mpdecimal.h bits.h constants.h convolute.h fnt.h \
+fourstep.h numbertheory.h sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c convolute.c -o .objs/convolute.o
+
+crt.o:\
+Makefile crt.c mpdecimal.h constants.h crt.h numbertheory.h umodarith.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS) -c crt.c
+
+.objs/crt.o:\
+Makefile crt.c mpdecimal.h constants.h crt.h numbertheory.h umodarith.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c crt.c -o .objs/crt.o
+
+difradix2.o:\
+Makefile difradix2.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c difradix2.c
+
+.objs/difradix2.o:\
+Makefile difradix2.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c difradix2.c -o .objs/difradix2.o
+
+fnt.o:\
+Makefile fnt.c mpdecimal.h bits.h difradix2.h numbertheory.h constants.h \
+fnt.h
+       $(CC) $(MPD_CFLAGS) -c fnt.c
+
+.objs/fnt.o:\
+Makefile fnt.c mpdecimal.h bits.h difradix2.h numbertheory.h constants.h \
+fnt.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c fnt.c -o .objs/fnt.o
+
+fourstep.o:\
+Makefile fourstep.c mpdecimal.h constants.h fourstep.h numbertheory.h \
+sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c fourstep.c
+
+.objs/fourstep.o:\
+Makefile fourstep.c mpdecimal.h constants.h fourstep.h numbertheory.h \
+sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c fourstep.c -o .objs/fourstep.o
+
+io.o:\
+Makefile io.c mpdecimal.h typearith.h io.h
+       $(CC) $(MPD_CFLAGS) -c io.c
+
+.objs/io.o:\
+Makefile io.c mpdecimal.h typearith.h io.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c io.c -o .objs/io.o
+
+mpalloc.o:\
+Makefile mpalloc.c mpdecimal.h mpalloc.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c mpalloc.c
+
+.objs/mpalloc.o:\
+Makefile mpalloc.c mpdecimal.h mpalloc.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpalloc.c -o .objs/mpalloc.o
+
+mpdecimal.o:\
+Makefile mpdecimal.c mpdecimal.h basearith.h typearith.h bits.h \
+constants.h convolute.h crt.h mpalloc.h
+       $(CC) $(MPD_CFLAGS) -c mpdecimal.c
+
+.objs/mpdecimal.o:\
+Makefile mpdecimal.c mpdecimal.h basearith.h typearith.h bits.h \
+constants.h convolute.h crt.h mpalloc.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpdecimal.c -o .objs/mpdecimal.o
+
+mpsignal.o:\
+Makefile mpsignal.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS) -c mpsignal.c
+
+.objs/mpsignal.o:\
+Makefile mpsignal.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpsignal.c -o .objs/mpsignal.o
+
+numbertheory.o:\
+Makefile numbertheory.c mpdecimal.h bits.h numbertheory.h \
+constants.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c numbertheory.c
+
+.objs/numbertheory.o:\
+Makefile numbertheory.c mpdecimal.h bits.h numbertheory.h \
+constants.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c numbertheory.c -o .objs/numbertheory.o
+
+sixstep.o:\
+Makefile sixstep.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h sixstep.h transpose.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c sixstep.c
+
+.objs/sixstep.o:\
+Makefile sixstep.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h sixstep.h transpose.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c sixstep.c -o .objs/sixstep.o
+
+transpose.o:\
+Makefile transpose.c mpdecimal.h bits.h constants.h transpose.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS) -c transpose.c
+
+.objs/transpose.o:\
+Makefile transpose.c mpdecimal.h bits.h constants.h transpose.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c transpose.c -o .objs/transpose.o
+
+
+bench: bench.c mpdecimal.h $(LIBSTATIC)
+       $(CC) $(MPD_BIN_CFLAGS) -o bench bench.c $(LINK_LIBSTATIC) -lm
+
+bench_full: bench_full.c mpdecimal.h $(LIBSTATIC)
+       $(CC) $(MPD_BIN_CFLAGS) -o bench_full bench_full.c $(LINK_LIBSTATIC) -lm
+
+bench_shared: bench.c mpdecimal.h $(LIBSHARED)
+       $(CC) -L. $(MPD_BIN_CFLAGS_SHARED) -o bench_shared bench.c -lmpdec -lm
+
+bench_full_shared: bench_full.c mpdecimal.h $(LIBSHARED)
+       $(CC) -L. $(MPD_BIN_CFLAGS_SHARED) -o bench_full_shared bench_full.c -lmpdec -lm
+
+
+pow: Makefile examples/pow.c $(LIBSTATIC)
+       $(CC) -I. $(MPD_BIN_CFLAGS) -o pow examples/pow.c $(LINK_LIBSTATIC) -lm
+
+sqrt: Makefile examples/sqrt.c $(LIBSTATIC)
+       $(CC) -I. $(MPD_BIN_CFLAGS) -o sqrt examples/sqrt.c $(LINK_LIBSTATIC) -lm
+
+examples: pow sqrt
+
+
+GEN_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+GEN_TARGETS += bench_full
+endif
+ifeq ($(ENABLE_SHARED), yes)
+GEN_TARGETS += bench_shared
+endif
+
+profile_gen: $(GEN_TARGETS)
+       ./.profile/train.sh $(MPD_PREC)
+
+profile_clean:
+       rm -f *.o *.so *.gch
+       rm -f bench bench_shared bench_full bench_full_shared pow sqrt
+       rm -f $(LIBSTATIC) $(LIBNAME) $(LIBSONAME) $(LIBSHARED) $(LIBIMPORT)
+       cd .objs && rm -f *.o *.so *.gch symbols.exp
+
+profile_use: default
+
+profile:
+       $(MAKE) clean
+       $(MAKE) profile_gen
+       $(MAKE) profile_clean
+       $(MAKE) profile_use
+
+
+check: default
+       cd ../tests && $(MAKE) && ./runshort.sh
+
+check_local: default
+       cd ../tests && $(MAKE) && ./runshort.sh --local
+
+check_alloc: default
+       cd ../tests && $(MAKE) && ./runshort_alloc.sh
+
+
+clean:
+       rm -f *.o *.so *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock
+       rm -f bench bench_shared bench_full bench_full_shared pow sqrt
+       rm -f $(LIBSTATIC) $(LIBNAME) $(LIBSONAME) $(LIBSHARED) $(LIBIMPORT)
+       cd .objs && rm -f *.o *.so *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock symbols.exp
+
+distclean: clean
+       rm -f config.h config.log config.status Makefile mpdecimal.h .pc/libmpdec.pc
diff --git a/libmpdec/Makefile.vc b/libmpdec/Makefile.vc
new file mode 100644 (file)
index 0000000..48bead3
--- /dev/null
@@ -0,0 +1,288 @@
+
+# ======================================================================
+#                 Visual C (nmake) Makefile for libmpdec
+# ======================================================================
+
+LIBSTATIC = libmpdec-4.0.1.lib
+LIBIMPORT = libmpdec-4.0.1.dll.lib
+LIBSHARED = libmpdec-4.0.1.dll
+
+MPD_HEADER = mpdecimal32vc.h
+MPD_PREC = 9
+MPD_DPREC = 18
+
+!if "$(MACHINE)" == "x64"
+CONFIG = /DCONFIG_64 /DMASM
+MPD_HEADER = mpdecimal64vc.h
+MPD_PREC = 19
+MPD_DPREC = 38
+!endif
+!if "$(MACHINE)" == "ansi64"
+CONFIG = /DCONFIG_64 /DANSI
+MPD_HEADER = mpdecimal64vc.h
+MPD_PREC = 19
+MPD_DPREC = 38
+!endif
+!if "$(MACHINE)" == "ppro"
+CONFIG = /DCONFIG_32 /DPPRO /DMASM
+!endif
+!if "$(MACHINE)" == "ansi32"
+CONFIG = /DCONFIG_32 /DANSI
+!endif
+
+!if "$(DEBUG)" == "1"
+OPT = /MTd /Od /Zi /EHsc
+OPT_SHARED = /MDd /Od /Zi /EHsc
+!else
+OPT = /MT /O2 /GS /EHsc /DNDEBUG
+OPT_SHARED = /MD /O2 /GS /EHsc /DNDEBUG
+!endif
+
+!if "$(CC)" == "clang-cl"
+WARN = /W4 /wd4200 /wd4204 /wd4221 -Wno-unknown-pragmas -Wno-undefined-inline /D_CRT_SECURE_NO_WARNINGS
+!else
+WARN = /W4 /wd4200 /wd4204 /wd4221 /D_CRT_SECURE_NO_WARNINGS
+!endif
+
+MPD_CFLAGS = $(WARN) /nologo $(CONFIG) $(OPT)
+MPD_CFLAGS_SHARED = /DBUILD_LIBMPDEC $(WARN) /nologo $(CONFIG) $(OPT_SHARED) $(PGOFLAGS)
+
+MPD_BIN_CFLAGS = $(WARN) /nologo $(OPT)
+MPD_BIN_CFLAGS_SHARED = $(WARN) /nologo $(OPT_SHARED) $(PGOFLAGS)
+
+MPD_LDFLAGS= /DLL /MANIFEST $(LDFLAGS)
+
+
+default: $(LIBSTATIC) $(LIBSHARED)
+
+
+OBJS = basearith.obj context.obj constants.obj convolute.obj crt.obj \
+       mpdecimal.obj mpsignal.obj difradix2.obj fnt.obj fourstep.obj \
+       io.obj mpalloc.obj numbertheory.obj sixstep.obj transpose.obj
+
+SHARED_OBJS = .objs\basearith.obj .objs\context.obj .objs\constants.obj \
+              .objs\convolute.obj .objs\crt.obj .objs\mpdecimal.obj .objs\mpsignal.obj \
+              .objs\difradix2.obj .objs\fnt.obj .objs\fourstep.obj .objs\io.obj \
+              .objs\mpalloc.obj .objs\numbertheory.obj .objs\sixstep.obj \
+              .objs\transpose.obj
+
+!if "$(MACHINE)" == "x64"
+OBJS = $(OBJS) vcdiv64.obj
+SHARED_OBJS = $(SHARED_OBJS) vcdiv64.obj
+!endif
+
+
+
+$(LIBSTATIC): Makefile $(OBJS)
+       -@if exist $@ del $(LIBSTATIC)
+       lib /out:$(LIBSTATIC) $(OBJS)
+
+
+$(LIBSHARED): Makefile $(SHARED_OBJS)
+       -@if exist $@ del $(LIBSHARED)
+       link $(MPD_LDFLAGS) /out:$(LIBSHARED) /implib:$(LIBIMPORT) $(SHARED_OBJS)
+       mt -manifest $(LIBSHARED).manifest -outputresource:$(LIBSHARED);2
+
+
+mpdecimal.h:\
+Makefile $(MPD_HEADER)
+       -@copy /y $(MPD_HEADER) mpdecimal.h
+
+basearith.obj:\
+Makefile basearith.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS) -c basearith.c
+
+.objs\basearith.obj:\
+Makefile basearith.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c basearith.c /Fo.objs\basearith.obj
+
+constants.obj:\
+Makefile constants.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS) -c constants.c
+
+.objs\constants.obj:\
+Makefile constants.c mpdecimal.h basearith.h typearith.h constants.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c constants.c /Fo.objs\constants.obj
+
+context.obj:\
+Makefile context.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS) -c context.c
+
+.objs\context.obj:\
+Makefile context.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c context.c /Fo.objs\context.obj
+
+convolute.obj:\
+Makefile convolute.c mpdecimal.h bits.h constants.h convolute.h fnt.h \
+fourstep.h numbertheory.h sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c convolute.c
+
+.objs\convolute.obj:\
+Makefile convolute.c mpdecimal.h bits.h constants.h convolute.h fnt.h \
+fourstep.h numbertheory.h sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c convolute.c /Fo.objs\convolute.obj
+
+crt.obj:\
+Makefile crt.c mpdecimal.h constants.h crt.h numbertheory.h umodarith.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS) -c crt.c
+
+.objs\crt.obj:\
+Makefile crt.c mpdecimal.h constants.h crt.h numbertheory.h umodarith.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c crt.c /Fo.objs\crt.obj
+
+difradix2.obj:\
+Makefile difradix2.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c difradix2.c
+
+.objs\difradix2.obj:\
+Makefile difradix2.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c difradix2.c /Fo.objs\difradix2.obj
+
+fnt.obj:\
+Makefile fnt.c mpdecimal.h bits.h difradix2.h numbertheory.h constants.h \
+fnt.h
+       $(CC) $(MPD_CFLAGS) -c fnt.c
+
+.objs\fnt.obj:\
+Makefile fnt.c mpdecimal.h bits.h difradix2.h numbertheory.h constants.h \
+fnt.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c fnt.c /Fo.objs\fnt.obj
+
+fourstep.obj:\
+Makefile fourstep.c mpdecimal.h constants.h fourstep.h numbertheory.h \
+sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c fourstep.c
+
+.objs\fourstep.obj:\
+Makefile fourstep.c mpdecimal.h constants.h fourstep.h numbertheory.h \
+sixstep.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c fourstep.c /Fo.objs\fourstep.obj
+
+io.obj:\
+Makefile io.c mpdecimal.h typearith.h io.h
+       $(CC) $(MPD_CFLAGS) -c io.c
+
+.objs\io.obj:\
+Makefile io.c mpdecimal.h typearith.h io.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c io.c /Fo.objs\io.obj
+
+mpalloc.obj:\
+Makefile mpalloc.c mpdecimal.h mpalloc.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c mpalloc.c
+
+.objs\mpalloc.obj:\
+Makefile mpalloc.c mpdecimal.h mpalloc.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpalloc.c /Fo.objs\mpalloc.obj
+
+mpdecimal.obj:\
+Makefile mpdecimal.c mpdecimal.h basearith.h typearith.h bits.h \
+constants.h convolute.h crt.h mpalloc.h
+       $(CC) $(MPD_CFLAGS) -c mpdecimal.c
+
+.objs\mpdecimal.obj:\
+Makefile mpdecimal.c mpdecimal.h basearith.h typearith.h bits.h \
+constants.h convolute.h crt.h mpalloc.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpdecimal.c /Fo.objs\mpdecimal.obj
+
+mpsignal.obj:\
+Makefile mpsignal.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS) -c mpsignal.c
+
+.objs\mpsignal.obj:\
+Makefile mpsignal.c mpdecimal.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c mpsignal.c /Fo.objs\mpsignal.obj
+
+numbertheory.obj:\
+Makefile numbertheory.c mpdecimal.h bits.h numbertheory.h \
+constants.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c numbertheory.c
+
+.objs\numbertheory.obj:\
+Makefile numbertheory.c mpdecimal.h bits.h numbertheory.h \
+constants.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c numbertheory.c /Fo.objs\numbertheory.obj
+
+sixstep.obj:\
+Makefile sixstep.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h sixstep.h transpose.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS) -c sixstep.c
+
+.objs\sixstep.obj:\
+Makefile sixstep.c mpdecimal.h bits.h constants.h difradix2.h \
+numbertheory.h sixstep.h transpose.h umodarith.h typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c sixstep.c /Fo.objs\sixstep.obj
+
+transpose.obj:\
+Makefile transpose.c mpdecimal.h bits.h constants.h transpose.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS) -c transpose.c
+
+.objs\transpose.obj:\
+Makefile transpose.c mpdecimal.h bits.h constants.h transpose.h \
+typearith.h
+       $(CC) $(MPD_CFLAGS_SHARED) -c transpose.c /Fo.objs\transpose.obj
+
+vcdiv64.obj:\
+Makefile vcdiv64.asm
+       ml64 /c /Cx vcdiv64.asm
+
+
+
+FORCE:
+
+bench: FORCE
+       $(CC) $(MPD_BIN_CFLAGS) bench.c $(LIBSTATIC)
+
+bench_shared: FORCE
+       $(CC) $(MPD_BIN_CFLAGS_SHARED) bench.c /Fobench_shared $(LIBIMPORT)
+
+pow: FORCE
+       $(CC) -I. $(MPD_BIN_CFLAGS) examples\pow.c $(LIBSTATIC)
+
+sqrt: FORCE
+       $(CC) -I. $(MPD_BIN_CFLAGS) examples\sqrt.c $(LIBSTATIC)
+
+examples: pow sqrt
+
+
+
+profile: FORCE
+       nmake clean
+       nmake "PGOFLAGS=/GL" "LDFLAGS=/LTCG:PGI"
+       nmake "PGOFLAGS=/GL" bench_shared
+       bench_shared.exe $(MPD_PREC) 1000
+       bench_shared.exe $(MPD_DPREC) 1000
+       del /Q *.dll bench_shared.exe
+       link /DLL /LTCG:PGO /out:$(LIBSHARED) /implib:$(LIBIMPORT) $(SHARED_OBJS)
+       mt -manifest $(LIBSHARED).manifest -outputresource:$(LIBSHARED);2
+       nmake bench_shared
+       bench_shared.exe $(MPD_PREC) 1000
+       bench_shared.exe $(MPD_DPREC) 1000
+
+clean: FORCE
+       -@if exist *.obj del *.obj
+       -@cd .objs
+       -@if exist *.obj del *.obj
+       -@cd ..
+       -@if exist *.dll del *.dll
+       -@if exist *.exp del *.exp
+       -@if exist *.lib del *.lib
+       -@if exist *.ilk del *.ilk
+       -@if exist *.pdb del *.pdb
+       -@if exist *.pgc del *.pgc
+       -@if exist *.pgd del *.pgd
+       -@if exist *.manifest del *.manifest
+       -@if exist *.exe del *.exe
+       -@if exist mpdecimal.h del mpdecimal.h
+       -@cd ..\tests
+       -@if exist Makefile nmake clean
+
+distclean: FORCE
+       nmake clean
+       -@if exist Makefile del Makefile
+       -@cd ..\tests
+       -@if exist Makefile nmake distclean
diff --git a/libmpdec/README.txt b/libmpdec/README.txt
new file mode 100644 (file)
index 0000000..49097a6
--- /dev/null
@@ -0,0 +1,84 @@
+
+
+libmpdec
+========
+
+libmpdec is a fast C/C++ library for correctly-rounded arbitrary precision
+decimal floating point arithmetic. It is a complete implementation of
+Mike Cowlishaw/IBM's General Decimal Arithmetic Specification.
+
+
+  Core files for small and medium precision arithmetic
+  ----------------------------------------------------
+
+    basearith.{c,h}  ->  Core arithmetic in base 10**9 or 10**19.
+    bits.h           ->  Portable detection of least/most significant one-bit.
+    constants.{c,h}  ->  Constants that are used in multiple files.
+    context.c        ->  Context functions.
+    io.{c,h}         ->  Conversions between mpd_t and ASCII strings,
+                         mpd_t formatting (allows UTF-8 fill character).
+    mpalloc.{c,h}    ->  Allocation handlers with overflow detection
+                         and functions for switching between static
+                         and dynamic mpd_t.
+    mpdecimal.{c,h}  ->  All (quiet) functions of the specification.
+    typearith.h      ->  Fast primitives for double word multiplication,
+                         division etc.
+
+    Visual Studio only:
+    ~~~~~~~~~~~~~~~~~~~
+      vcdiv64.asm   ->  Double word division used in typearith.h. VS 2008 does
+                        not allow inline asm for x64. Also, it does not provide
+                        an intrinsic for double word division.
+
+  Files for bignum arithmetic:
+  ----------------------------
+
+    The following files implement the Fast Number Theoretic Transform
+    used for multiplying coefficients with more than 1024 words (see
+    mpdecimal.c: _mpd_fntmul()).
+
+      umodarith.h        ->  Fast low level routines for unsigned modular arithmetic.
+      numbertheory.{c,h} ->  Routines for setting up the Number Theoretic Transform.
+      difradix2.{c,h}    ->  Decimation in frequency transform, used as the
+                             "base case" by the following three files:
+
+        fnt.{c,h}        ->  Transform arrays up to 4096 words.
+        sixstep.{c,h}    ->  Transform larger arrays of length 2**n.
+        fourstep.{c,h}   ->  Transform larger arrays of length 3 * 2**n.
+
+      convolute.{c,h}    ->  Fast convolution using one of the three transform
+                             functions.
+      transpose.{c,h}    ->  Transpositions needed for the sixstep algorithm.
+      crt.{c,h}          ->  Chinese Remainder Theorem: use information from three
+                             transforms modulo three different primes to get the
+                             final result.
+
+
+Pointers to literature, proofs and more
+=======================================
+
+  literature/
+  -----------
+
+    REFERENCES.txt  ->  List of relevant papers.
+    bignum.txt      ->  Explanation of the Fast Number Theoretic Transform (FNT).
+    fnt.py          ->  Verify constants used in the FNT; Python demo for the
+                        O(N**2) discrete transform.
+
+    matrix-transform.txt -> Proof for the Matrix Fourier Transform used in
+                            fourstep.c.
+    six-step.txt         -> Show that the algorithm used in sixstep.c is
+                            a variant of the Matrix Fourier Transform.
+    mulmod-64.txt        -> Proof for the mulmod64 algorithm from
+                            umodarith.h.
+    mulmod-ppro.txt      -> Proof for the x87 FPU modular multiplication
+                            from umodarith.h.
+    umodarith.lisp       -> ACL2 proofs for many functions from umodarith.h.
+
+
+Library Author
+==============
+
+  Stefan Krah <skrah@bytereef.org>
+
+
diff --git a/libmpdec/basearith.c b/libmpdec/basearith.c
new file mode 100644 (file)
index 0000000..29dee70
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "basearith.h"
+#include "constants.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+/*********************************************************************/
+/*                   Calculations in base MPD_RADIX                  */
+/*********************************************************************/
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ *     w := sum of u (len m) and v (len n)
+ *     n > 0 and m >= n
+ * The calling function has to handle a possible final carry.
+ */
+mpd_uint_t
+_mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+             mpd_size_t m, mpd_size_t n)
+{
+    mpd_uint_t s;
+    mpd_uint_t carry = 0;
+    mpd_size_t i;
+
+    assert(n > 0 && m >= n);
+
+    /* add n members of u and v */
+    for (i = 0; i < n; i++) {
+        s = u[i] + (v[i] + carry);
+        carry = (s < u[i]) | (s >= MPD_RADIX);
+        w[i] = carry ? s-MPD_RADIX : s;
+    }
+    /* if there is a carry, propagate it */
+    for (; carry && i < m; i++) {
+        s = u[i] + carry;
+        carry = (s == MPD_RADIX);
+        w[i] = carry ? 0 : s;
+    }
+    /* copy the rest of u */
+    for (; i < m; i++) {
+        w[i] = u[i];
+    }
+
+    return carry;
+}
+
+/*
+ * Add the contents of u to w. Carries are propagated further. The caller
+ * has to make sure that w is big enough.
+ */
+void
+_mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n)
+{
+    mpd_uint_t s;
+    mpd_uint_t carry = 0;
+    mpd_size_t i;
+
+    if (n == 0) return;
+
+    /* add n members of u to w */
+    for (i = 0; i < n; i++) {
+        s = w[i] + (u[i] + carry);
+        carry = (s < w[i]) | (s >= MPD_RADIX);
+        w[i] = carry ? s-MPD_RADIX : s;
+    }
+    /* if there is a carry, propagate it */
+    for (; carry; i++) {
+        s = w[i] + carry;
+        carry = (s == MPD_RADIX);
+        w[i] = carry ? 0 : s;
+    }
+}
+
+/*
+ * Add v to w (len m). The calling function has to handle a possible
+ * final carry. Assumption: m > 0.
+ */
+mpd_uint_t
+_mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v)
+{
+    mpd_uint_t s;
+    mpd_uint_t carry;
+    mpd_size_t i;
+
+    assert(m > 0);
+
+    /* add v to w */
+    s = w[0] + v;
+    carry = (s < v) | (s >= MPD_RADIX);
+    w[0] = carry ? s-MPD_RADIX : s;
+
+    /* if there is a carry, propagate it */
+    for (i = 1; carry && i < m; i++) {
+        s = w[i] + carry;
+        carry = (s == MPD_RADIX);
+        w[i] = carry ? 0 : s;
+    }
+
+    return carry;
+}
+
+/* Increment u. The calling function has to handle a possible carry. */
+mpd_uint_t
+_mpd_baseincr(mpd_uint_t *u, mpd_size_t n)
+{
+    mpd_uint_t s;
+    mpd_uint_t carry = 1;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    /* if there is a carry, propagate it */
+    for (i = 0; carry && i < n; i++) {
+        s = u[i] + carry;
+        carry = (s == MPD_RADIX);
+        u[i] = carry ? 0 : s;
+    }
+
+    return carry;
+}
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ *     w := difference of u (len m) and v (len n).
+ *     number in u >= number in v;
+ */
+void
+_mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+             mpd_size_t m, mpd_size_t n)
+{
+    mpd_uint_t d;
+    mpd_uint_t borrow = 0;
+    mpd_size_t i;
+
+    assert(m > 0 && n > 0);
+
+    /* subtract n members of v from u */
+    for (i = 0; i < n; i++) {
+        d = u[i] - (v[i] + borrow);
+        borrow = (u[i] < d);
+        w[i] = borrow ? d + MPD_RADIX : d;
+    }
+    /* if there is a borrow, propagate it */
+    for (; borrow && i < m; i++) {
+        d = u[i] - borrow;
+        borrow = (u[i] == 0);
+        w[i] = borrow ? MPD_RADIX-1 : d;
+    }
+    /* copy the rest of u */
+    for (; i < m; i++) {
+        w[i] = u[i];
+    }
+}
+
+/*
+ * Subtract the contents of u from w. w is larger than u. Borrows are
+ * propagated further, but eventually w can absorb the final borrow.
+ */
+void
+_mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n)
+{
+    mpd_uint_t d;
+    mpd_uint_t borrow = 0;
+    mpd_size_t i;
+
+    if (n == 0) return;
+
+    /* subtract n members of u from w */
+    for (i = 0; i < n; i++) {
+        d = w[i] - (u[i] + borrow);
+        borrow = (w[i] < d);
+        w[i] = borrow ? d + MPD_RADIX : d;
+    }
+    /* if there is a borrow, propagate it */
+    for (; borrow; i++) {
+        d = w[i] - borrow;
+        borrow = (w[i] == 0);
+        w[i] = borrow ? MPD_RADIX-1 : d;
+    }
+}
+
+/* w := product of u (len n) and v (single word) */
+void
+_mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t carry = 0;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    for (i=0; i < n; i++) {
+
+        _mpd_mul_words(&hi, &lo, u[i], v);
+        lo = carry + lo;
+        if (lo < carry) hi++;
+
+        _mpd_div_words_r(&carry, &w[i], hi, lo);
+    }
+    w[i] = carry;
+}
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ *     w := product of u (len m) and v (len n)
+ *     w must be initialized to zero
+ */
+void
+_mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+             mpd_size_t m, mpd_size_t n)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t carry;
+    mpd_size_t i, j;
+
+    assert(m > 0 && n > 0);
+
+    for (j=0; j < n; j++) {
+        carry = 0;
+        for (i=0; i < m; i++) {
+
+            _mpd_mul_words(&hi, &lo, u[i], v[j]);
+            lo = w[i+j] + lo;
+            if (lo < w[i+j]) hi++;
+            lo = carry + lo;
+            if (lo < carry) hi++;
+
+            _mpd_div_words_r(&carry, &w[i+j], hi, lo);
+        }
+        w[j+m] = carry;
+    }
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1, exercise 16:
+ *     w := quotient of u (len n) divided by a single word v
+ */
+mpd_uint_t
+_mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t rem = 0;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    for (i=n-1; i != MPD_SIZE_MAX; i--) {
+
+        _mpd_mul_words(&hi, &lo, rem, MPD_RADIX);
+        lo = u[i] + lo;
+        if (lo < u[i]) hi++;
+
+        _mpd_div_words(&w[i], &rem, hi, lo, v);
+    }
+
+    return rem;
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1:
+ *     q, r := quotient and remainder of uconst (len nplusm)
+ *             divided by vconst (len n)
+ *     nplusm >= n
+ *
+ * If r is not NULL, r will contain the remainder. If r is NULL, the
+ * return value indicates if there is a remainder: 1 for true, 0 for
+ * false.  A return value of -1 indicates an error.
+ */
+int
+_mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r,
+                const mpd_uint_t *uconst, const mpd_uint_t *vconst,
+                mpd_size_t nplusm, mpd_size_t n)
+{
+    mpd_uint_t ustatic[MPD_MINALLOC_MAX];
+    mpd_uint_t vstatic[MPD_MINALLOC_MAX];
+    mpd_uint_t *u = ustatic;
+    mpd_uint_t *v = vstatic;
+    mpd_uint_t d, qhat, rhat, w2[2];
+    mpd_uint_t hi, lo, x;
+    mpd_uint_t carry;
+    mpd_size_t i, j, m;
+    int retval = 0;
+
+    assert(n > 1 && nplusm >= n);
+    m = sub_size_t(nplusm, n);
+
+    /* D1: normalize */
+    d = MPD_RADIX / (vconst[n-1] + 1);
+
+    if (nplusm >= MPD_MINALLOC_MAX) {
+        if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) {
+            return -1;
+        }
+    }
+    if (n >= MPD_MINALLOC_MAX) {
+        if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) {
+            mpd_free(u);
+            return -1;
+        }
+    }
+
+    _mpd_shortmul(u, uconst, nplusm, d);
+    _mpd_shortmul(v, vconst, n, d);
+
+    /* D2: loop */
+    for (j=m; j != MPD_SIZE_MAX; j--) {
+
+        /* D3: calculate qhat and rhat */
+        rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]);
+        qhat = w2[1] * MPD_RADIX + w2[0];
+
+        while (1) {
+            if (qhat < MPD_RADIX) {
+                _mpd_singlemul(w2, qhat, v[n-2]);
+                if (w2[1] <= rhat) {
+                    if (w2[1] != rhat || w2[0] <= u[j+n-2]) {
+                        break;
+                    }
+                }
+            }
+            qhat -= 1;
+            rhat += v[n-1];
+            if (rhat < v[n-1] || rhat >= MPD_RADIX) {
+                break;
+            }
+        }
+        /* D4: multiply and subtract */
+        carry = 0;
+        for (i=0; i <= n; i++) {
+
+            _mpd_mul_words(&hi, &lo, qhat, v[i]);
+
+            lo = carry + lo;
+            if (lo < carry) hi++;
+
+            _mpd_div_words_r(&hi, &lo, hi, lo);
+
+            x = u[i+j] - lo;
+            carry = (u[i+j] < x);
+            u[i+j] = carry ? x+MPD_RADIX : x;
+            carry += hi;
+        }
+        q[j] = qhat;
+        /* D5: test remainder */
+        if (carry) {
+            q[j] -= 1;
+            /* D6: add back */
+            (void)_mpd_baseadd(u+j, u+j, v, n+1, n);
+        }
+    }
+
+    /* D8: unnormalize */
+    if (r != NULL) {
+        _mpd_shortdiv(r, u, n, d);
+        /* we are not interested in the return value here */
+        retval = 0;
+    }
+    else {
+        retval = !_mpd_isallzero(u, n);
+    }
+
+
+if (u != ustatic) mpd_free(u);
+if (v != vstatic) mpd_free(v);
+return retval;
+}
+
+/*
+ * Left shift of src by 'shift' digits; src may equal dest.
+ *
+ *  dest := area of n mpd_uint_t with space for srcdigits+shift digits.
+ *  src  := coefficient with length m.
+ *
+ * The case splits in the function are non-obvious. The following
+ * equations might help:
+ *
+ *  Let msdigits denote the number of digits in the most significant
+ *  word of src. Then 1 <= msdigits <= rdigits.
+ *
+ *   1) shift = q * rdigits + r
+ *   2) srcdigits = qsrc * rdigits + msdigits
+ *   3) destdigits = shift + srcdigits
+ *                 = q * rdigits + r + qsrc * rdigits + msdigits
+ *                 = q * rdigits + (qsrc * rdigits + (r + msdigits))
+ *
+ *  The result has q zero words, followed by the coefficient that is left-
+ *  shifted by r.  The case r == 0 is trivial. For r > 0, it is important
+ *  to keep in mind that we always read m source words, but write m+1
+ *  destination words if r + msdigits > rdigits, m words otherwise.
+ */
+void
+_mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, mpd_size_t m,
+                mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+    /* spurious uninitialized warnings */
+    mpd_uint_t l=l, lprev=lprev, h=h;
+#else
+    mpd_uint_t l, lprev, h;
+#endif
+    mpd_uint_t q, r;
+    mpd_uint_t ph;
+
+    assert(m > 0 && n >= m);
+
+    _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+    if (r != 0) {
+
+        ph = mpd_pow10[r];
+
+        --m; --n;
+        _mpd_divmod_pow10(&h, &lprev, src[m--], MPD_RDIGITS-r);
+        if (h != 0) { /* r + msdigits > rdigits <==> h != 0 */
+            dest[n--] = h;
+        }
+        /* write m-1 shifted words */
+        for (; m != MPD_SIZE_MAX; m--,n--) {
+            _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r);
+            dest[n] = ph * lprev + h;
+            lprev = l;
+        }
+        /* write least significant word */
+        dest[q] = ph * lprev;
+    }
+    else {
+        while (--m != MPD_SIZE_MAX) {
+            dest[m+q] = src[m];
+        }
+    }
+
+    mpd_uint_zero(dest, q);
+}
+
+/*
+ * Right shift of src by 'shift' digits; src may equal dest.
+ * Assumption: srcdigits-shift > 0.
+ *
+ *  dest := area with space for srcdigits-shift digits.
+ *  src  := coefficient with length 'slen'.
+ *
+ * The case splits in the function rely on the following equations:
+ *
+ *  Let msdigits denote the number of digits in the most significant
+ *  word of src. Then 1 <= msdigits <= rdigits.
+ *
+ *  1) shift = q * rdigits + r
+ *  2) srcdigits = qsrc * rdigits + msdigits
+ *  3) destdigits = srcdigits - shift
+ *                = qsrc * rdigits + msdigits - (q * rdigits + r)
+ *                = (qsrc - q) * rdigits + msdigits - r
+ *
+ * Since destdigits > 0 and 1 <= msdigits <= rdigits:
+ *
+ *  4) qsrc >= q
+ *  5) qsrc == q  ==>  msdigits > r
+ *
+ * The result has slen-q words if msdigits > r, slen-q-1 words otherwise.
+ */
+mpd_uint_t
+_mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen,
+                mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+    /* spurious uninitialized warnings */
+    mpd_uint_t l=l, h=h, hprev=hprev; /* low, high, previous high */
+#else
+    mpd_uint_t l, h, hprev; /* low, high, previous high */
+#endif
+    mpd_uint_t rnd, rest;   /* rounding digit, rest */
+    mpd_uint_t q, r;
+    mpd_size_t i, j;
+    mpd_uint_t ph;
+
+    assert(slen > 0);
+
+    _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+    rnd = rest = 0;
+    if (r != 0) {
+
+        ph = mpd_pow10[MPD_RDIGITS-r];
+
+        _mpd_divmod_pow10(&hprev, &rest, src[q], r);
+        _mpd_divmod_pow10(&rnd, &rest, rest, r-1);
+
+        if (rest == 0 && q > 0) {
+            rest = !_mpd_isallzero(src, q);
+        }
+        /* write slen-q-1 words */
+        for (j=0,i=q+1; i<slen; i++,j++) {
+            _mpd_divmod_pow10(&h, &l, src[i], r);
+            dest[j] = ph * l + hprev;
+            hprev = h;
+        }
+        /* write most significant word */
+        if (hprev != 0) { /* always the case if slen==q-1 */
+            dest[j] = hprev;
+        }
+    }
+    else {
+        if (q > 0) {
+            _mpd_divmod_pow10(&rnd, &rest, src[q-1], MPD_RDIGITS-1);
+            /* is there any non-zero digit below rnd? */
+            if (rest == 0) rest = !_mpd_isallzero(src, q-1);
+        }
+        for (j = 0; j < slen-q; j++) {
+            dest[j] = src[q+j];
+        }
+    }
+
+    /* 0-4  ==> rnd+rest < 0.5   */
+    /* 5    ==> rnd+rest == 0.5  */
+    /* 6-9  ==> rnd+rest > 0.5   */
+    return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd;
+}
+
+/*********************************************************************/
+/*                      Calculations in base b                       */
+/*********************************************************************/
+
+/*
+ * Add v to w (len m). The calling function has to handle a possible
+ * final carry. Assumption: m > 0.
+ */
+mpd_uint_t
+_mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, mpd_uint_t b)
+{
+    mpd_uint_t s;
+    mpd_uint_t carry;
+    mpd_size_t i;
+
+    assert(m > 0);
+
+    /* add v to w */
+    s = w[0] + v;
+    carry = (s < v) | (s >= b);
+    w[0] = carry ? s-b : s;
+
+    /* if there is a carry, propagate it */
+    for (i = 1; carry && i < m; i++) {
+        s = w[i] + carry;
+        carry = (s == b);
+        w[i] = carry ? 0 : s;
+    }
+
+    return carry;
+}
+
+/* w := product of u (len n) and v (single word). Return carry. */
+mpd_uint_t
+_mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t carry = 0;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    for (i=0; i < n; i++) {
+
+        _mpd_mul_words(&hi, &lo, u[i], v);
+        lo = carry + lo;
+        if (lo < carry) hi++;
+
+        _mpd_div_words_r(&carry, &w[i], hi, lo);
+    }
+
+    return carry;
+}
+
+/* w := product of u (len n) and v (single word) */
+mpd_uint_t
+_mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                mpd_uint_t v, mpd_uint_t b)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t carry = 0;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    for (i=0; i < n; i++) {
+
+        _mpd_mul_words(&hi, &lo, u[i], v);
+        lo = carry + lo;
+        if (lo < carry) hi++;
+
+        _mpd_div_words(&carry, &w[i], hi, lo, b);
+    }
+
+    return carry;
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1, exercise 16:
+ *     w := quotient of u (len n) divided by a single word v
+ */
+mpd_uint_t
+_mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                mpd_uint_t v, mpd_uint_t b)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t rem = 0;
+    mpd_size_t i;
+
+    assert(n > 0);
+
+    for (i=n-1; i != MPD_SIZE_MAX; i--) {
+
+        _mpd_mul_words(&hi, &lo, rem, b);
+        lo = u[i] + lo;
+        if (lo < u[i]) hi++;
+
+        _mpd_div_words(&w[i], &rem, hi, lo, v);
+    }
+
+    return rem;
+}
diff --git a/libmpdec/basearith.h b/libmpdec/basearith.h
new file mode 100644 (file)
index 0000000..89b84ed
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_BASEARITH_H_
+#define LIBMPDEC_BASEARITH_H_
+
+
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+mpd_uint_t _mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+                        mpd_size_t m, mpd_size_t n);
+void _mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n);
+mpd_uint_t _mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v);
+mpd_uint_t _mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v,
+                           mpd_uint_t b);
+mpd_uint_t _mpd_baseincr(mpd_uint_t *u, mpd_size_t n);
+void _mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+                  mpd_size_t m, mpd_size_t n);
+void _mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n);
+void _mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+                  mpd_size_t m, mpd_size_t n);
+void _mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                   mpd_uint_t v);
+mpd_uint_t _mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                           mpd_uint_t v);
+mpd_uint_t _mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                           mpd_uint_t v, mpd_uint_t b);
+mpd_uint_t _mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                         mpd_uint_t v);
+mpd_uint_t _mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+                           mpd_uint_t v, mpd_uint_t b);
+int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst,
+                    const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n);
+void _mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n,
+                     mpd_size_t m, mpd_size_t shift);
+mpd_uint_t _mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen,
+                           mpd_size_t shift);
+
+
+
+#ifdef CONFIG_64
+extern const mpd_uint_t mprime_rdx;
+
+/*
+ * Algorithm from: Division by Invariant Integers using Multiplication,
+ * T. Granlund and P. L. Montgomery, Proceedings of the SIGPLAN '94
+ * Conference on Programming Language Design and Implementation.
+ *
+ * http://gmplib.org/~tege/divcnst-pldi94.pdf
+ *
+ * Variables from the paper and their translations (See section 8):
+ *
+ *  N := 64
+ *  d := MPD_RADIX
+ *  l := 64
+ *  m' := floor((2**(64+64) - 1)/MPD_RADIX) - 2**64
+ *
+ * Since N-l == 0:
+ *
+ *  dnorm := d
+ *  n2 := hi
+ *  n10 := lo
+ *
+ * ACL2 proof: mpd-div-words-r-correct
+ */
+static inline void
+_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo)
+{
+    mpd_uint_t n_adj, h, l, t;
+    mpd_uint_t n1_neg;
+
+    /* n1_neg = if lo >= 2**63 then MPD_UINT_MAX else 0 */
+    n1_neg = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0;
+    /* n_adj = if lo >= 2**63 then lo+MPD_RADIX else lo */
+    n_adj = lo + (n1_neg & MPD_RADIX);
+
+    /* (h, l) = if lo >= 2**63 then m'*(hi+1) else m'*hi */
+    _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg);
+    l = l + n_adj;
+    if (l < n_adj) h++;
+    t = h + hi;
+    /* At this point t == qest, with q == qest or q == qest+1:
+     *   1) 0 <= 2**64*hi + lo - qest*MPD_RADIX < 2*MPD_RADIX
+     */
+
+    /* t = 2**64-1 - qest = 2**64 - (qest+1) */
+    t = MPD_UINT_MAX - t;
+
+    /* (h, l) = 2**64*MPD_RADIX - (qest+1)*MPD_RADIX */
+    _mpd_mul_words(&h, &l, t, MPD_RADIX);
+    l = l + lo;
+    if (l < lo) h++;
+    h += hi;
+    h -= MPD_RADIX;
+    /* (h, l) = 2**64*hi + lo - (qest+1)*MPD_RADIX (mod 2**128)
+     * Case q == qest+1:
+     *     a) h == 0, l == r
+     *     b) q := h - t == qest+1
+     *     c) r := l
+     * Case q == qest:
+     *     a) h == MPD_UINT_MAX, l == 2**64-(MPD_RADIX-r)
+     *     b) q := h - t == qest
+     *     c) r := l + MPD_RADIX = r
+     */
+
+    *q = (h - t);
+    *r = l + (MPD_RADIX & h);
+}
+#else
+static inline void
+_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo)
+{
+    _mpd_div_words(q, r, hi, lo, MPD_RADIX);
+}
+#endif
+
+
+/* Multiply two single base MPD_RADIX words, store result in array w[2]. */
+static inline void
+_mpd_singlemul(mpd_uint_t w[2], mpd_uint_t u, mpd_uint_t v)
+{
+    mpd_uint_t hi, lo;
+
+    _mpd_mul_words(&hi, &lo, u, v);
+    _mpd_div_words_r(&w[1], &w[0], hi, lo);
+}
+
+/* Multiply u (len 2) and v (len m, 1 <= m <= 2). */
+static inline void
+_mpd_mul_2_le2(mpd_uint_t w[4], mpd_uint_t u[2], mpd_uint_t v[2], mpd_ssize_t m)
+{
+    mpd_uint_t hi, lo;
+
+    _mpd_mul_words(&hi, &lo, u[0], v[0]);
+    _mpd_div_words_r(&w[1], &w[0], hi, lo);
+
+    _mpd_mul_words(&hi, &lo, u[1], v[0]);
+    lo = w[1] + lo;
+    if (lo < w[1]) hi++;
+    _mpd_div_words_r(&w[2], &w[1], hi, lo);
+    if (m == 1) return;
+
+    _mpd_mul_words(&hi, &lo, u[0], v[1]);
+    lo = w[1] + lo;
+    if (lo < w[1]) hi++;
+    _mpd_div_words_r(&w[3], &w[1], hi, lo);
+
+    _mpd_mul_words(&hi, &lo, u[1], v[1]);
+    lo = w[2] + lo;
+    if (lo < w[2]) hi++;
+    lo = w[3] + lo;
+    if (lo < w[3]) hi++;
+    _mpd_div_words_r(&w[3], &w[2], hi, lo);
+}
+
+
+/*
+ * Test if all words from data[len-1] to data[0] are zero. If len is 0, nothing
+ * is tested and the coefficient is regarded as "all zero".
+ */
+static inline int
+_mpd_isallzero(const mpd_uint_t *data, mpd_ssize_t len)
+{
+    while (--len >= 0) {
+        if (data[len] != 0) return 0;
+    }
+    return 1;
+}
+
+/*
+ * Test if all full words from data[len-1] to data[0] are MPD_RADIX-1
+ * (all nines). Return true if len == 0.
+ */
+static inline int
+_mpd_isallnine(const mpd_uint_t *data, mpd_ssize_t len)
+{
+    while (--len >= 0) {
+        if (data[len] != MPD_RADIX-1) return 0;
+    }
+    return 1;
+}
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_BASEARITH_H_ */
diff --git a/libmpdec/bench.c b/libmpdec/bench.c
new file mode 100644 (file)
index 0000000..656584e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "mpdecimal.h"
+
+
+static void
+err_exit(const char *msg)
+{
+    fprintf(stderr, "%s\n", msg);
+    exit(1);
+}
+
+static mpd_t *
+new_mpd(void)
+{
+    mpd_t *x = mpd_qnew();
+    if (x == NULL) {
+        err_exit("out of memory");
+    }
+
+    return x;
+}
+
+/* Nonsense version of escape-time algorithm for calculating a Mandelbrot
+ * set. Just for benchmarking. */
+static void
+color_point(mpd_t *x0, mpd_t *y0, long maxiter, mpd_context_t *ctx)
+{
+    mpd_t *x, *y, *sq_x, *sq_y;
+    mpd_t *two;
+
+    x = new_mpd();
+    y = new_mpd();
+    mpd_set_u32(x, 0, ctx);
+    mpd_set_u32(y, 0, ctx);
+
+    sq_x = new_mpd();
+    sq_y = new_mpd();
+    mpd_set_u32(sq_x, 0, ctx);
+    mpd_set_u32(sq_y, 0, ctx);
+
+    two = new_mpd();
+    mpd_set_u32(two, 2, ctx);
+
+    for (long i = 0; i < maxiter; i++) {
+        mpd_mul(y, x, y, ctx);
+        mpd_mul(y, y, two, ctx);
+        mpd_add(y, y, y0, ctx);
+
+        mpd_sub(x, sq_x, sq_y, ctx);
+        mpd_add(x, x, x0, ctx);
+
+        mpd_mul(sq_x, x, x, ctx);
+        mpd_mul(sq_y, y, y, ctx);
+    }
+
+    mpd_copy(x0, x, ctx);
+
+    mpd_del(two);
+    mpd_del(sq_y);
+    mpd_del(sq_x);
+    mpd_del(y);
+    mpd_del(x);
+}
+
+int
+main(int argc, char **argv)
+{
+    mpd_context_t ctx;
+    mpd_t *x0, *y0;
+    uint32_t prec = 19;
+    long iter = 10000000;
+    clock_t start_clock, end_clock;
+
+    if (argc != 3) {
+        err_exit("usage: bench prec iter\n");
+    }
+    prec = strtoul(argv[1], NULL, 10);
+    iter = strtol(argv[2], NULL, 10);
+
+    mpd_init(&ctx, prec);
+    /* no more MPD_MINALLOC changes after here */
+
+    x0 = new_mpd();
+    y0 = new_mpd();
+    mpd_set_string(x0, "0.222", &ctx);
+    mpd_set_string(y0, "0.333", &ctx);
+    if (ctx.status & MPD_Errors) {
+        mpd_del(y0);
+        mpd_del(x0);
+        err_exit("unexpected error during conversion");
+    }
+
+    start_clock = clock();
+    color_point(x0, y0, iter, &ctx);
+    end_clock = clock();
+
+    mpd_print(x0);
+    fprintf(stderr, "time: %f\n\n", (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+    mpd_del(y0);
+    mpd_del(x0);
+
+    return 0;
+}
diff --git a/libmpdec/bench_full.c b/libmpdec/bench_full.c
new file mode 100644 (file)
index 0000000..32ef36b
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "mpdecimal.h"
+
+
+static void
+err_exit(const char *msg)
+{
+    fprintf(stderr, "%s\n", msg);
+    exit(1);
+}
+
+static mpd_t *
+new_mpd(void)
+{
+    mpd_t *x = mpd_qnew();
+    if (x == NULL) {
+        err_exit("out of memory");
+    }
+
+    return x;
+}
+
+/*
+ * Example from: http://en.wikipedia.org/wiki/Mandelbrot_set
+ *
+ * Escape time algorithm for drawing the set:
+ *
+ * Point x0, y0 is deemed to be in the Mandelbrot set if the return
+ * value is maxiter. Lower return values indicate how quickly points
+ * escaped and can be used for coloring.
+ */
+static int
+color_point(const mpd_t *x0, const mpd_t *y0, const long maxiter, mpd_context_t *ctx)
+{
+    mpd_t *x, *y, *sq_x, *sq_y;
+    mpd_t *two, *four, *c;
+    long i;
+
+    x = new_mpd();
+    y = new_mpd();
+    mpd_set_u32(x, 0, ctx);
+    mpd_set_u32(y, 0, ctx);
+
+    sq_x = new_mpd();
+    sq_y = new_mpd();
+    mpd_set_u32(sq_x, 0, ctx);
+    mpd_set_u32(sq_y, 0, ctx);
+
+    two = new_mpd();
+    four = new_mpd();
+    mpd_set_u32(two, 2, ctx);
+    mpd_set_u32(four, 4, ctx);
+
+    c = new_mpd();
+    mpd_set_u32(c, 0, ctx);
+
+    for (i = 0; i < maxiter && mpd_cmp(c, four, ctx) <= 0; i++) {
+        mpd_mul(y, x, y, ctx);
+        mpd_mul(y, y, two, ctx);
+        mpd_add(y, y, y0, ctx);
+
+        mpd_sub(x, sq_x, sq_y, ctx);
+        mpd_add(x, x, x0, ctx);
+
+        mpd_mul(sq_x, x, x, ctx);
+        mpd_mul(sq_y, y, y, ctx);
+        mpd_add(c, sq_x, sq_y, ctx);
+    }
+
+    mpd_del(c);
+    mpd_del(four);
+    mpd_del(two);
+    mpd_del(sq_y);
+    mpd_del(sq_x);
+    mpd_del(y);
+    mpd_del(x);
+
+    return i;
+}
+
+int
+main(int argc, char **argv)
+{
+    mpd_context_t ctx;
+    mpd_t *x0, *y0;
+    mpd_t *sqrt_2, *xstep, *ystep;
+    mpd_ssize_t prec = 19;
+
+    long iter = 1000;
+    int points[40][80];
+    int i, j;
+    clock_t start_clock, end_clock;
+
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: ./bench prec iter\n");
+        exit(1);
+    }
+    prec = strtoll(argv[1], NULL, 10);
+    iter = strtol(argv[2], NULL, 10);
+
+    mpd_init(&ctx, prec);
+    /* no more MPD_MINALLOC changes after here */
+
+    sqrt_2 = new_mpd();
+    xstep = new_mpd();
+    ystep = new_mpd();
+    x0 = new_mpd();
+    y0 = new_mpd();
+
+    mpd_set_u32(sqrt_2, 2, &ctx);
+    mpd_sqrt(sqrt_2, sqrt_2, &ctx);
+    mpd_div_u32(xstep, sqrt_2, 40, &ctx);
+    mpd_div_u32(ystep, sqrt_2, 20, &ctx);
+
+    start_clock = clock();
+    mpd_copy(y0, sqrt_2, &ctx);
+    for (i = 0; i < 40; i++) {
+        mpd_copy(x0, sqrt_2, &ctx);
+        mpd_set_negative(x0);
+        for (j = 0; j < 80; j++) {
+            points[i][j] = color_point(x0, y0, iter, &ctx);
+            mpd_add(x0, x0, xstep, &ctx);
+        }
+        mpd_sub(y0, y0, ystep, &ctx);
+    }
+    end_clock = clock();
+
+#ifdef BENCH_VERBOSE
+    for (i = 0; i < 40; i++) {
+        for (j = 0; j < 80; j++) {
+            if (points[i][j] == iter) {
+                putchar('*');
+            }
+            else if (points[i][j] >= 10) {
+                putchar('+');
+            }
+            else if (points[i][j] >= 5) {
+                putchar('.');
+            }
+            else {
+                putchar(' ');
+            }
+        }
+        putchar('\n');
+    }
+    putchar('\n');
+#else
+    (void)points; /* suppress gcc warning */
+#endif
+
+    printf("time: %f\n\n", (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+    mpd_del(y0);
+    mpd_del(x0);
+    mpd_del(ystep);
+    mpd_del(xstep);
+    mpd_del(sqrt_2);
+
+    return 0;
+}
diff --git a/libmpdec/bits.h b/libmpdec/bits.h
new file mode 100644 (file)
index 0000000..00e21c4
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_BITS_H_
+#define LIBMPDEC_BITS_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Check if n is a power of 2. */
+static inline int
+ispower2(mpd_size_t n)
+{
+    return n != 0 && (n & (n-1)) == 0;
+}
+
+#if defined(ANSI)
+/*
+ * Return the most significant bit position of n from 0 to 31 (63).
+ * Assumptions: n != 0.
+ */
+static inline int
+mpd_bsr(mpd_size_t n)
+{
+    int pos = 0;
+    mpd_size_t tmp;
+
+#ifdef CONFIG_64
+    tmp = n >> 32;
+    if (tmp != 0) { n = tmp; pos += 32; }
+#endif
+    tmp = n >> 16;
+    if (tmp != 0) { n = tmp; pos += 16; }
+    tmp = n >> 8;
+    if (tmp != 0) { n = tmp; pos += 8; }
+    tmp = n >> 4;
+    if (tmp != 0) { n = tmp; pos += 4; }
+    tmp = n >> 2;
+    if (tmp != 0) { n = tmp; pos += 2; }
+    tmp = n >> 1;
+    if (tmp != 0) { n = tmp; pos += 1; }
+
+    return pos + (int)n - 1;
+}
+
+/*
+ * Return the least significant bit position of n from 0 to 31 (63).
+ * Assumptions: n != 0.
+ */
+static inline int
+mpd_bsf(mpd_size_t n)
+{
+    int pos;
+
+#ifdef CONFIG_64
+    pos = 63;
+    if (n & 0x00000000FFFFFFFFULL) { pos -= 32; } else { n >>= 32; }
+    if (n & 0x000000000000FFFFULL) { pos -= 16; } else { n >>= 16; }
+    if (n & 0x00000000000000FFULL) { pos -=  8; } else { n >>=  8; }
+    if (n & 0x000000000000000FULL) { pos -=  4; } else { n >>=  4; }
+    if (n & 0x0000000000000003ULL) { pos -=  2; } else { n >>=  2; }
+    if (n & 0x0000000000000001ULL) { pos -=  1; }
+#else
+    pos = 31;
+    if (n & 0x000000000000FFFFUL) { pos -= 16; } else { n >>= 16; }
+    if (n & 0x00000000000000FFUL) { pos -=  8; } else { n >>=  8; }
+    if (n & 0x000000000000000FUL) { pos -=  4; } else { n >>=  4; }
+    if (n & 0x0000000000000003UL) { pos -=  2; } else { n >>=  2; }
+    if (n & 0x0000000000000001UL) { pos -=  1; }
+#endif
+    return pos;
+}
+/* END ANSI */
+
+#elif defined(ASM)
+/*
+ * Bit scan reverse. Assumptions: a != 0.
+ */
+static inline int
+mpd_bsr(mpd_size_t a)
+{
+    mpd_size_t retval;
+
+    __asm__ (
+#ifdef CONFIG_64
+        "bsrq %1, %0\n\t"
+#else
+        "bsr %1, %0\n\t"
+#endif
+        :"=r" (retval)
+        :"r" (a)
+        :"cc"
+    );
+
+    return (int)retval;
+}
+
+/*
+ * Bit scan forward. Assumptions: a != 0.
+ */
+static inline int
+mpd_bsf(mpd_size_t a)
+{
+    mpd_size_t retval;
+
+    __asm__ (
+#ifdef CONFIG_64
+        "bsfq %1, %0\n\t"
+#else
+        "bsf %1, %0\n\t"
+#endif
+        :"=r" (retval)
+        :"r" (a)
+        :"cc"
+    );
+
+    return (int)retval;
+}
+/* END ASM */
+
+#elif defined(MASM)
+#include <intrin.h>
+/*
+ * Bit scan reverse. Assumptions: a != 0.
+ */
+static inline int __cdecl
+mpd_bsr(mpd_size_t a)
+{
+    unsigned long retval;
+
+#ifdef CONFIG_64
+    _BitScanReverse64(&retval, a);
+#else
+    _BitScanReverse(&retval, a);
+#endif
+
+    return (int)retval;
+}
+
+/*
+ * Bit scan forward. Assumptions: a != 0.
+ */
+static inline int __cdecl
+mpd_bsf(mpd_size_t a)
+{
+    unsigned long retval;
+
+#ifdef CONFIG_64
+    _BitScanForward64(&retval, a);
+#else
+    _BitScanForward(&retval, a);
+#endif
+
+    return (int)retval;
+}
+/* END MASM (_MSC_VER) */
+
+#else
+  #error "missing preprocessor definitions"
+#endif /* BSR/BSF */
+
+
+#endif /* LIBMPDEC_BITS_H_ */
diff --git a/libmpdec/constants.c b/libmpdec/constants.c
new file mode 100644 (file)
index 0000000..7a3516c
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "basearith.h"
+#include "constants.h"
+#include "mpdecimal.h"
+
+
+#if defined(CONFIG_64)
+
+  /* number-theory.c */
+  const mpd_uint_t mpd_moduli[3] = {
+    18446744069414584321ULL, 18446744056529682433ULL, 18446742974197923841ULL
+  };
+  const mpd_uint_t mpd_roots[3]  = {7ULL, 10ULL, 19ULL};
+
+  /* crt.c */
+  const mpd_uint_t INV_P1_MOD_P2   = 18446744055098026669ULL;
+  const mpd_uint_t INV_P1P2_MOD_P3 = 287064143708160ULL;
+  const mpd_uint_t LH_P1P2 = 18446744052234715137ULL;     /* (P1*P2) % 2^64 */
+  const mpd_uint_t UH_P1P2 = 18446744052234715141ULL;     /* (P1*P2) / 2^64 */
+
+  /* transpose.c */
+  const mpd_size_t mpd_bits[64] = {
+    1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,  2048, 4096, 8192, 16384,
+    32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608,
+    16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824,
+    2147483648ULL, 4294967296ULL, 8589934592ULL, 17179869184ULL, 34359738368ULL,
+    68719476736ULL, 137438953472ULL, 274877906944ULL, 549755813888ULL,
+    1099511627776ULL, 2199023255552ULL, 4398046511104, 8796093022208ULL,
+    17592186044416ULL, 35184372088832ULL, 70368744177664ULL, 140737488355328ULL,
+    281474976710656ULL, 562949953421312ULL, 1125899906842624ULL,
+    2251799813685248ULL, 4503599627370496ULL, 9007199254740992ULL,
+    18014398509481984ULL, 36028797018963968ULL, 72057594037927936ULL,
+    144115188075855872ULL, 288230376151711744ULL, 576460752303423488ULL,
+    1152921504606846976ULL, 2305843009213693952ULL, 4611686018427387904ULL,
+    9223372036854775808ULL
+  };
+
+  /* mpdecimal.c */
+  const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = {
+    1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,
+    10000000000ULL,100000000000ULL,1000000000000ULL,10000000000000ULL,
+    100000000000000ULL,1000000000000000ULL,10000000000000000ULL,
+    100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL
+  };
+
+  /* magic number for constant division by MPD_RADIX */
+  const mpd_uint_t mprime_rdx = 15581492618384294730ULL;
+
+#elif defined(CONFIG_32)
+
+  /* number-theory.c */
+  const mpd_uint_t mpd_moduli[3]  = {2113929217UL, 2013265921UL, 1811939329UL};
+  const mpd_uint_t mpd_roots[3]   = {5UL, 31UL, 13UL};
+
+  /* PentiumPro modular multiplication: These constants have to be loaded as
+   * 80 bit long doubles, which are not supported by certain compilers. */
+  const uint32_t mpd_invmoduli[3][3] = {
+    {4293885170U, 2181570688U, 16352U},  /* ((long double) 1 / 2113929217UL) */
+    {1698898177U, 2290649223U, 16352U},  /* ((long double) 1 / 2013265921UL) */
+    {2716021846U, 2545165803U, 16352U}   /* ((long double) 1 / 1811939329UL) */
+  };
+
+  const float MPD_TWO63 = 9223372036854775808.0; /* 2^63 */
+
+  /* crt.c */
+  const mpd_uint_t INV_P1_MOD_P2   = 2013265901UL;
+  const mpd_uint_t INV_P1P2_MOD_P3 = 54UL;
+  const mpd_uint_t LH_P1P2 = 4127195137UL;  /* (P1*P2) % 2^32 */
+  const mpd_uint_t UH_P1P2 = 990904320UL;   /* (P1*P2) / 2^32 */
+
+  /* transpose.c */
+  const mpd_size_t mpd_bits[32] = {
+    1, 2, 4, 8, 16, 32, 64, 128, 256, 512,  1024,  2048, 4096, 8192, 16384,
+    32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608,
+    16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824,
+    2147483648UL
+  };
+
+  /* mpdecimal.c */
+  const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = {
+    1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000
+  };
+
+#else
+  #error "CONFIG_64 or CONFIG_32 must be defined."
+#endif
+
+const char * const mpd_round_string[MPD_ROUND_GUARD] = {
+    "ROUND_UP",          /* round away from 0               */
+    "ROUND_DOWN",        /* round toward 0 (truncate)       */
+    "ROUND_CEILING",     /* round toward +infinity          */
+    "ROUND_FLOOR",       /* round toward -infinity          */
+    "ROUND_HALF_UP",     /* 0.5 is rounded up               */
+    "ROUND_HALF_DOWN",   /* 0.5 is rounded down             */
+    "ROUND_HALF_EVEN",   /* 0.5 is rounded to even          */
+    "ROUND_05UP",        /* round zero or five away from 0  */
+    "ROUND_TRUNC",       /* truncate, but set infinity      */
+};
+
+const char * const mpd_clamp_string[MPD_CLAMP_GUARD] = {
+    "CLAMP_DEFAULT",
+    "CLAMP_IEEE_754"
+};
diff --git a/libmpdec/constants.h b/libmpdec/constants.h
new file mode 100644 (file)
index 0000000..e5460c4
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_CONSTANTS_H_
+#define LIBMPDEC_CONSTANTS_H_
+
+
+#include <stdint.h>
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+/* choice of optimized functions */
+#if defined(CONFIG_64)
+/* x64 */
+  #define MULMOD(a, b) x64_mulmod(a, b, umod)
+  #define MULMOD2C(a0, a1, w) x64_mulmod2c(a0, a1, w, umod)
+  #define MULMOD2(a0, b0, a1, b1) x64_mulmod2(a0, b0, a1, b1, umod)
+  #define POWMOD(base, exp) x64_powmod(base, exp, umod)
+  #define SETMODULUS(modnum) std_setmodulus(modnum, &umod)
+  #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod)
+#elif defined(PPRO)
+/* PentiumPro (or later) gcc inline asm */
+  #define MULMOD(a, b) ppro_mulmod(a, b, &dmod, dinvmod)
+  #define MULMOD2C(a0, a1, w) ppro_mulmod2c(a0, a1, w, &dmod, dinvmod)
+  #define MULMOD2(a0, b0, a1, b1) ppro_mulmod2(a0, b0, a1, b1, &dmod, dinvmod)
+  #define POWMOD(base, exp) ppro_powmod(base, exp, &dmod, dinvmod)
+  #define SETMODULUS(modnum) ppro_setmodulus(modnum, &umod, &dmod, dinvmod)
+  #define SIZE3_NTT(x0, x1, x2, w3table) ppro_size3_ntt(x0, x1, x2, w3table, umod, &dmod, dinvmod)
+#else
+  /* ANSI C99 */
+  #define MULMOD(a, b) std_mulmod(a, b, umod)
+  #define MULMOD2C(a0, a1, w) std_mulmod2c(a0, a1, w, umod)
+  #define MULMOD2(a0, b0, a1, b1) std_mulmod2(a0, b0, a1, b1, umod)
+  #define POWMOD(base, exp) std_powmod(base, exp, umod)
+  #define SETMODULUS(modnum) std_setmodulus(modnum, &umod)
+  #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod)
+#endif
+
+/* PentiumPro (or later) gcc inline asm */
+extern const float MPD_TWO63;
+extern const uint32_t mpd_invmoduli[3][3];
+
+enum {P1, P2, P3};
+
+extern const mpd_uint_t mpd_moduli[];
+extern const mpd_uint_t mpd_roots[];
+extern const mpd_size_t mpd_bits[];
+extern const mpd_uint_t mpd_pow10[];
+
+extern const mpd_uint_t INV_P1_MOD_P2;
+extern const mpd_uint_t INV_P1P2_MOD_P3;
+extern const mpd_uint_t LH_P1P2;
+extern const mpd_uint_t UH_P1P2;
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_CONSTANTS_H_ */
diff --git a/libmpdec/context.c b/libmpdec/context.c
new file mode 100644 (file)
index 0000000..8a2aff1
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mpdecimal.h"
+
+
+void
+mpd_dflt_traphandler(mpd_context_t *ctx)
+{
+    (void)ctx;
+    raise(SIGFPE);
+}
+
+void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler;
+
+
+/* Set guaranteed minimum number of coefficient words. The function may
+   be used once at program start. Setting MPD_MINALLOC to out-of-bounds
+   values is a catastrophic error, so in that case the function exits rather
+   than relying on the user to check a return value. */
+void
+mpd_setminalloc(mpd_ssize_t n)
+{
+    static int minalloc_is_set = 0;
+
+    if (minalloc_is_set) {
+        mpd_err_warn("mpd_setminalloc: ignoring request to set "
+                     "MPD_MINALLOC a second time\n");
+        return;
+    }
+    if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) {
+        mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */
+    }
+    MPD_MINALLOC = n;
+    minalloc_is_set = 1;
+}
+
+void
+mpd_init(mpd_context_t *ctx, mpd_ssize_t prec)
+{
+    mpd_ssize_t ideal_minalloc;
+
+    mpd_defaultcontext(ctx);
+
+    if (!mpd_qsetprec(ctx, prec)) {
+        mpd_addstatus_raise(ctx, MPD_Invalid_context);
+        return;
+    }
+
+    ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS);
+    if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN;
+    if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX;
+
+    mpd_setminalloc(ideal_minalloc);
+}
+
+void
+mpd_maxcontext(mpd_context_t *ctx)
+{
+    ctx->prec=MPD_MAX_PREC;
+    ctx->emax=MPD_MAX_EMAX;
+    ctx->emin=MPD_MIN_EMIN;
+    ctx->round=MPD_ROUND_HALF_EVEN;
+    ctx->traps=MPD_Traps;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=0;
+    ctx->allcr=1;
+}
+
+void
+mpd_defaultcontext(mpd_context_t *ctx)
+{
+    ctx->prec=2*MPD_RDIGITS;
+    ctx->emax=MPD_MAX_EMAX;
+    ctx->emin=MPD_MIN_EMIN;
+    ctx->round=MPD_ROUND_HALF_UP;
+    ctx->traps=MPD_Traps;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=0;
+    ctx->allcr=1;
+}
+
+void
+mpd_basiccontext(mpd_context_t *ctx)
+{
+    ctx->prec=9;
+    ctx->emax=MPD_MAX_EMAX;
+    ctx->emin=MPD_MIN_EMIN;
+    ctx->round=MPD_ROUND_HALF_UP;
+    ctx->traps=MPD_Traps|MPD_Clamped;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=0;
+    ctx->allcr=1;
+}
+
+int
+mpd_ieee_context(mpd_context_t *ctx, int bits)
+{
+    if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) {
+        return -1;
+    }
+
+    ctx->prec = 9 * (bits/32) - 2;
+    ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3));
+    ctx->emin = 1 - ctx->emax;
+    ctx->round=MPD_ROUND_HALF_EVEN;
+    ctx->traps=0;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=1;
+    ctx->allcr=1;
+
+    return 0;
+}
+
+mpd_ssize_t
+mpd_getprec(const mpd_context_t *ctx)
+{
+    return ctx->prec;
+}
+
+mpd_ssize_t
+mpd_getemax(const mpd_context_t *ctx)
+{
+    return ctx->emax;
+}
+
+mpd_ssize_t
+mpd_getemin(const mpd_context_t *ctx)
+{
+    return ctx->emin;
+}
+
+int
+mpd_getround(const mpd_context_t *ctx)
+{
+    return ctx->round;
+}
+
+uint32_t
+mpd_gettraps(const mpd_context_t *ctx)
+{
+    return ctx->traps;
+}
+
+uint32_t
+mpd_getstatus(const mpd_context_t *ctx)
+{
+    return ctx->status;
+}
+
+int
+mpd_getclamp(const mpd_context_t *ctx)
+{
+    return ctx->clamp;
+}
+
+int
+mpd_getcr(const mpd_context_t *ctx)
+{
+    return ctx->allcr;
+}
+
+
+int
+mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec)
+{
+    if (prec <= 0 || prec > MPD_MAX_PREC) {
+        return 0;
+    }
+    ctx->prec = prec;
+    return 1;
+}
+
+int
+mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax)
+{
+    if (emax < 0 || emax > MPD_MAX_EMAX) {
+        return 0;
+    }
+    ctx->emax = emax;
+    return 1;
+}
+
+int
+mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin)
+{
+    if (emin > 0 || emin < MPD_MIN_EMIN) {
+        return 0;
+    }
+    ctx->emin = emin;
+    return 1;
+}
+
+int
+mpd_qsetround(mpd_context_t *ctx, int round)
+{
+    if (!(0 <= round && round < MPD_ROUND_GUARD)) {
+        return 0;
+    }
+    ctx->round = round;
+    return 1;
+}
+
+int
+mpd_qsettraps(mpd_context_t *ctx, uint32_t flags)
+{
+    if (flags > MPD_Max_status) {
+        return 0;
+    }
+    ctx->traps = flags;
+    return 1;
+}
+
+int
+mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags)
+{
+    if (flags > MPD_Max_status) {
+        return 0;
+    }
+    ctx->status = flags;
+    return 1;
+}
+
+int
+mpd_qsetclamp(mpd_context_t *ctx, int c)
+{
+    if (c != 0 && c != 1) {
+        return 0;
+    }
+    ctx->clamp = c;
+    return 1;
+}
+
+int
+mpd_qsetcr(mpd_context_t *ctx, int c)
+{
+    if (c != 0 && c != 1) {
+        return 0;
+    }
+    ctx->allcr = c;
+    return 1;
+}
+
+
+void
+mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags)
+{
+    ctx->status |= flags;
+    if (flags&ctx->traps) {
+        ctx->newtrap = (flags&ctx->traps);
+        mpd_traphandler(ctx);
+    }
+}
diff --git a/libmpdec/convolute.c b/libmpdec/convolute.c
new file mode 100644 (file)
index 0000000..4208b9e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "bits.h"
+#include "constants.h"
+#include "convolute.h"
+#include "fnt.h"
+#include "fourstep.h"
+#include "mpdecimal.h"
+#include "numbertheory.h"
+#include "sixstep.h"
+#include "umodarith.h"
+
+
+/*
+ * Bignum: Fast convolution using the Number Theoretic Transform. Used for
+ *         the multiplication of very large coefficients.
+ */
+
+
+/* Convolute the data in c1 and c2. Result is in c1. */
+int
+fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum)
+{
+    int (*fnt)(mpd_uint_t *, mpd_size_t, int);
+    int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int);
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t n_inv, umod;
+    mpd_size_t i;
+
+
+    SETMODULUS(modnum);
+    n_inv = POWMOD(n, (umod-2));
+
+    if (ispower2(n)) {
+        if (n > SIX_STEP_THRESHOLD) {
+            fnt = six_step_fnt;
+            inv_fnt = inv_six_step_fnt;
+        }
+        else {
+            fnt = std_fnt;
+            inv_fnt = std_inv_fnt;
+        }
+    }
+    else {
+        fnt = four_step_fnt;
+        inv_fnt = inv_four_step_fnt;
+    }
+
+    if (!fnt(c1, n, modnum)) {
+        return 0;
+    }
+    if (!fnt(c2, n, modnum)) {
+        return 0;
+    }
+    for (i = 0; i < n-1; i += 2) {
+        mpd_uint_t x0 = c1[i];
+        mpd_uint_t y0 = c2[i];
+        mpd_uint_t x1 = c1[i+1];
+        mpd_uint_t y1 = c2[i+1];
+        MULMOD2(&x0, y0, &x1, y1);
+        c1[i] = x0;
+        c1[i+1] = x1;
+    }
+
+    if (!inv_fnt(c1, n, modnum)) {
+        return 0;
+    }
+    for (i = 0; i < n-3; i += 4) {
+        mpd_uint_t x0 = c1[i];
+        mpd_uint_t x1 = c1[i+1];
+        mpd_uint_t x2 = c1[i+2];
+        mpd_uint_t x3 = c1[i+3];
+        MULMOD2C(&x0, &x1, n_inv);
+        MULMOD2C(&x2, &x3, n_inv);
+        c1[i] = x0;
+        c1[i+1] = x1;
+        c1[i+2] = x2;
+        c1[i+3] = x3;
+    }
+
+    return 1;
+}
+
+/* Autoconvolute the data in c1. Result is in c1. */
+int
+fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum)
+{
+    int (*fnt)(mpd_uint_t *, mpd_size_t, int);
+    int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int);
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t n_inv, umod;
+    mpd_size_t i;
+
+
+    SETMODULUS(modnum);
+    n_inv = POWMOD(n, (umod-2));
+
+    if (ispower2(n)) {
+        if (n > SIX_STEP_THRESHOLD) {
+            fnt = six_step_fnt;
+            inv_fnt = inv_six_step_fnt;
+        }
+        else {
+            fnt = std_fnt;
+            inv_fnt = std_inv_fnt;
+        }
+    }
+    else {
+        fnt = four_step_fnt;
+        inv_fnt = inv_four_step_fnt;
+    }
+
+    if (!fnt(c1, n, modnum)) {
+        return 0;
+    }
+    for (i = 0; i < n-1; i += 2) {
+        mpd_uint_t x0 = c1[i];
+        mpd_uint_t x1 = c1[i+1];
+        MULMOD2(&x0, x0, &x1, x1);
+        c1[i] = x0;
+        c1[i+1] = x1;
+    }
+
+    if (!inv_fnt(c1, n, modnum)) {
+        return 0;
+    }
+    for (i = 0; i < n-3; i += 4) {
+        mpd_uint_t x0 = c1[i];
+        mpd_uint_t x1 = c1[i+1];
+        mpd_uint_t x2 = c1[i+2];
+        mpd_uint_t x3 = c1[i+3];
+        MULMOD2C(&x0, &x1, n_inv);
+        MULMOD2C(&x2, &x3, n_inv);
+        c1[i] = x0;
+        c1[i+1] = x1;
+        c1[i+2] = x2;
+        c1[i+3] = x3;
+    }
+
+    return 1;
+}
diff --git a/libmpdec/convolute.h b/libmpdec/convolute.h
new file mode 100644 (file)
index 0000000..d75a99a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_CONVOLUTE_H_
+#define LIBMPDEC_CONVOLUTE_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+#define SIX_STEP_THRESHOLD 4096
+
+int fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum);
+int fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif  /* LIBMPDEC_CONVOLUTE_H_ */
diff --git a/libmpdec/crt.c b/libmpdec/crt.c
new file mode 100644 (file)
index 0000000..689b6ed
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+
+#include "constants.h"
+#include "crt.h"
+#include "numbertheory.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+#include "umodarith.h"
+
+
+/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */
+
+
+/* Multiply P1P2 by v, store result in w. */
+static inline void
+_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v)
+{
+    mpd_uint_t hi1, hi2, lo;
+
+    _mpd_mul_words(&hi1, &lo, LH_P1P2, v);
+    w[0] = lo;
+
+    _mpd_mul_words(&hi2, &lo, UH_P1P2, v);
+    lo = hi1 + lo;
+    if (lo < hi1) hi2++;
+
+    w[1] = lo;
+    w[2] = hi2;
+}
+
+/* Add 3 words from v to w. The result is known to fit in w. */
+static inline void
+_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3])
+{
+    mpd_uint_t carry;
+
+    w[0] = w[0] + v[0];
+    carry = (w[0] < v[0]);
+
+    w[1] = w[1] + v[1];
+    if (w[1] < v[1]) w[2]++;
+
+    w[1] = w[1] + carry;
+    if (w[1] < carry) w[2]++;
+
+    w[2] += v[2];
+}
+
+/* Divide 3 words in u by v, store result in w, return remainder. */
+static inline mpd_uint_t
+_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v)
+{
+    mpd_uint_t r1 = u[2];
+    mpd_uint_t r2;
+
+    if (r1 < v) {
+        w[2] = 0;
+    }
+    else {
+        _mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */
+    }
+
+    _mpd_div_words(&w[1], &r2, r1, u[1], v);
+    _mpd_div_words(&w[0], &r1, r2, u[0], v);
+
+    return r1;
+}
+
+
+/*
+ * Chinese Remainder Theorem:
+ * Algorithm from Joerg Arndt, "Matters Computational",
+ * Chapter 37.4.1 [http://www.jjj.de/fxt/]
+ *
+ * See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7.
+ */
+
+/*
+ * CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each
+ * triple of members of the arrays, find the unique z modulo p1*p2*p3, with
+ * zmax = p1*p2*p3 - 1.
+ *
+ * In each iteration of the loop, split z into result[i] = z % MPD_RADIX
+ * and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the
+ * maximum carry.
+ *
+ * Limits for the 32-bit build:
+ *
+ *   N    = 2**96
+ *   cmax = 7711435591312380274
+ *
+ * Limits for the 64 bit build:
+ *
+ *   N    = 2**192
+ *   cmax = 627710135393475385904124401220046371710
+ *
+ * The following statements hold for both versions:
+ *
+ *   1) cmax + zmax < N, so the addition does not overflow.
+ *
+ *   2) (cmax + zmax) / MPD_RADIX == cmax.
+ *
+ *   3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax.
+ */
+void
+crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize)
+{
+    mpd_uint_t p1 = mpd_moduli[P1];
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t a1, a2, a3;
+    mpd_uint_t s;
+    mpd_uint_t z[3], t[3];
+    mpd_uint_t carry[3] = {0,0,0};
+    mpd_uint_t hi, lo;
+    mpd_size_t i;
+
+    for (i = 0; i < rsize; i++) {
+
+        a1 = x1[i];
+        a2 = x2[i];
+        a3 = x3[i];
+
+        SETMODULUS(P2);
+        s = ext_submod(a2, a1, umod);
+        s = MULMOD(s, INV_P1_MOD_P2);
+
+        _mpd_mul_words(&hi, &lo, s, p1);
+        lo = lo + a1;
+        if (lo < a1) hi++;
+
+        SETMODULUS(P3);
+        s = dw_submod(a3, hi, lo, umod);
+        s = MULMOD(s, INV_P1P2_MOD_P3);
+
+        z[0] = lo;
+        z[1] = hi;
+        z[2] = 0;
+
+        _crt_mulP1P2_3(t, s);
+        _crt_add3(z, t);
+        _crt_add3(carry, z);
+
+        x1[i] = _crt_div3(carry, carry, MPD_RADIX);
+    }
+
+    assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0);
+}
diff --git a/libmpdec/crt.h b/libmpdec/crt.h
new file mode 100644 (file)
index 0000000..2cebff7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_CRT_H_
+#define LIBMPDEC_CRT_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+void crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_CRT_H_ */
diff --git a/libmpdec/difradix2.c b/libmpdec/difradix2.c
new file mode 100644 (file)
index 0000000..fef9551
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+
+#include "bits.h"
+#include "constants.h"
+#include "difradix2.h"
+#include "mpdecimal.h"
+#include "numbertheory.h"
+#include "umodarith.h"
+
+
+/* Bignum: The actual transform routine (decimation in frequency). */
+
+
+/*
+ * Generate index pairs (x, bitreverse(x)) and carry out the permutation.
+ * n must be a power of two.
+ * Algorithm due to Brent/Lehmann, see Joerg Arndt, "Matters Computational",
+ * Chapter 1.14.4. [http://www.jjj.de/fxt/]
+ */
+static inline void
+bitreverse_permute(mpd_uint_t a[], mpd_size_t n)
+{
+    mpd_size_t x = 0;
+    mpd_size_t r = 0;
+    mpd_uint_t t;
+
+    do { /* Invariant: r = bitreverse(x) */
+        if (r > x) {
+            t = a[x];
+            a[x] = a[r];
+            a[r] = t;
+        }
+        /* Flip trailing consecutive 1 bits and the first zero bit
+         * that absorbs a possible carry. */
+        x += 1;
+        /* Mirror the operation on r: Flip n_trailing_zeros(x)+1
+           high bits of r. */
+        r ^= (n - (n >> (mpd_bsf(x)+1)));
+        /* The loop invariant is preserved. */
+    } while (x < n);
+}
+
+
+/* Fast Number Theoretic Transform, decimation in frequency. */
+void
+fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams)
+{
+    mpd_uint_t *wtable = tparams->wtable;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t u0, u1, v0, v1;
+    mpd_uint_t w, w0, w1, wstep;
+    mpd_size_t m, mhalf;
+    mpd_size_t j, r;
+
+
+    assert(ispower2(n));
+    assert(n >= 4);
+
+    SETMODULUS(tparams->modnum);
+
+    /* m == n */
+    mhalf = n / 2;
+    for (j = 0; j < mhalf; j += 2) {
+
+        w0 = wtable[j];
+        w1 = wtable[j+1];
+
+        u0 = a[j];
+        v0 = a[j+mhalf];
+
+        u1 = a[j+1];
+        v1 = a[j+1+mhalf];
+
+        a[j] = addmod(u0, v0, umod);
+        v0 = submod(u0, v0, umod);
+
+        a[j+1] = addmod(u1, v1, umod);
+        v1 = submod(u1, v1, umod);
+
+        MULMOD2(&v0, w0, &v1, w1);
+
+        a[j+mhalf] = v0;
+        a[j+1+mhalf] = v1;
+
+    }
+
+    wstep = 2;
+    for (m = n/2; m >= 2; m>>=1, wstep<<=1) {
+
+        mhalf = m / 2;
+
+        /* j == 0 */
+        for (r = 0; r < n; r += 2*m) {
+
+            u0 = a[r];
+            v0 = a[r+mhalf];
+
+            u1 = a[m+r];
+            v1 = a[m+r+mhalf];
+
+            a[r] = addmod(u0, v0, umod);
+            v0 = submod(u0, v0, umod);
+
+            a[m+r] = addmod(u1, v1, umod);
+            v1 = submod(u1, v1, umod);
+
+            a[r+mhalf] = v0;
+            a[m+r+mhalf] = v1;
+        }
+
+        for (j = 1; j < mhalf; j++) {
+
+            w = wtable[j*wstep];
+
+            for (r = 0; r < n; r += 2*m) {
+
+                u0 = a[r+j];
+                v0 = a[r+j+mhalf];
+
+                u1 = a[m+r+j];
+                v1 = a[m+r+j+mhalf];
+
+                a[r+j] = addmod(u0, v0, umod);
+                v0 = submod(u0, v0, umod);
+
+                a[m+r+j] = addmod(u1, v1, umod);
+                v1 = submod(u1, v1, umod);
+
+                MULMOD2C(&v0, &v1, w);
+
+                a[r+j+mhalf] = v0;
+                a[m+r+j+mhalf] = v1;
+            }
+
+        }
+
+    }
+
+    bitreverse_permute(a, n);
+}
diff --git a/libmpdec/difradix2.h b/libmpdec/difradix2.h
new file mode 100644 (file)
index 0000000..6b8678c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_DIFRADIX2_H_
+#define LIBMPDEC_DIFRADIX2_H_
+
+
+#include "mpdecimal.h"
+#include "numbertheory.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+void fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_DIFRADIX2_H_ */
diff --git a/libmpdec/examples/README.txt b/libmpdec/examples/README.txt
new file mode 100644 (file)
index 0000000..646ebed
--- /dev/null
@@ -0,0 +1,7 @@
+
+This directory contains a number of examples. In order to compile, run
+(for example):
+
+   gcc -Wall -W -O2 -o powmod powmod.c -lmpdec -lm
+
+
diff --git a/libmpdec/examples/compare.c b/libmpdec/examples/compare.c
new file mode 100644 (file)
index 0000000..c852d4e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "compare: usage: ./compare x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_compare(result, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/div.c b/libmpdec/examples/div.c
new file mode 100644 (file)
index 0000000..b8391a3
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "div: usage: ./div x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_div(result, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/divmod.c b/libmpdec/examples/divmod.c
new file mode 100644 (file)
index 0000000..f98d0db
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *q, *r;
+       char *qs, *rs;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "divmod: usage: ./divmod x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       q = mpd_new(&ctx);
+       r = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_divmod(q, r, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       qs = mpd_to_sci(q, 1);
+       rs = mpd_to_sci(r, 1);
+
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s  %s\n", qs, rs, status_str);
+
+       mpd_del(q);
+       mpd_del(r);
+       mpd_del(a);
+       mpd_del(b);
+       mpd_free(qs);
+       mpd_free(rs);
+
+       return 0;
+}
diff --git a/libmpdec/examples/multiply.c b/libmpdec/examples/multiply.c
new file mode 100644 (file)
index 0000000..0ff8417
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "multiply: usage: ./multiply x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_mul(result, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/pow.c b/libmpdec/examples/pow.c
new file mode 100644 (file)
index 0000000..b2975c8
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "pow: usage: ./pow x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_pow(result, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/powmod.c b/libmpdec/examples/powmod.c
new file mode 100644 (file)
index 0000000..074320d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b, *c;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 4) {
+               fprintf(stderr, "powmod: usage: ./powmod x y z\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       c = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+       mpd_set_string(c, argv[3], &ctx);
+
+       start_clock = clock();
+       mpd_powmod(result, a, b, c, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(c);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/shift.c b/libmpdec/examples/shift.c
new file mode 100644 (file)
index 0000000..94ec1cf
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a, *b;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 3) {
+               fprintf(stderr, "shift: usage: ./shift x y\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       b = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+       mpd_set_string(b, argv[2], &ctx);
+
+       start_clock = clock();
+       mpd_shift(result, a, b, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(b);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/examples/sqrt.c b/libmpdec/examples/sqrt.c
new file mode 100644 (file)
index 0000000..d1f4dd8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <mpdecimal.h>
+
+
+int
+main(int argc, char **argv)
+{
+       mpd_context_t ctx;
+       mpd_t *a;
+       mpd_t *result;
+       char *rstring;
+       char status_str[MPD_MAX_FLAG_STRING];
+       clock_t start_clock, end_clock;
+
+       if (argc != 2) {
+               fprintf(stderr, "sqrt: usage: ./sqrt x\n");
+               exit(1);
+       }
+
+       mpd_init(&ctx, 38);
+       ctx.traps = 0;
+
+       result = mpd_new(&ctx);
+       a = mpd_new(&ctx);
+       mpd_set_string(a, argv[1], &ctx);
+
+       start_clock = clock();
+       mpd_sqrt(result, a, &ctx);
+       end_clock = clock();
+       fprintf(stderr, "time: %f\n\n",
+                  (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC);
+
+       rstring = mpd_to_sci(result, 1);
+       mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status);
+       printf("%s  %s\n", rstring, status_str);
+
+       mpd_del(a);
+       mpd_del(result);
+       mpd_free(rstring);
+
+       return 0;
+}
+
+
diff --git a/libmpdec/fnt.c b/libmpdec/fnt.c
new file mode 100644 (file)
index 0000000..1d2a751
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "bits.h"
+#include "difradix2.h"
+#include "fnt.h"
+#include "mpdecimal.h"
+#include "numbertheory.h"
+
+
+/* Bignum: Fast transform for medium-sized coefficients */
+
+
+/* forward transform, sign = -1 */
+int
+std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    struct fnt_params *tparams;
+
+    assert(ispower2(n));
+    assert(n >= 4);
+    assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+    if ((tparams = _mpd_init_fnt_params(n, -1, modnum)) == NULL) {
+        return 0;
+    }
+    fnt_dif2(a, n, tparams);
+
+    mpd_free(tparams);
+    return 1;
+}
+
+/* reverse transform, sign = 1 */
+int
+std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    struct fnt_params *tparams;
+
+    assert(ispower2(n));
+    assert(n >= 4);
+    assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+    if ((tparams = _mpd_init_fnt_params(n, 1, modnum)) == NULL) {
+        return 0;
+    }
+    fnt_dif2(a, n, tparams);
+
+    mpd_free(tparams);
+    return 1;
+}
diff --git a/libmpdec/fnt.h b/libmpdec/fnt.h
new file mode 100644 (file)
index 0000000..60f852d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_FNT_H_
+#define LIBMPDEC_FNT_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+int std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+int std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_FNT_H_ */
diff --git a/libmpdec/fourstep.c b/libmpdec/fourstep.c
new file mode 100644 (file)
index 0000000..66e7e8d
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+
+#include "constants.h"
+#include "fourstep.h"
+#include "mpdecimal.h"
+#include "numbertheory.h"
+#include "sixstep.h"
+#include "umodarith.h"
+
+
+/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the
+   form 3 * 2**n (See literature/matrix-transform.txt). */
+
+
+#ifndef PPRO
+static inline void
+std_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3,
+              mpd_uint_t w3table[3], mpd_uint_t umod)
+{
+    mpd_uint_t r1, r2;
+    mpd_uint_t w;
+    mpd_uint_t s, tmp;
+
+
+    /* k = 0 -> w = 1 */
+    s = *x1;
+    s = addmod(s, *x2, umod);
+    s = addmod(s, *x3, umod);
+
+    r1 = s;
+
+    /* k = 1 */
+    s = *x1;
+
+    w = w3table[1];
+    tmp = MULMOD(*x2, w);
+    s = addmod(s, tmp, umod);
+
+    w = w3table[2];
+    tmp = MULMOD(*x3, w);
+    s = addmod(s, tmp, umod);
+
+    r2 = s;
+
+    /* k = 2 */
+    s = *x1;
+
+    w = w3table[2];
+    tmp = MULMOD(*x2, w);
+    s = addmod(s, tmp, umod);
+
+    w = w3table[1];
+    tmp = MULMOD(*x3, w);
+    s = addmod(s, tmp, umod);
+
+    *x3 = s;
+    *x2 = r2;
+    *x1 = r1;
+}
+#else /* PPRO */
+static inline void
+ppro_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_uint_t w3table[3],
+               mpd_uint_t umod, double *dmod, uint32_t dinvmod[3])
+{
+    mpd_uint_t r1, r2;
+    mpd_uint_t w;
+    mpd_uint_t s, tmp;
+
+
+    /* k = 0 -> w = 1 */
+    s = *x1;
+    s = addmod(s, *x2, umod);
+    s = addmod(s, *x3, umod);
+
+    r1 = s;
+
+    /* k = 1 */
+    s = *x1;
+
+    w = w3table[1];
+    tmp = ppro_mulmod(*x2, w, dmod, dinvmod);
+    s = addmod(s, tmp, umod);
+
+    w = w3table[2];
+    tmp = ppro_mulmod(*x3, w, dmod, dinvmod);
+    s = addmod(s, tmp, umod);
+
+    r2 = s;
+
+    /* k = 2 */
+    s = *x1;
+
+    w = w3table[2];
+    tmp = ppro_mulmod(*x2, w, dmod, dinvmod);
+    s = addmod(s, tmp, umod);
+
+    w = w3table[1];
+    tmp = ppro_mulmod(*x3, w, dmod, dinvmod);
+    s = addmod(s, tmp, umod);
+
+    *x3 = s;
+    *x2 = r2;
+    *x1 = r1;
+}
+#endif
+
+
+/* forward transform, sign = -1; transform length = 3 * 2**n */
+int
+four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    mpd_size_t R = 3; /* number of rows */
+    mpd_size_t C = n / 3; /* number of columns */
+    mpd_uint_t w3table[3];
+    mpd_uint_t kernel, w0, w1, wstep;
+    mpd_uint_t *s, *p0, *p1, *p2;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_size_t i, k;
+
+
+    assert(n >= 48);
+    assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+
+    /* Length R transform on the columns. */
+    SETMODULUS(modnum);
+    _mpd_init_w3table(w3table, -1, modnum);
+    for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) {
+
+        SIZE3_NTT(p0, p1, p2, w3table);
+    }
+
+    /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+    kernel = _mpd_getkernel(n, -1, modnum);
+    for (i = 1; i < R; i++) {
+        w0 = 1;                  /* r**(i*0): initial value for k=0 */
+        w1 = POWMOD(kernel, i);  /* r**(i*1): initial value for k=1 */
+        wstep = MULMOD(w1, w1);  /* r**(2*i) */
+        for (k = 0; k < C-1; k += 2) {
+            mpd_uint_t x0 = a[i*C+k];
+            mpd_uint_t x1 = a[i*C+k+1];
+            MULMOD2(&x0, w0, &x1, w1);
+            MULMOD2C(&w0, &w1, wstep);  /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */
+            a[i*C+k] = x0;
+            a[i*C+k+1] = x1;
+        }
+    }
+
+    /* Length C transform on the rows. */
+    for (s = a; s < a+n; s += C) {
+        if (!six_step_fnt(s, C, modnum)) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/* backward transform, sign = 1; transform length = 3 * 2**n */
+int
+inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    mpd_size_t R = 3; /* number of rows */
+    mpd_size_t C = n / 3; /* number of columns */
+    mpd_uint_t w3table[3];
+    mpd_uint_t kernel, w0, w1, wstep;
+    mpd_uint_t *s, *p0, *p1, *p2;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_size_t i, k;
+
+
+    assert(n >= 48);
+    assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+    /* Length C transform on the rows. */
+    for (s = a; s < a+n; s += C) {
+        if (!inv_six_step_fnt(s, C, modnum)) {
+            return 0;
+        }
+    }
+
+    /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+    SETMODULUS(modnum);
+    kernel = _mpd_getkernel(n, 1, modnum);
+    for (i = 1; i < R; i++) {
+        w0 = 1;
+        w1 = POWMOD(kernel, i);
+        wstep = MULMOD(w1, w1);
+        for (k = 0; k < C; k += 2) {
+            mpd_uint_t x0 = a[i*C+k];
+            mpd_uint_t x1 = a[i*C+k+1];
+            MULMOD2(&x0, w0, &x1, w1);
+            MULMOD2C(&w0, &w1, wstep);
+            a[i*C+k] = x0;
+            a[i*C+k+1] = x1;
+        }
+    }
+
+    /* Length R transform on the columns. */
+    _mpd_init_w3table(w3table, 1, modnum);
+    for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) {
+
+        SIZE3_NTT(p0, p1, p2, w3table);
+    }
+
+    return 1;
+}
diff --git a/libmpdec/fourstep.h b/libmpdec/fourstep.h
new file mode 100644 (file)
index 0000000..9572668
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_FOURSTEP_H_
+#define LIBMPDEC_FOURSTEP_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+int four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+int inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_FOURSTEP_H_ */
diff --git a/libmpdec/io.c b/libmpdec/io.c
new file mode 100644 (file)
index 0000000..1593abe
--- /dev/null
@@ -0,0 +1,1610 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "io.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+/* This file contains functions for decimal <-> string conversions, including
+   PEP-3101 formatting for numeric types. */
+
+
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 7
+  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+  #pragma GCC diagnostic ignored "-Wmisleading-indentation"
+  #pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+
+
+/*
+ * Work around the behavior of tolower() and strcasecmp() in certain
+ * locales. For example, in tr_TR.utf8:
+ *
+ * tolower((unsigned char)'I') == 'I'
+ *
+ * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1
+ */
+static inline int
+_mpd_strneq(const char *s, const char *l, const char *u, size_t n)
+{
+    while (--n != SIZE_MAX) {
+        if (*s != *l && *s != *u) {
+            return 0;
+        }
+        s++; u++; l++;
+    }
+
+    return 1;
+}
+
+static mpd_ssize_t
+strtoexp(const char *s)
+{
+    char *end;
+    mpd_ssize_t retval;
+
+    errno = 0;
+    retval = mpd_strtossize(s, &end, 10);
+    if (errno == 0 && !(*s != '\0' && *end == '\0'))
+        errno = EINVAL;
+
+    return retval;
+}
+
+/*
+ * Scan 'len' words. The most significant word contains 'r' digits,
+ * the remaining words are full words. Skip dpoint. The string 's' must
+ * consist of digits and an optional single decimal point at 'dpoint'.
+ */
+static void
+string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r,
+                size_t len)
+{
+    int j;
+
+    if (r > 0) {
+        data[--len] = 0;
+        for (j = 0; j < r; j++, s++) {
+            if (s == dpoint) s++;
+            data[len] = 10 * data[len] + (*s - '0');
+        }
+    }
+
+    while (--len != SIZE_MAX) {
+        data[len] = 0;
+        for (j = 0; j < MPD_RDIGITS; j++, s++) {
+            if (s == dpoint) s++;
+            data[len] = 10 * data[len] + (*s - '0');
+        }
+    }
+}
+
+/*
+ * Partially verify a numeric string of the form:
+ *
+ *     [cdigits][.][cdigits][eE][+-][edigits]
+ *
+ * If successful, return a pointer to the location of the first
+ * relevant coefficient digit. This digit is either non-zero or
+ * part of one of the following patterns:
+ *
+ *     ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"]
+ *
+ * The locations of a single optional dot or indicator are stored
+ * in 'dpoint' and 'exp'.
+ *
+ * The end of the string is stored in 'end'. If an indicator [eE]
+ * occurs without trailing [edigits], the condition is caught
+ * later by strtoexp().
+ */
+static const char *
+scan_dpoint_exp(const char *s, const char **dpoint, const char **exp,
+                const char **end)
+{
+    const char *coeff = NULL;
+
+    *dpoint = NULL;
+    *exp = NULL;
+    for (; *s != '\0'; s++) {
+        switch (*s) {
+        case '.':
+            if (*dpoint != NULL || *exp != NULL)
+                return NULL;
+            *dpoint = s;
+            break;
+        case 'E': case 'e':
+            if (*exp != NULL)
+                return NULL;
+            *exp = s;
+            if (*(s+1) == '+' || *(s+1) == '-')
+                s++;
+            break;
+        default:
+            if (!isdigit((unsigned char)*s))
+                return NULL;
+            if (coeff == NULL && *exp == NULL) {
+                if (*s == '0') {
+                    if (!isdigit((unsigned char)*(s+1)))
+                        if (!(*(s+1) == '.' &&
+                              isdigit((unsigned char)*(s+2))))
+                            coeff = s;
+                }
+                else {
+                    coeff = s;
+                }
+            }
+            break;
+
+        }
+    }
+
+    *end = s;
+    return coeff;
+}
+
+/* scan the payload of a NaN */
+static const char *
+scan_payload(const char *s, const char **end)
+{
+    const char *coeff;
+
+    while (*s == '0')
+        s++;
+    coeff = s;
+
+    while (isdigit((unsigned char)*s))
+        s++;
+    *end = s;
+
+    return (*s == '\0') ? coeff : NULL;
+}
+
+/* convert a character string to a decimal */
+void
+mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx,
+                uint32_t *status)
+{
+    mpd_ssize_t q, r, len;
+    const char *coeff, *end;
+    const char *dpoint = NULL, *exp = NULL;
+    size_t digits;
+    uint8_t sign = MPD_POS;
+
+    mpd_set_flags(dec, 0);
+    dec->len = 0;
+    dec->exp = 0;
+
+    /* sign */
+    if (*s == '+') {
+        s++;
+    }
+    else if (*s == '-') {
+        mpd_set_negative(dec);
+        sign = MPD_NEG;
+        s++;
+    }
+
+    if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */
+        s += 3;
+        mpd_setspecial(dec, sign, MPD_NAN);
+        if (*s == '\0')
+            return;
+        /* validate payload: digits only */
+        if ((coeff = scan_payload(s, &end)) == NULL)
+            goto conversion_error;
+        /* payload consists entirely of zeros */
+        if (*coeff == '\0')
+            return;
+        digits = end - coeff;
+        /* prec >= 1, clamp is 0 or 1 */
+        if (digits > (size_t)(ctx->prec-ctx->clamp))
+            goto conversion_error;
+    } /* sNaN */
+    else if (_mpd_strneq(s, "snan", "SNAN", 4)) {
+        s += 4;
+        mpd_setspecial(dec, sign, MPD_SNAN);
+        if (*s == '\0')
+            return;
+        /* validate payload: digits only */
+        if ((coeff = scan_payload(s, &end)) == NULL)
+            goto conversion_error;
+        /* payload consists entirely of zeros */
+        if (*coeff == '\0')
+            return;
+        digits = end - coeff;
+        if (digits > (size_t)(ctx->prec-ctx->clamp))
+            goto conversion_error;
+    }
+    else if (_mpd_strneq(s, "inf", "INF", 3)) {
+        s += 3;
+        if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) {
+            /* numeric-value: infinity */
+            mpd_setspecial(dec, sign, MPD_INF);
+            return;
+        }
+        goto conversion_error;
+    }
+    else {
+        /* scan for start of coefficient, decimal point, indicator, end */
+        if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL)
+            goto conversion_error;
+
+        /* numeric-value: [exponent-part] */
+        if (exp) {
+            /* exponent-part */
+            end = exp; exp++;
+            dec->exp = strtoexp(exp);
+            if (errno) {
+                if (!(errno == ERANGE &&
+                     (dec->exp == MPD_SSIZE_MAX ||
+                      dec->exp == MPD_SSIZE_MIN)))
+                    goto conversion_error;
+            }
+        }
+
+        digits = end - coeff;
+        if (dpoint) {
+            size_t fracdigits = end-dpoint-1;
+            if (dpoint > coeff) digits--;
+
+            if (fracdigits > MPD_MAX_PREC) {
+                goto conversion_error;
+            }
+            if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) {
+                dec->exp = MPD_SSIZE_MIN;
+            }
+            else {
+                dec->exp -= (mpd_ssize_t)fracdigits;
+            }
+        }
+        if (digits > MPD_MAX_PREC) {
+            goto conversion_error;
+        }
+        if (dec->exp > MPD_EXP_INF) {
+            dec->exp = MPD_EXP_INF;
+        }
+        if (dec->exp == MPD_SSIZE_MIN) {
+            dec->exp = MPD_SSIZE_MIN+1;
+        }
+    }
+
+    _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS);
+
+    len = (r == 0) ? q : q+1;
+    if (len == 0) {
+        goto conversion_error; /* GCOV_NOT_REACHED */
+    }
+    if (!mpd_qresize(dec, len, status)) {
+        mpd_seterror(dec, MPD_Malloc_error, status);
+        return;
+    }
+    dec->len = len;
+
+    string_to_coeff(dec->data, coeff, dpoint, (int)r, len);
+
+    mpd_setdigits(dec);
+    mpd_qfinalize(dec, ctx, status);
+    return;
+
+conversion_error:
+    /* standard wants a positive NaN */
+    mpd_seterror(dec, MPD_Conversion_syntax, status);
+}
+
+/* convert a character string to a decimal, use a maxcontext for conversion */
+void
+mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    uint32_t workstatus = 0;
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_string(dec, s, &maxcontext, &workstatus);
+
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        /* we want exact results */
+        mpd_seterror(dec, MPD_Invalid_operation, status);
+    }
+    *status |= (workstatus&MPD_Errors);
+}
+
+/* Print word x with n decimal digits to string s. dot is either NULL
+   or the location of a decimal point. */
+#define EXTRACT_DIGIT(s, x, d, dot) \
+        if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d
+static inline char *
+word_to_string(char *s, mpd_uint_t x, int n, char *dot)
+{
+    switch(n) {
+#ifdef CONFIG_64
+    case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */
+    case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot);
+    case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot);
+    case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot);
+    case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot);
+    case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot);
+    case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot);
+    case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot);
+    case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot);
+    case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot);
+#endif
+    case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot);
+    case 9:  EXTRACT_DIGIT(s, x, 100000000UL, dot);
+    case 8:  EXTRACT_DIGIT(s, x, 10000000UL, dot);
+    case 7:  EXTRACT_DIGIT(s, x, 1000000UL, dot);
+    case 6:  EXTRACT_DIGIT(s, x, 100000UL, dot);
+    case 5:  EXTRACT_DIGIT(s, x, 10000UL, dot);
+    case 4:  EXTRACT_DIGIT(s, x, 1000UL, dot);
+    case 3:  EXTRACT_DIGIT(s, x, 100UL, dot);
+    case 2:  EXTRACT_DIGIT(s, x, 10UL, dot);
+    default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x;
+    }
+
+    *s = '\0';
+    return s;
+}
+
+/* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */
+static inline char *
+exp_to_string(char *s, mpd_ssize_t x)
+{
+    char sign = '+';
+
+    if (x < 0) {
+        sign = '-';
+        x = -x;
+    }
+    *s++ = sign;
+
+    return word_to_string(s, x, mpd_word_digits(x), NULL);
+}
+
+/* Print the coefficient of dec to string s. len(dec) > 0. */
+static inline char *
+coeff_to_string(char *s, const mpd_t *dec)
+{
+    mpd_uint_t x;
+    mpd_ssize_t i;
+
+    /* most significant word */
+    x = mpd_msword(dec);
+    s = word_to_string(s, x, mpd_word_digits(x), NULL);
+
+    /* remaining full words */
+    for (i=dec->len-2; i >= 0; --i) {
+        x = dec->data[i];
+        s = word_to_string(s, x, MPD_RDIGITS, NULL);
+    }
+
+    return s;
+}
+
+/* Print the coefficient of dec to string s. len(dec) > 0. dot is either
+   NULL or a pointer to the location of a decimal point. */
+static inline char *
+coeff_to_string_dot(char *s, char *dot, const mpd_t *dec)
+{
+    mpd_uint_t x;
+    mpd_ssize_t i;
+
+    /* most significant word */
+    x = mpd_msword(dec);
+    s = word_to_string(s, x, mpd_word_digits(x), dot);
+
+    /* remaining full words */
+    for (i=dec->len-2; i >= 0; --i) {
+        x = dec->data[i];
+        s = word_to_string(s, x, MPD_RDIGITS, dot);
+    }
+
+    return s;
+}
+
+/* Format type */
+#define MPD_FMT_LOWER       0x00000000
+#define MPD_FMT_UPPER       0x00000001
+#define MPD_FMT_TOSCI       0x00000002
+#define MPD_FMT_TOENG       0x00000004
+#define MPD_FMT_EXP         0x00000008
+#define MPD_FMT_FIXED       0x00000010
+#define MPD_FMT_PERCENT     0x00000020
+#define MPD_FMT_SIGN_SPACE  0x00000040
+#define MPD_FMT_SIGN_PLUS   0x00000080
+#define MPD_FMT_SIGN_COERCE 0x00000100
+
+/* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */
+#define MPD_DEFAULT_DOTPLACE 1
+
+/*
+ * Set *result to the string representation of a decimal. Return the length
+ * of *result, not including the terminating '\0' character.
+ *
+ * Formatting is done according to 'flags'. A return value of -1 with *result
+ * set to NULL indicates MPD_Malloc_error.
+ *
+ * 'dplace' is the default place of the decimal point. It is always set to
+ * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP.
+ */
+static mpd_ssize_t
+_mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace)
+{
+    char *decstring = NULL, *cp = NULL;
+    mpd_ssize_t ldigits;
+    mpd_ssize_t mem = 0, k;
+
+    if (mpd_isspecial(dec)) {
+
+        mem = sizeof "-Infinity%";
+        if (mpd_isnan(dec) && dec->len > 0) {
+            /* diagnostic code */
+            mem += dec->digits;
+        }
+        cp = decstring = mpd_alloc(mem, sizeof *decstring);
+        if (cp == NULL) {
+            *result = NULL;
+            return -1;
+        }
+
+        if (mpd_isnegative(dec)) {
+            *cp++ = '-';
+        }
+        else if (flags&MPD_FMT_SIGN_SPACE) {
+            *cp++ = ' ';
+        }
+        else if (flags&MPD_FMT_SIGN_PLUS) {
+            *cp++ = '+';
+        }
+
+        if (mpd_isnan(dec)) {
+            if (mpd_isqnan(dec)) {
+                strcpy(cp, "NaN");
+                cp += 3;
+            }
+            else {
+                strcpy(cp, "sNaN");
+                cp += 4;
+            }
+            if (dec->len > 0) { /* diagnostic code */
+                cp = coeff_to_string(cp, dec);
+            }
+        }
+        else if (mpd_isinfinite(dec)) {
+            strcpy(cp, "Infinity");
+            cp += 8;
+        }
+        else { /* debug */
+            abort(); /* GCOV_NOT_REACHED */
+        }
+    }
+    else {
+        assert(dec->len > 0);
+
+        /*
+         * For easier manipulation of the decimal point's location
+         * and the exponent that is finally printed, the number is
+         * rescaled to a virtual representation with exp = 0. Here
+         * ldigits denotes the number of decimal digits to the left
+         * of the decimal point and remains constant once initialized.
+         *
+         * dplace is the location of the decimal point relative to
+         * the start of the coefficient. Note that 3) always holds
+         * when dplace is shifted.
+         *
+         *   1) ldigits := dec->digits - dec->exp
+         *   2) dplace  := ldigits            (initially)
+         *   3) exp     := ldigits - dplace   (initially exp = 0)
+         *
+         *   0.00000_.____._____000000.
+         *    ^      ^    ^           ^
+         *    |      |    |           |
+         *    |      |    |           `- dplace >= digits
+         *    |      |    `- dplace in the middle of the coefficient
+         *    |      ` dplace = 1 (after the first coefficient digit)
+         *    `- dplace <= 0
+         */
+
+        ldigits = dec->digits + dec->exp;
+
+        if (flags&MPD_FMT_EXP) {
+            ;
+        }
+        else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) {
+            /* MPD_FMT_FIXED: always use fixed point notation.
+             * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range,
+             * override exponent notation. */
+            dplace = ldigits;
+        }
+        else if (flags&MPD_FMT_TOENG) {
+            if (mpd_iszero(dec)) {
+                /* If the exponent is divisible by three,
+                 * dplace = 1. Otherwise, move dplace one
+                 * or two places to the left. */
+                dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3);
+            }
+            else { /* ldigits-1 is the adjusted exponent, which
+                    * should be divisible by three. If not, move
+                    * dplace one or two places to the right. */
+                dplace += mod_mpd_ssize_t(ldigits-1, 3);
+            }
+        }
+
+        /*
+         * Basic space requirements:
+         *
+         * [-][.][coeffdigits][E][-][expdigits+1][%]['\0']
+         *
+         * If the decimal point lies outside of the coefficient digits,
+         * space is adjusted accordingly.
+         */
+        if (dplace <= 0) {
+            mem = -dplace + dec->digits + 2;
+        }
+        else if (dplace >= dec->digits) {
+            mem = dplace;
+        }
+        else {
+            mem = dec->digits;
+        }
+        mem += (MPD_EXPDIGITS+1+6);
+
+        cp = decstring = mpd_alloc(mem, sizeof *decstring);
+        if (cp == NULL) {
+            *result = NULL;
+            return -1;
+        }
+
+
+        if (mpd_isnegative(dec) && !(flags&MPD_FMT_SIGN_COERCE && mpd_iszero(dec))) {
+            *cp++ = '-';
+        }
+        else if (flags&MPD_FMT_SIGN_SPACE) {
+            *cp++ = ' ';
+        }
+        else if (flags&MPD_FMT_SIGN_PLUS) {
+            *cp++ = '+';
+        }
+
+        if (dplace <= 0) {
+            /* space: -dplace+dec->digits+2 */
+            *cp++ = '0';
+            *cp++ = '.';
+            for (k = 0; k < -dplace; k++) {
+                *cp++ = '0';
+            }
+            cp = coeff_to_string(cp, dec);
+        }
+        else if (dplace >= dec->digits) {
+            /* space: dplace */
+            cp = coeff_to_string(cp, dec);
+            for (k = 0; k < dplace-dec->digits; k++) {
+                *cp++ = '0';
+            }
+        }
+        else {
+            /* space: dec->digits+1 */
+            cp = coeff_to_string_dot(cp, cp+dplace, dec);
+        }
+
+        /*
+         * Conditions for printing an exponent:
+         *
+         *   MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace
+         *   MPD_FMT_FIXED:                never (ldigits == dplace)
+         *   MPD_FMT_EXP:                  always
+         */
+        if (ldigits != dplace || flags&MPD_FMT_EXP) {
+            /* space: expdigits+2 */
+            *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e';
+            cp = exp_to_string(cp, ldigits-dplace);
+        }
+    }
+
+    if (flags&MPD_FMT_PERCENT) {
+        *cp++ = '%';
+    }
+
+    assert(cp < decstring+mem);
+    assert(cp-decstring < MPD_SSIZE_MAX);
+
+    *cp = '\0';
+    *result = decstring;
+    return (mpd_ssize_t)(cp-decstring);
+}
+
+char *
+mpd_to_sci(const mpd_t *dec, int fmt)
+{
+    char *res;
+    int flags = MPD_FMT_TOSCI;
+
+    flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+    (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
+    return res;
+}
+
+char *
+mpd_to_eng(const mpd_t *dec, int fmt)
+{
+    char *res;
+    int flags = MPD_FMT_TOENG;
+
+    flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+    (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
+    return res;
+}
+
+mpd_ssize_t
+mpd_to_sci_size(char **res, const mpd_t *dec, int fmt)
+{
+    int flags = MPD_FMT_TOSCI;
+
+    flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+    return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
+}
+
+mpd_ssize_t
+mpd_to_eng_size(char **res, const mpd_t *dec, int fmt)
+{
+    int flags = MPD_FMT_TOENG;
+
+    flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+    return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
+}
+
+/* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2,
+   chapter 3.9: Well-formed UTF-8 byte sequences. */
+static int
+_mpd_copy_utf8(char dest[5], const char *s)
+{
+    const unsigned char *cp = (const unsigned char *)s;
+    unsigned char lb, ub;
+    int count, i;
+
+
+    if (*cp == 0) {
+        /* empty string */
+        dest[0] = '\0';
+        return 0;
+    }
+    else if (*cp <= 0x7f) {
+        /* ascii */
+        dest[0] = *cp;
+        dest[1] = '\0';
+        return 1;
+    }
+    else if (0xc2 <= *cp && *cp <= 0xdf) {
+        lb = 0x80; ub = 0xbf;
+        count = 2;
+    }
+    else if (*cp == 0xe0) {
+        lb = 0xa0; ub = 0xbf;
+        count = 3;
+    }
+    else if (*cp <= 0xec) {
+        lb = 0x80; ub = 0xbf;
+        count = 3;
+    }
+    else if (*cp == 0xed) {
+        lb = 0x80; ub = 0x9f;
+        count = 3;
+    }
+    else if (*cp <= 0xef) {
+        lb = 0x80; ub = 0xbf;
+        count = 3;
+    }
+    else if (*cp == 0xf0) {
+        lb = 0x90; ub = 0xbf;
+        count = 4;
+    }
+    else if (*cp <= 0xf3) {
+        lb = 0x80; ub = 0xbf;
+        count = 4;
+    }
+    else if (*cp == 0xf4) {
+        lb = 0x80; ub = 0x8f;
+        count = 4;
+    }
+    else {
+        /* invalid */
+        goto error;
+    }
+
+    dest[0] = (char)*cp++;
+    if (*cp < lb || ub < *cp) {
+        goto error;
+    }
+    dest[1] = (char)*cp++;
+    for (i = 2; i < count; i++) {
+        if (*cp < 0x80 || 0xbf < *cp) {
+            goto error;
+        }
+        dest[i] = (char)*cp++;
+    }
+    dest[i] = '\0';
+
+    return count;
+
+error:
+    dest[0] = '\0';
+    return -1;
+}
+
+int
+mpd_validate_lconv(mpd_spec_t *spec)
+{
+    size_t n;
+#if CHAR_MAX == SCHAR_MAX
+    const char *cp = spec->grouping;
+    while (*cp != '\0') {
+        if (*cp++ < 0) {
+            return -1;
+        }
+    }
+#endif
+    n = strlen(spec->dot);
+    if (n == 0 || n > 4) {
+        return -1;
+    }
+    if (strlen(spec->sep) > 4) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps)
+{
+    char *cp = (char *)fmt;
+    int have_align = 0, n;
+
+    /* defaults */
+    spec->min_width = 0;
+    spec->prec = -1;
+    spec->type = caps ? 'G' : 'g';
+    spec->align = '>';
+    spec->sign = '-';
+    spec->sign_coerce = 0;
+    spec->dot = "";
+    spec->sep = "";
+    spec->grouping = "";
+
+
+    /* presume that the first character is a UTF-8 fill character */
+    if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) {
+        return 0;
+    }
+
+    /* alignment directive, prefixed by a fill character */
+    if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' ||
+                *(cp+n) == '=' || *(cp+n) == '^')) {
+        cp += n;
+        spec->align = *cp++;
+        have_align = 1;
+    } /* alignment directive */
+    else {
+        /* default fill character */
+        spec->fill[0] = ' ';
+        spec->fill[1] = '\0';
+        if (*cp == '<' || *cp == '>' ||
+            *cp == '=' || *cp == '^') {
+            spec->align = *cp++;
+            have_align = 1;
+        }
+    }
+
+    /* sign formatting */
+    if (*cp == '+' || *cp == '-' || *cp == ' ') {
+        spec->sign = *cp++;
+    }
+
+    /* coerce to positive zero */
+    if (*cp == 'z') {
+        spec->sign_coerce = 1;
+        cp++;
+    }
+
+    /* zero padding */
+    if (*cp == '0') {
+        /* zero padding implies alignment, which should not be
+         * specified twice. */
+        if (have_align) {
+            return 0;
+        }
+        spec->align = 'z';
+        spec->fill[0] = *cp++;
+        spec->fill[1] = '\0';
+    }
+
+    /* minimum width */
+    if (isdigit((unsigned char)*cp)) {
+        if (*cp == '0') {
+            return 0;
+        }
+        errno = 0;
+        spec->min_width = mpd_strtossize(cp, &cp, 10);
+        if (errno == ERANGE || errno == EINVAL) {
+            return 0;
+        }
+    }
+
+    /* thousands separator */
+    if (*cp == ',') {
+        spec->dot = ".";
+        spec->sep = ",";
+        spec->grouping = "\003\003";
+        cp++;
+    }
+
+    /* fraction digits or significant digits */
+    if (*cp == '.') {
+        cp++;
+        if (!isdigit((unsigned char)*cp)) {
+            return 0;
+        }
+        errno = 0;
+        spec->prec = mpd_strtossize(cp, &cp, 10);
+        if (errno == ERANGE || errno == EINVAL) {
+            return 0;
+        }
+    }
+
+    /* type */
+    if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' ||
+        *cp == 'G' || *cp == 'g' || *cp == '%') {
+        spec->type = *cp++;
+    }
+    else if (*cp == 'N' || *cp == 'n') {
+        /* locale specific conversion */
+        struct lconv *lc;
+        /* separator has already been specified */
+        if (*spec->sep) {
+            return 0;
+        }
+        spec->type = *cp++;
+        spec->type = (spec->type == 'N') ? 'G' : 'g';
+        lc = localeconv();
+        spec->dot = lc->decimal_point;
+        spec->sep = lc->thousands_sep;
+        spec->grouping = lc->grouping;
+        if (mpd_validate_lconv(spec) < 0) {
+            return 0; /* GCOV_NOT_REACHED */
+        }
+    }
+
+    /* check correctness */
+    if (*cp != '\0') {
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * The following functions assume that spec->min_width <= MPD_MAX_PREC, which
+ * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a
+ * four-byte separator after each digit, nbytes in the following struct
+ * cannot overflow.
+ */
+
+/* Multibyte string */
+typedef struct {
+    mpd_ssize_t nbytes; /* length in bytes */
+    mpd_ssize_t nchars; /* length in chars */
+    mpd_ssize_t cur;    /* current write index */
+    char *data;
+} mpd_mbstr_t;
+
+static inline void
+_mpd_bcopy(char *dest, const char *src, mpd_ssize_t n)
+{
+    while (--n >= 0) {
+        dest[n] = src[n];
+    }
+}
+
+static inline void
+_mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
+{
+    dest->nbytes += n;
+    dest->nchars += (n > 0 ? 1 : 0);
+    dest->cur -= n;
+
+    if (dest->data != NULL) {
+        _mpd_bcopy(dest->data+dest->cur, src, n);
+    }
+}
+
+static inline void
+_mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
+{
+    dest->nbytes += n;
+    dest->nchars += n;
+    dest->cur -= n;
+
+    if (dest->data != NULL) {
+        _mpd_bcopy(dest->data+dest->cur, src, n);
+    }
+}
+
+static inline void
+_mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n)
+{
+    dest->nbytes += n;
+    dest->nchars += n;
+    dest->cur -= n;
+
+    if (dest->data != NULL) {
+        char *cp = dest->data + dest->cur;
+        while (--n >= 0) {
+            cp[n] = '0';
+        }
+    }
+}
+
+/*
+ * Copy a numeric string to dest->data, adding separators in the integer
+ * part according to spec->grouping. If leading zero padding is enabled
+ * and the result is smaller than spec->min_width, continue adding zeros
+ * and separators until the minimum width is reached.
+ *
+ * The final length of dest->data is stored in dest->nbytes. The number
+ * of UTF-8 characters is stored in dest->nchars.
+ *
+ * First run (dest->data == NULL): determine the length of the result
+ * string and store it in dest->nbytes.
+ *
+ * Second run (write to dest->data): data is written in chunks and in
+ * reverse order, starting with the rest of the numeric string.
+ */
+static void
+_mpd_add_sep_dot(mpd_mbstr_t *dest,
+                 const char *sign, /* location of optional sign */
+                 const char *src, mpd_ssize_t n_src, /* integer part and length */
+                 const char *dot, /* location of optional decimal point */
+                 const char *rest, mpd_ssize_t n_rest, /* remaining part and length */
+                 const mpd_spec_t *spec)
+{
+    mpd_ssize_t n_sep, n_sign, consume;
+    const char *g;
+    int pad = 0;
+
+    n_sign = sign ? 1 : 0;
+    n_sep = (mpd_ssize_t)strlen(spec->sep);
+    /* Initial write index: set to location of '\0' in the output string.
+     * Irrelevant for the first run. */
+    dest->cur = dest->nbytes;
+    dest->nbytes = dest->nchars = 0;
+
+    _mbstr_copy_ascii(dest, rest, n_rest);
+
+    if (dot) {
+        _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot));
+    }
+
+    g = spec->grouping;
+    consume = *g;
+    while (1) {
+        /* If the group length is 0 or CHAR_MAX or greater than the
+         * number of source bytes, consume all remaining bytes. */
+        if (*g == 0 || *g == CHAR_MAX || consume > n_src) {
+            consume = n_src;
+        }
+        n_src -= consume;
+        if (pad) {
+            _mbstr_copy_pad(dest, consume);
+        }
+        else {
+            _mbstr_copy_ascii(dest, src+n_src, consume);
+        }
+
+        if (n_src == 0) {
+            /* Either the real source of intpart digits or the virtual
+             * source of padding zeros is exhausted. */
+            if (spec->align == 'z' &&
+                dest->nchars + n_sign < spec->min_width) {
+                /* Zero padding is set and length < min_width:
+                 * Generate n_src additional characters. */
+                n_src = spec->min_width - (dest->nchars + n_sign);
+                /* Next iteration:
+                 *   case *g == 0 || *g == CHAR_MAX:
+                 *      consume all padding characters
+                 *   case consume < g*:
+                 *      fill remainder of current group
+                 *   case consume == g*
+                 *      copying is a no-op */
+                consume = *g - consume;
+                /* Switch on virtual source of zeros. */
+                pad = 1;
+                continue;
+            }
+            break;
+        }
+
+        if (n_sep > 0) {
+            /* If padding is switched on, separators are counted
+             * as padding characters. This rule does not apply if
+             * the separator would be the first character of the
+             * result string. */
+            if (pad && n_src > 1) n_src -= 1;
+            _mbstr_copy_char(dest, spec->sep, n_sep);
+        }
+
+        /* If non-NUL, use the next value for grouping. */
+        if (*g && *(g+1)) g++;
+        consume = *g;
+    }
+
+    if (sign) {
+        _mbstr_copy_ascii(dest, sign, 1);
+    }
+
+    if (dest->data) {
+        dest->data[dest->nbytes] = '\0';
+    }
+}
+
+/*
+ * Convert a numeric-string to its locale-specific appearance.
+ * The string must have one of these forms:
+ *
+ *     1) [sign] digits [exponent-part]
+ *     2) [sign] digits '.' [digits] [exponent-part]
+ *
+ * Not allowed, since _mpd_to_string() never returns this form:
+ *
+ *     3) [sign] '.' digits [exponent-part]
+ *
+ * Input: result->data := original numeric string (ASCII)
+ *        result->bytes := strlen(result->data)
+ *        result->nchars := strlen(result->data)
+ *
+ * Output: result->data := modified or original string
+ *         result->bytes := strlen(result->data)
+ *         result->nchars := number of characters (possibly UTF-8)
+ */
+static int
+_mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
+{
+    const char *sign = NULL, *intpart = NULL, *dot = NULL;
+    const char *rest, *dp;
+    char *decstring;
+    mpd_ssize_t n_int, n_rest;
+
+    /* original numeric string */
+    dp = result->data;
+
+    /* sign */
+    if (*dp == '+' || *dp == '-' || *dp == ' ') {
+        sign = dp++;
+    }
+    /* integer part */
+    assert(isdigit((unsigned char)*dp));
+    intpart = dp++;
+    while (isdigit((unsigned char)*dp)) {
+        dp++;
+    }
+    n_int = (mpd_ssize_t)(dp-intpart);
+    /* decimal point */
+    if (*dp == '.') {
+        dp++; dot = spec->dot;
+    }
+    /* rest */
+    rest = dp;
+    n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data);
+
+    if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) {
+        /* _mpd_add_sep_dot() would not change anything */
+        return 1;
+    }
+
+    /* Determine the size of the new decimal string after inserting the
+     * decimal point, optional separators and optional padding. */
+    decstring = result->data;
+    result->data = NULL;
+    _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
+                     rest, n_rest, spec);
+
+    result->data = mpd_alloc(result->nbytes+1, 1);
+    if (result->data == NULL) {
+        *status |= MPD_Malloc_error;
+        mpd_free(decstring);
+        return 0;
+    }
+
+    /* Perform actual writes. */
+    _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
+                     rest, n_rest, spec);
+
+    mpd_free(decstring);
+    return 1;
+}
+
+/* Add padding to the formatted string if necessary. */
+static int
+_mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
+{
+    if (result->nchars < spec->min_width) {
+        mpd_ssize_t add_chars, add_bytes;
+        size_t lpad = 0, rpad = 0;
+        size_t n_fill, len, i, j;
+        char align = spec->align;
+        uint8_t err = 0;
+        char *cp;
+
+        n_fill = strlen(spec->fill);
+        add_chars = (spec->min_width - result->nchars);
+        /* max value: MPD_MAX_PREC * 4 */
+        add_bytes = add_chars * (mpd_ssize_t)n_fill;
+
+        cp = result->data = mpd_realloc(result->data,
+                                        result->nbytes+add_bytes+1,
+                                        sizeof *result->data, &err);
+        if (err) {
+            *status |= MPD_Malloc_error;
+            mpd_free(result->data);
+            return 0;
+        }
+
+        if (align == 'z') {
+            align = '=';
+        }
+
+        if (align == '<') {
+            rpad = add_chars;
+        }
+        else if (align == '>' || align == '=') {
+            lpad = add_chars;
+        }
+        else { /* align == '^' */
+            lpad = add_chars/2;
+            rpad = add_chars-lpad;
+        }
+
+        len = result->nbytes;
+        if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) {
+            /* leave sign in the leading position */
+            cp++; len--;
+        }
+
+        memmove(cp+n_fill*lpad, cp, len);
+        for (i = 0; i < lpad; i++) {
+            for (j = 0; j < n_fill; j++) {
+                cp[i*n_fill+j] = spec->fill[j];
+            }
+        }
+        cp += (n_fill*lpad + len);
+        for (i = 0; i < rpad; i++) {
+            for (j = 0; j < n_fill; j++) {
+                cp[i*n_fill+j] = spec->fill[j];
+            }
+        }
+
+        result->nbytes += add_bytes;
+        result->nchars += add_chars;
+        result->data[result->nbytes] = '\0';
+    }
+
+    return 1;
+}
+
+/* Round a number to prec digits. The adjusted exponent stays the same
+   or increases by one if rounding up crosses a power of ten boundary.
+   If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation
+   is set and the result is NaN. */
+static inline void
+_mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec,
+           const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t exp = a->exp + a->digits - prec;
+
+    if (prec <= 0) {
+        mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */
+        return; /* GCOV_NOT_REACHED */
+    }
+    if (mpd_isspecial(a) || mpd_iszero(a)) {
+        mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */
+        return; /* GCOV_NOT_REACHED */
+    }
+
+    mpd_qrescale_fmt(result, a, exp, ctx, status);
+    if (result->digits > prec) {
+        mpd_qrescale_fmt(result, result, exp+1, ctx, status);
+    }
+}
+
+/*
+ * Return the string representation of an mpd_t, formatted according to 'spec'.
+ * The format specification is assumed to be valid. Memory errors are indicated
+ * as usual. This function is quiet.
+ */
+char *
+mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec,
+                 const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_uint_t dt[MPD_MINALLOC_MAX];
+    mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt};
+    mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE;
+    mpd_mbstr_t result;
+    mpd_spec_t stackspec;
+    char type = spec->type;
+    int flags = 0;
+
+
+    if (spec->min_width > MPD_MAX_PREC) {
+        *status |= MPD_Invalid_operation;
+        return NULL;
+    }
+
+    if (isupper((unsigned char)type)) {
+        type = (char)tolower((unsigned char)type);
+        flags |= MPD_FMT_UPPER;
+    }
+
+    if (spec->sign_coerce) {
+        flags |= MPD_FMT_SIGN_COERCE;
+    }
+
+    if (spec->sign == ' ') {
+        flags |= MPD_FMT_SIGN_SPACE;
+    }
+    else if (spec->sign == '+') {
+        flags |= MPD_FMT_SIGN_PLUS;
+    }
+
+    if (mpd_isspecial(dec)) {
+        if (spec->align == 'z') {
+            stackspec = *spec;
+            stackspec.fill[0] = ' ';
+            stackspec.fill[1] = '\0';
+            stackspec.align = '>';
+            spec = &stackspec;
+        }
+        if (type == '%') {
+            flags |= MPD_FMT_PERCENT;
+        }
+    }
+    else {
+        uint32_t workstatus = 0;
+        mpd_ssize_t prec;
+
+        switch (type) {
+        case 'g': flags |= MPD_FMT_TOSCI; break;
+        case 'e': flags |= MPD_FMT_EXP; break;
+        case '%': flags |= MPD_FMT_PERCENT;
+                  if (!mpd_qcopy(&tmp, dec, status)) {
+                      return NULL;
+                  }
+                  tmp.exp += 2;
+                  dec = &tmp;
+                  type = 'f'; /* fall through */
+        case 'f': flags |= MPD_FMT_FIXED; break;
+        default: abort(); /* debug: GCOV_NOT_REACHED */
+        }
+
+        if (spec->prec >= 0) {
+            if (spec->prec > MPD_MAX_PREC) {
+                *status |= MPD_Invalid_operation;
+                goto error;
+            }
+
+            switch (type) {
+            case 'g':
+                prec = (spec->prec == 0) ? 1 : spec->prec;
+                if (dec->digits > prec) {
+                    _mpd_round(&tmp, dec, prec, ctx,
+                               &workstatus);
+                    dec = &tmp;
+                }
+                break;
+            case 'e':
+                if (mpd_iszero(dec)) {
+                    dplace = 1-spec->prec;
+                }
+                else {
+                    _mpd_round(&tmp, dec, spec->prec+1, ctx,
+                               &workstatus);
+                    dec = &tmp;
+                }
+                break;
+            case 'f':
+                mpd_qrescale(&tmp, dec, -spec->prec, ctx,
+                             &workstatus);
+                dec = &tmp;
+                break;
+            }
+        }
+
+        if (type == 'f') {
+            if (mpd_iszero(dec) && dec->exp > 0) {
+                mpd_qrescale(&tmp, dec, 0, ctx, &workstatus);
+                dec = &tmp;
+            }
+        }
+
+        if (workstatus&MPD_Errors) {
+            *status |= (workstatus&MPD_Errors);
+            goto error;
+        }
+    }
+
+    /*
+     * At this point, for all scaled or non-scaled decimals:
+     *   1) 1 <= digits <= MAX_PREC+1
+     *   2) adjexp(scaled) = adjexp(orig) [+1]
+     *   3)   case 'g': MIN_ETINY <= exp <= MAX_EMAX+1
+     *        case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1
+     *        case 'f': MIN_ETINY <= exp <= MAX_EMAX+1
+     *   4) max memory alloc in _mpd_to_string:
+     *        case 'g': MAX_PREC+36
+     *        case 'e': MAX_PREC+36
+     *        case 'f': 2*MPD_MAX_PREC+30
+     */
+    result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace);
+    result.nchars = result.nbytes;
+    if (result.nbytes < 0) {
+        *status |= MPD_Malloc_error;
+        goto error;
+    }
+
+    if (*spec->dot != '\0' && !mpd_isspecial(dec)) {
+        if (result.nchars > MPD_MAX_PREC+36) {
+            /* Since a group length of one is not explicitly
+             * disallowed, ensure that it is always possible to
+             * insert a four byte separator after each digit. */
+            *status |= MPD_Invalid_operation;
+            mpd_free(result.data);
+            goto error;
+        }
+        if (!_mpd_apply_lconv(&result, spec, status)) {
+            goto error;
+        }
+    }
+
+    if (spec->min_width) {
+        if (!_mpd_add_pad(&result, spec, status)) {
+            goto error;
+        }
+    }
+
+    mpd_del(&tmp);
+    return result.data;
+
+error:
+    mpd_del(&tmp);
+    return NULL;
+}
+
+char *
+mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx,
+            uint32_t *status)
+{
+    mpd_spec_t spec;
+
+    if (!mpd_parse_fmt_str(&spec, fmt, 1)) {
+        *status |= MPD_Invalid_operation;
+        return NULL;
+    }
+
+    return mpd_qformat_spec(dec, &spec, ctx, status);
+}
+
+/*
+ * The specification has a *condition* called Invalid_operation and an
+ * IEEE *signal* called Invalid_operation. The former corresponds to
+ * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation.
+ * MPD_IEEE_Invalid_operation comprises the following conditions:
+ *
+ * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined,
+ *  MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation,
+ *  MPD_Malloc_error]
+ *
+ * In the following functions, 'flag' denotes the condition, 'signal'
+ * denotes the IEEE signal.
+ */
+
+static const char *mpd_flag_string[MPD_NUM_FLAGS] = {
+    "Clamped",
+    "Conversion_syntax",
+    "Division_by_zero",
+    "Division_impossible",
+    "Division_undefined",
+    "Fpu_error",
+    "Inexact",
+    "Invalid_context",
+    "Invalid_operation",
+    "Malloc_error",
+    "Not_implemented",
+    "Overflow",
+    "Rounded",
+    "Subnormal",
+    "Underflow",
+};
+
+static const char *mpd_signal_string[MPD_NUM_FLAGS] = {
+    "Clamped",
+    "IEEE_Invalid_operation",
+    "Division_by_zero",
+    "IEEE_Invalid_operation",
+    "IEEE_Invalid_operation",
+    "IEEE_Invalid_operation",
+    "Inexact",
+    "IEEE_Invalid_operation",
+    "IEEE_Invalid_operation",
+    "IEEE_Invalid_operation",
+    "Not_implemented",
+    "Overflow",
+    "Rounded",
+    "Subnormal",
+    "Underflow",
+};
+
+/* print conditions to buffer, separated by spaces */
+int
+mpd_snprint_flags(char *dest, int nmemb, uint32_t flags)
+{
+    char *cp;
+    int n, j;
+
+    assert(nmemb >= MPD_MAX_FLAG_STRING);
+
+    *dest = '\0'; cp = dest;
+    for (j = 0; j < MPD_NUM_FLAGS; j++) {
+        if (flags & (1U<<j)) {
+            n = snprintf(cp, nmemb, "%s ", mpd_flag_string[j]);
+            if (n < 0 || n >= nmemb) return -1;
+            cp += n; nmemb -= n;
+        }
+    }
+
+    if (cp != dest) {
+        *(--cp) = '\0';
+    }
+
+    return (int)(cp-dest);
+}
+
+/* print conditions to buffer, in list form */
+int
+mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[])
+{
+    char *cp;
+    int n, j;
+
+    assert(nmemb >= MPD_MAX_FLAG_LIST);
+    if (flag_string == NULL) {
+        flag_string = mpd_flag_string;
+    }
+
+    *dest = '[';
+    *(dest+1) = '\0';
+    cp = dest+1;
+    --nmemb;
+
+    for (j = 0; j < MPD_NUM_FLAGS; j++) {
+        if (flags & (1U<<j)) {
+            n = snprintf(cp, nmemb, "%s, ", flag_string[j]);
+            if (n < 0 || n >= nmemb) return -1;
+            cp += n; nmemb -= n;
+        }
+    }
+
+    /* erase the last ", " */
+    if (cp != dest+1) {
+        cp -= 2;
+    }
+
+    *cp++ = ']';
+    *cp = '\0';
+
+    return (int)(cp-dest); /* strlen, without NUL terminator */
+}
+
+/* print signals to buffer, in list form */
+int
+mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[])
+{
+    char *cp;
+    int n, j;
+    int ieee_invalid_done = 0;
+
+    assert(nmemb >= MPD_MAX_SIGNAL_LIST);
+    if (signal_string == NULL) {
+        signal_string = mpd_signal_string;
+    }
+
+    *dest = '[';
+    *(dest+1) = '\0';
+    cp = dest+1;
+    --nmemb;
+
+    for (j = 0; j < MPD_NUM_FLAGS; j++) {
+        uint32_t f = flags & (1U<<j);
+        if (f) {
+            if (f&MPD_IEEE_Invalid_operation) {
+                if (ieee_invalid_done) {
+                    continue;
+                }
+                ieee_invalid_done = 1;
+            }
+            n = snprintf(cp, nmemb, "%s, ", signal_string[j]);
+            if (n < 0 || n >= nmemb) return -1;
+            cp += n; nmemb -= n;
+        }
+    }
+
+    /* erase the last ", " */
+    if (cp != dest+1) {
+        cp -= 2;
+    }
+
+    *cp++ = ']';
+    *cp = '\0';
+
+    return (int)(cp-dest); /* strlen, without NUL terminator */
+}
+
+/* The following two functions are mainly intended for debugging. */
+void
+mpd_fprint(FILE *file, const mpd_t *dec)
+{
+    char *decstring;
+
+    decstring = mpd_to_sci(dec, 1);
+    if (decstring != NULL) {
+        fprintf(file, "%s\n", decstring);
+        mpd_free(decstring);
+    }
+    else {
+        fputs("mpd_fprint: output error\n", file); /* GCOV_NOT_REACHED */
+    }
+}
+
+void
+mpd_print(const mpd_t *dec)
+{
+    char *decstring;
+
+    decstring = mpd_to_sci(dec, 1);
+    if (decstring != NULL) {
+        printf("%s\n", decstring);
+        mpd_free(decstring);
+    }
+    else {
+        fputs("mpd_fprint: output error\n", stderr); /* GCOV_NOT_REACHED */
+    }
+}
diff --git a/libmpdec/io.h b/libmpdec/io.h
new file mode 100644 (file)
index 0000000..4d8c44c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_IO_H_
+#define LIBMPDEC_IO_H_
+
+
+#include <stdint.h>
+
+#include "mpdecimal.h"
+
+
+#if SIZE_MAX == MPD_SIZE_MAX
+  #define mpd_strtossize _mpd_strtossize
+#else
+#include <errno.h>
+
+static inline mpd_ssize_t
+mpd_strtossize(const char *s, char **end, int base)
+{
+    int64_t retval;
+
+    errno = 0;
+    retval = _mpd_strtossize(s, end, base);
+    if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) {
+        errno = ERANGE;
+    }
+    if (errno == ERANGE) {
+        return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX;
+    }
+
+    return (mpd_ssize_t)retval;
+}
+#endif
+
+
+#endif  /* LIBMPDEC_IO_H_ */
diff --git a/libmpdec/literature/REFERENCES.txt b/libmpdec/literature/REFERENCES.txt
new file mode 100644 (file)
index 0000000..8a6eb55
--- /dev/null
@@ -0,0 +1,55 @@
+
+
+This document contains links to the literature used in the process of
+creating the library. The list is probably not complete.
+
+
+Mike Cowlishaw: General Decimal Arithmetic Specification
+http://speleotrove.com/decimal/decarith.html
+
+
+Jean-Michel Muller: On the definition of ulp (x)
+lara.inist.fr/bitstream/2332/518/1/LIP-RR2005-09.pdf
+
+
+T. E. Hull, A. Abrham: Properly rounded variable precision square root
+http://portal.acm.org/citation.cfm?id=214413
+
+
+T. E. Hull, A. Abrham: Variable precision exponential function
+http://portal.acm.org/citation.cfm?id=6498
+
+
+T. Granlund, P. L. Montgomery: Division by Invariant Integers using Multiplication
+http://gmplib.org/~tege/divcnst-pldi94.pdf
+
+
+Roman E. Maeder: Storage allocation for the Karatsuba integer multiplication
+algorithm.  http://www.springerlink.com/content/w15058mj6v59t565/
+
+
+J. M. Pollard: The fast Fourier transform in a finite field
+http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html
+
+
+David H. Bailey: FFTs in External or Hierarchical Memory
+http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
+W. Morven Gentleman: Matrix Multiplication and Fast Fourier Transforms
+http://www.alcatel-lucent.com/bstj/vol47-1968/articles/bstj47-6-1099.pdf
+
+
+Mikko Tommila: Apfloat documentation
+http://www.apfloat.org/apfloat/2.41/apfloat.pdf
+
+
+Joerg Arndt: "Matters Computational"
+http://www.jjj.de/fxt/
+
+
+Karl Hasselstrom: Fast Division of Large Integers
+www.treskal.com/kalle/exjobb/original-report.pdf
+
+
+
diff --git a/libmpdec/literature/bignum.txt b/libmpdec/literature/bignum.txt
new file mode 100644 (file)
index 0000000..f34ff67
--- /dev/null
@@ -0,0 +1,83 @@
+
+
+Bignum support (Fast Number Theoretic Transform or FNT):
+========================================================
+
+Bignum arithmetic in libmpdec uses the scheme for fast convolution
+of integer sequences from:
+
+J. M. Pollard: The fast Fourier transform in a finite field
+http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html
+
+
+The transform in a finite field can be used for convolution in the same
+way as the Fourier Transform. The main advantages of the Number Theoretic
+Transform are that it is both exact and very memory efficient.
+
+
+Convolution in pseudo-code:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  fnt_convolute(a, b):
+    x = fnt(a)                          # forward transform of a
+    y = fnt(b)                          # forward transform of b
+    z = pairwise multiply x[i] and y[i]
+    result = inv_fnt(z)                 # backward transform of z.
+
+
+Extending the maximum transform length (Chinese Remainder Theorem):
+-------------------------------------------------------------------
+
+The maximum transform length is quite limited when using a single
+prime field. However, it is possible to use multiple primes and
+recover the result using the Chinese Remainder Theorem.
+
+
+Multiplication in pseudo-code:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  _mpd_fntmul(u, v):
+    c1 = fnt_convolute(u, v, P1)  # convolute modulo prime1
+    c2 = fnt_convolute(u, v, P2)  # convolute modulo prime2
+    c3 = fnt_convolute(u, v, P3)  # convolute modulo prime3
+    result = crt3(c1, c2, c3)     # Chinese Remainder Theorem
+
+
+Optimized transform functions:
+------------------------------
+
+There are three different fnt() functions:
+
+   std_fnt: "standard" decimation in frequency transform for array lengths
+            of 2**n. Performs well up to 1024 words.
+
+   sixstep: Cache-friendly algorithm for array lengths of 2**n. Outperforms
+            std_fnt for large arrays.
+
+   fourstep: Algorithm for array lengths of 3 * 2**n. Also cache friendly
+             in large parts.
+
+
+List of bignum-only files:
+--------------------------
+
+Functions from these files are only used in _mpd_fntmul().
+
+  umodarith.h    -> fast low level routines for unsigned modular arithmetic
+  numbertheory.c -> routines for setting up the FNT
+  difradix2.c    -> decimation in frequency transform, used as the
+                    "base case" by the following three files:
+
+      fnt.c          -> standard transform for smaller arrays
+      sixstep.c      -> transform large arrays of length 2**n
+      fourstep.c     -> transform arrays of length 3 * 2**n
+
+  convolute.c    -> do the actual fast convolution, using one of
+                    the three transform functions.
+  transpose.c    -> transpositions needed for the sixstep algorithm.
+  crt.c          -> Chinese Remainder Theorem: use information from three
+                    transforms modulo three different primes to get the
+                    final result.
+
+
+
diff --git a/libmpdec/literature/fnt.py b/libmpdec/literature/fnt.py
new file mode 100644 (file)
index 0000000..ea665e2
--- /dev/null
@@ -0,0 +1,207 @@
+#
+# Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+
+######################################################################
+#  This file lists and checks some of the constants and limits used  #
+#  in libmpdec's Number Theoretic Transform. At the end of the file  #
+#  there is an example function for the plain DFT transform.         #
+######################################################################
+
+
+#
+# Number theoretic transforms are done in subfields of F(p). P[i]
+# are the primes, D[i] = P[i] - 1 are highly composite and w[i]
+# are the respective primitive roots of F(p).
+#
+# The strategy is to convolute two coefficients modulo all three
+# primes, then use the Chinese Remainder Theorem on the three
+# result arrays to recover the result in the usual base RADIX
+# form.
+#
+
+# ======================================================================
+#                           Primitive roots
+# ======================================================================
+
+#
+# Verify primitive roots:
+#
+# For a prime field, r is a primitive root if and only if for all prime
+# factors f of p-1, r**((p-1)/f) =/= 1  (mod p).
+#
+def prod(F, E):
+    """Check that the factorization of P-1 is correct. F is the list of
+       factors of P-1, E lists the number of occurrences of each factor."""
+    x = 1
+    for y, z in zip(F, E):
+        x *= y**z
+    return x
+
+def is_primitive_root(r, p, factors, exponents):
+    """Check if r is a primitive root of F(p)."""
+    if p != prod(factors, exponents) + 1:
+        return False
+    for f in factors:
+        q, control = divmod(p-1, f)
+        if control != 0:
+            return False
+        if pow(r, q, p) == 1:
+            return False
+    return True
+
+
+# =================================================================
+#             Constants and limits for the 64-bit version
+# =================================================================
+
+RADIX = 10**19
+
+# Primes P1, P2 and P3:
+P = [2**64-2**32+1, 2**64-2**34+1, 2**64-2**40+1]
+
+# P-1, highly composite. The transform length d is variable and
+# must divide D = P-1. Since all D are divisible by 3 * 2**32,
+# transform lengths can be 2**n or 3 * 2**n (where n <= 32).
+D = [2**32 * 3    * (5 * 17 * 257 * 65537),
+     2**34 * 3**2 * (7 * 11 * 31 * 151 * 331),
+     2**40 * 3**2 * (5 * 7 * 13 * 17 * 241)]
+
+# Prime factors of P-1 and their exponents:
+F = [(2,3,5,17,257,65537), (2,3,7,11,31,151,331), (2,3,5,7,13,17,241)]
+E = [(32,1,1,1,1,1), (34,2,1,1,1,1,1), (40,2,1,1,1,1,1)]
+
+# Maximum transform length for 2**n. Above that only 3 * 2**31
+# or 3 * 2**32 are possible.
+MPD_MAXTRANSFORM_2N = 2**32
+
+
+# Limits in the terminology of Pollard's paper:
+m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array.
+M1 = M2 = RADIX-1                   # Maximum value per single word.
+L = m2 * M1 * M2
+P[0] * P[1] * P[2] > 2 * L
+
+
+# Primitive roots of F(P1), F(P2) and F(P3):
+w = [7, 10, 19]
+
+# The primitive roots are correct:
+for i in range(3):
+    if not is_primitive_root(w[i], P[i], F[i], E[i]):
+        print("FAIL")
+
+
+# =================================================================
+#             Constants and limits for the 32-bit version
+# =================================================================
+
+RADIX = 10**9
+
+# Primes P1, P2 and P3:
+P = [2113929217, 2013265921, 1811939329]
+
+# P-1, highly composite. All D = P-1 are divisible by 3 * 2**25,
+# allowing for transform lengths up to 3 * 2**25 words.
+D = [2**25 * 3**2 * 7,
+     2**27 * 3    * 5,
+     2**26 * 3**3]
+
+# Prime factors of P-1 and their exponents:
+F = [(2,3,7), (2,3,5), (2,3)]
+E = [(25,2,1), (27,1,1), (26,3)]
+
+# Maximum transform length for 2**n. Above that only 3 * 2**24 or
+# 3 * 2**25 are possible.
+MPD_MAXTRANSFORM_2N = 2**25
+
+
+# Limits in the terminology of Pollard's paper:
+m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array.
+M1 = M2 = RADIX-1                   # Maximum value per single word.
+L = m2 * M1 * M2
+P[0] * P[1] * P[2] > 2 * L
+
+
+# Primitive roots of F(P1), F(P2) and F(P3):
+w = [5, 31, 13]
+
+# The primitive roots are correct:
+for i in range(3):
+    if not is_primitive_root(w[i], P[i], F[i], E[i]):
+        print("FAIL")
+
+
+# ======================================================================
+#                 Example transform using a single prime
+# ======================================================================
+
+def ntt(lst, dir):
+    """Perform a transform on the elements of lst. len(lst) must
+       be 2**n or 3 * 2**n, where n <= 25. This is the slow DFT."""
+    p = 2113929217             # prime
+    d = len(lst)               # transform length
+    d_prime = pow(d, (p-2), p) # inverse of d
+    xi = (p-1)//d
+    w = 5                         # primitive root of F(p)
+    r = pow(w, xi, p)             # primitive root of the subfield
+    r_prime = pow(w, (p-1-xi), p) # inverse of r
+    if dir == 1:      # forward transform
+        a = lst       # input array
+        A = [0] * d   # transformed values
+        for i in range(d):
+            s = 0
+            for j in range(d):
+                s += a[j] * pow(r, i*j, p)
+            A[i] = s % p
+        return A
+    elif dir == -1: # backward transform
+        A = lst     # input array
+        a = [0] * d # transformed values
+        for j in range(d):
+            s = 0
+            for i in range(d):
+                s += A[i] * pow(r_prime, i*j, p)
+            a[j] = (d_prime * s) % p
+        return a
+
+def ntt_convolute(a, b):
+    """convolute arrays a and b."""
+    assert(len(a) == len(b))
+    x = ntt(a, 1)
+    y = ntt(b, 1)
+    for i in range(len(a)):
+        y[i] = y[i] * x[i]
+    r = ntt(y, -1)
+    return r
+
+
+# Example: Two arrays representing 21 and 81 in little-endian:
+a = [1, 2, 0, 0]
+b = [1, 8, 0, 0]
+
+assert(ntt_convolute(a, b) == [1,        10,        16,        0])
+assert(21 * 81             == (1*10**0 + 10*10**1 + 16*10**2 + 0*10**3))
diff --git a/libmpdec/literature/matrix-transform.txt b/libmpdec/literature/matrix-transform.txt
new file mode 100644 (file)
index 0000000..875322e
--- /dev/null
@@ -0,0 +1,256 @@
+
+
+(* Copyright (c) 2011-2025 Stefan Krah. All rights reserved. *)
+
+
+The Matrix Fourier Transform:
+=============================
+
+In libmpdec, the Matrix Fourier Transform [1] is called four-step transform
+after a variant that appears in [2]. The algorithm requires that the input
+array can be viewed as an R*C matrix.
+
+All operations are done modulo p. For readability, the proofs drop all
+instances of (mod p).
+
+
+Algorithm four-step (forward transform):
+----------------------------------------
+
+  a := input array
+  d := len(a) = R * C
+  p := prime
+  w := primitive root of unity of the prime field
+  r := w**((p-1)/d)
+  A := output array
+
+  1) Apply a length R FNT to each column.
+
+  2) Multiply each matrix element (addressed by j*C+m) by r**(j*m).
+
+  3) Apply a length C FNT to each row.
+
+  4) Transpose the matrix.
+
+
+Proof (forward transform):
+--------------------------
+
+  The algorithm can be derived starting from the regular definition of
+  the finite-field transform of length d:
+
+            d-1
+           ,----
+           \
+    A[k] =  |  a[l]  * r**(k * l)
+           /
+           `----
+           l = 0
+
+
+  The sum can be rearranged into the sum of the sums of columns:
+
+            C-1     R-1
+           ,----   ,----
+           \       \
+         =  |       |  a[i * C + j] * r**(k * (i * C + j))
+           /       /
+           `----   `----
+           j = 0   i = 0
+
+
+  Extracting a constant from the inner sum:
+
+            C-1           R-1
+           ,----         ,----
+           \             \
+         =  |  r**k*j  *  |  a[i * C + j] * r**(k * i * C)
+           /             /
+           `----         `----
+           j = 0         i = 0
+
+
+  Without any loss of generality, let k = n * R + m,
+  where n < C and m < R:
+
+                C-1                          R-1
+               ,----                        ,----
+               \                            \
+    A[n*R+m] =  |  r**(R*n*j) * r**(m*j)  *  |  a[i*C+j] * r**(R*C*n*i) * r**(C*m*i)
+               /                            /
+               `----                        `----
+               j = 0                        i = 0
+
+
+  Since r = w ** ((p-1) / (R*C)):
+
+     a) r**(R*C*n*i) = w**((p-1)*n*i) = 1
+
+     b) r**(C*m*i) = w**((p-1) / R) ** (m*i) = r_R ** (m*i)
+
+     c) r**(R*n*j) = w**((p-1) / C) ** (n*j) = r_C ** (n*j)
+
+     r_R := root of the subfield of length R.
+     r_C := root of the subfield of length C.
+
+
+                C-1                             R-1
+               ,----                           ,----
+               \                               \
+    A[n*R+m] =  |  r_C**(n*j) * [ r**(m*j)  *   |  a[i*C+j] * r_R**(m*i) ]
+               /                     ^         /
+               `----                 |         `----    1) transform the columns
+               j = 0                 |         i = 0
+                 ^                   |
+                 |                   `-- 2) multiply
+                 |
+                 `-- 3) transform the rows
+
+
+   Note that the entire RHS is a function of n and m and that the results
+   for each pair (n, m) are stored in Fortran order.
+
+   Let the term in square brackets be f(m, j). Step 1) and 2) precalculate
+   the term for all (m, j). After that, the original matrix is now a lookup
+   table with the mth element in the jth column at location m * C + j.
+
+   Let the complete RHS be g(m, n). Step 3) does an in-place transform of
+   length n on all rows. After that, the original matrix is now a lookup
+   table with the mth element in the nth column at location m * C + n.
+
+   But each (m, n) pair should be written to location n * R + m. Therefore,
+   step 4) transposes the result of step 3).
+
+
+
+Algorithm four-step (inverse transform):
+----------------------------------------
+
+  A  := input array
+  d  := len(A) = R * C
+  p  := prime
+  d' := d**(p-2)             # inverse of d
+  w  := primitive root of unity of the prime field
+  r  := w**((p-1)/d)         # root of the subfield
+  r' := w**((p-1) - (p-1)/d) # inverse of r
+  a  := output array
+
+  0) View the matrix as a C*R matrix.
+
+  1) Transpose the matrix, producing an R*C matrix.
+
+  2) Apply a length C FNT to each row.
+
+  3) Multiply each matrix element (addressed by i*C+n) by r**(i*n).
+
+  4) Apply a length R FNT to each column.
+
+
+Proof (inverse transform):
+--------------------------
+
+  The algorithm can be derived starting from the regular definition of
+  the finite-field inverse transform of length d:
+
+                  d-1
+                 ,----
+                 \
+    a[k] =  d' *  |  A[l]  * r' ** (k * l)
+                 /
+                 `----
+                 l = 0
+
+
+  The sum can be rearranged into the sum of the sums of columns. Note
+  that at this stage we still have a C*R matrix, so C denotes the number
+  of rows:
+
+                  R-1     C-1
+                 ,----   ,----
+                 \       \
+         =  d' *  |       |  a[j * R + i] * r' ** (k * (j * R + i))
+                 /       /
+                 `----   `----
+                 i = 0   j = 0
+
+
+  Extracting a constant from the inner sum:
+
+                  R-1                C-1
+                 ,----              ,----
+                 \                  \
+         =  d' *  |  r' ** (k*i)  *  |  a[j * R + i] * r' ** (k * j * R)
+                 /                  /
+                 `----              `----
+                 i = 0              j = 0
+
+
+  Without any loss of generality, let k = m * C + n,
+  where m < R and n < C:
+
+                     R-1                                  C-1
+                    ,----                                ,----
+                    \                                    \
+    A[m*C+n] = d' *  |  r' ** (C*m*i) *  r' ** (n*i)   *  |  a[j*R+i] * r' ** (R*C*m*j) * r' ** (R*n*j)
+                    /                                    /
+                    `----                                `----
+                    i = 0                                j = 0
+
+
+  Since r' = w**((p-1) - (p-1)/d) and d = R*C:
+
+     a) r' ** (R*C*m*j) = w**((p-1)*R*C*m*j - (p-1)*m*j) = 1
+
+     b) r' ** (C*m*i) = w**((p-1)*C - (p-1)/R) ** (m*i) = r_R' ** (m*i)
+
+     c) r' ** (R*n*j) = r_C' ** (n*j)
+
+     d) d' = d**(p-2) = (R*C) ** (p-2) = R**(p-2) * C**(p-2) = R' * C'
+
+     r_R' := inverse of the root of the subfield of length R.
+     r_C' := inverse of the root of the subfield of length C.
+     R'   := inverse of R
+     C'   := inverse of C
+
+
+                     R-1                                      C-1
+                    ,----                                    ,----  2) transform the rows of a^T
+                    \                                        \
+    A[m*C+n] = R' *  |  r_R' ** (m*i) * [ r' ** (n*i) * C' *  |  a[j*R+i] * r_C' ** (n*j) ]
+                    /                           ^            /       ^
+                    `----                       |            `----   |
+                    i = 0                       |            j = 0   |
+                      ^                         |                    `-- 1) Transpose input matrix
+                      |                         `-- 3) multiply             to address elements by
+                      |                                                     i * C + j
+                      `-- 3) transform the columns
+
+
+
+   Note that the entire RHS is a function of m and n and that the results
+   for each pair (m, n) are stored in C order.
+
+   Let the term in square brackets be f(n, i). Without step 1), the sum
+   would perform a length C transform on the columns of the input matrix.
+   This is a) inefficient and b) the results are needed in C order, so
+   step 1) exchanges rows and columns.
+
+   Step 2) and 3) precalculate f(n, i) for all (n, i). After that, the
+   original matrix is now a lookup table with the ith element in the nth
+   column at location i * C + n.
+
+   Let the complete RHS be g(m, n). Step 4) does an in-place transform of
+   length m on all columns. After that, the original matrix is now a lookup
+   table with the mth element in the nth column at location m * C + n,
+   which means that all A[k] = A[m * C + n] are in the correct order.
+
+
+--
+
+  [1] Joerg Arndt: "Matters Computational"
+      http://www.jjj.de/fxt/
+  [2] David H. Bailey: FFTs in External or Hierarchical Memory
+      http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
+
diff --git a/libmpdec/literature/mulmod-64.txt b/libmpdec/literature/mulmod-64.txt
new file mode 100644 (file)
index 0000000..b0d644d
--- /dev/null
@@ -0,0 +1,127 @@
+
+
+(* Copyright (c) 2011-2025 Stefan Krah. All rights reserved. *)
+
+
+==========================================================================
+                Calculate (a * b) % p using special primes
+==========================================================================
+
+A description of the algorithm can be found in the apfloat manual by
+Tommila [1].
+
+
+Definitions:
+------------
+
+In the whole document, "==" stands for "is congruent with".
+
+Result of a * b in terms of high/low words:
+
+   (1) hi * 2**64 + lo = a * b
+
+Special primes:
+
+   (2) p = 2**64 - z + 1, where z = 2**n
+
+Single step modular reduction:
+
+   (3) R(hi, lo) = hi * z - hi + lo
+
+
+Strategy:
+---------
+
+   a) Set (hi, lo) to the result of a * b.
+
+   b) Set (hi', lo') to the result of R(hi, lo).
+
+   c) Repeat step b) until 0 <= hi' * 2**64 + lo' < 2*p.
+
+   d) If the result is less than p, return lo'. Otherwise return lo' - p.
+
+
+The reduction step b) preserves congruence:
+-------------------------------------------
+
+    hi * 2**64 + lo == hi * z - hi + lo   (mod p)
+
+    Proof:
+    ~~~~~~
+
+       hi * 2**64 + lo = (2**64 - z + 1) * hi + z * hi - hi + lo
+
+                       = p * hi               + z * hi - hi + lo
+
+                       == z * hi - hi + lo   (mod p)
+
+
+Maximum numbers of step b):
+---------------------------
+
+# To avoid unnecessary formalism, define:
+
+def R(hi, lo, z):
+     return divmod(hi * z - hi + lo, 2**64)
+
+# For simplicity, assume hi=2**64-1, lo=2**64-1 after the
+# initial multiplication a * b. This is of course impossible
+# but certainly covers all cases.
+
+# Then, for p1:
+hi=2**64-1; lo=2**64-1; z=2**32
+p1 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z)    # First reduction
+hi, lo = R(hi, lo, z)    # Second reduction
+hi * 2**64 + lo < 2 * p1 # True
+
+# For p2:
+hi=2**64-1; lo=2**64-1; z=2**34
+p2 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z)    # First reduction
+hi, lo = R(hi, lo, z)    # Second reduction
+hi, lo = R(hi, lo, z)    # Third reduction
+hi * 2**64 + lo < 2 * p2 # True
+
+# For p3:
+hi=2**64-1; lo=2**64-1; z=2**40
+p3 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z)    # First reduction
+hi, lo = R(hi, lo, z)    # Second reduction
+hi, lo = R(hi, lo, z)    # Third reduction
+hi * 2**64 + lo < 2 * p3 # True
+
+
+Step d) preserves congruence and yields a result < p:
+-----------------------------------------------------
+
+   Case hi = 0:
+
+       Case lo < p: trivial.
+
+       Case lo >= p:
+
+          lo == lo - p   (mod p)             # result is congruent
+
+          p <= lo < 2*p  ->  0 <= lo - p < p # result is in the correct range
+
+   Case hi = 1:
+
+       p < 2**64 /\ 2**64 + lo < 2*p  ->  lo < p  # lo is always less than p
+
+       2**64 + lo == 2**64 + (lo - p)   (mod p)   # result is congruent
+
+                  = lo - p   # exactly the same value as the previous RHS
+                             # in uint64_t arithmetic.
+
+       p < 2**64 + lo < 2*p  ->  0 < 2**64 + (lo - p) < p  # correct range
+
+
+
+[1]  http://www.apfloat.org/apfloat/2.40/apfloat.pdf
+
+
+
diff --git a/libmpdec/literature/mulmod-ppro.txt b/libmpdec/literature/mulmod-ppro.txt
new file mode 100644 (file)
index 0000000..ed69a41
--- /dev/null
@@ -0,0 +1,266 @@
+
+
+(* Copyright (c) 2011-2025 Stefan Krah. All rights reserved. *)
+
+
+========================================================================
+             Calculate (a * b) % p using the 80-bit x87 FPU
+========================================================================
+
+A description of the algorithm can be found in the apfloat manual by
+Tommila [1].
+
+The proof follows an argument made by Granlund/Montgomery in [2].
+
+
+Definitions and assumptions:
+----------------------------
+
+The 80-bit extended precision format uses 64 bits for the significand:
+
+  (1) F = 64
+
+The modulus is prime and less than 2**31:
+
+  (2) 2 <= p < 2**31
+
+The factors are less than p:
+
+  (3) 0 <= a < p
+  (4) 0 <= b < p
+
+The product a * b is less than 2**62 and is thus exact in 64 bits:
+
+  (5) n = a * b
+
+The product can be represented in terms of quotient and remainder:
+
+  (6) n = q * p + r
+
+Using (3), (4) and the fact that p is prime, the remainder is always
+greater than zero:
+
+  (7) 0 <= q < p  /\  1 <= r < p
+
+
+Strategy:
+---------
+
+Precalculate the 80-bit long double inverse of p, with a maximum
+relative error of 2**(1-F):
+
+  (8) pinv = (long double)1.0 / p
+
+Calculate an estimate for q = floor(n/p). The multiplication has another
+maximum relative error of 2**(1-F):
+
+  (9) qest = n * pinv
+
+If we can show that q < qest < q+1, then trunc(qest) = q. It is then
+easy to recover the remainder r. The complete algorithm is:
+
+  a) Set the control word to 64-bit precision and truncation mode.
+
+  b) n = a * b              # Calculate exact product.
+
+  c) qest = n * pinv        # Calculate estimate for the quotient.
+
+  d) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient.
+
+  f) r = n - q * p          # Calculate remainder.
+
+
+Proof for q < qest < q+1:
+-------------------------
+
+Using the cumulative error, the error bounds for qest are:
+
+                n                       n * (1 + 2**(1-F))**2
+  (9) --------------------- <= qest <=  ---------------------
+      p * (1 + 2**(1-F))**2                       p
+
+
+  Lemma 1:
+  --------
+                       n                   q * p + r
+    (10) q < --------------------- = ---------------------
+             p * (1 + 2**(1-F))**2   p * (1 + 2**(1-F))**2
+
+
+    Proof:
+    ~~~~~~
+
+    (I)     q * p * (1 + 2**(1-F))**2 < q * p + r
+
+    (II)    q * p * 2**(2-F) + q * p * 2**(2-2*F) < r
+
+    Using (1) and (7), it is sufficient to show that:
+
+    (III)   q * p * 2**(-62) + q * p * 2**(-126) < 1 <= r
+
+    (III) can easily be verified by substituting the largest possible
+    values p = 2**31-1 and q = 2**31-2.
+
+    The critical cases occur when r = 1, n = m * p + 1. These cases
+    can be exhaustively verified with a test program.
+
+
+  Lemma 2:
+  --------
+
+           n * (1 + 2**(1-F))**2     (q * p + r) * (1 + 2**(1-F))**2
+    (11)   ---------------------  =  -------------------------------  <  q + 1
+                     p                              p
+
+    Proof:
+    ~~~~~~
+
+    (I)  (q * p + r) + (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < q * p + p
+
+    (II)  (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < p - r
+
+    Using (1) and (7), it is sufficient to show that:
+
+    (III) (q * p + r) * 2**(-62) + (q * p + r) * 2**(-126) < 1 <= p - r
+
+    (III) can easily be verified by substituting the largest possible
+    values p = 2**31-1, q = 2**31-2 and r = 2**31-2.
+
+    The critical cases occur when r = (p - 1), n = m * p - 1. These cases
+    can be exhaustively verified with a test program.
+
+
+[1]  http://www.apfloat.org/apfloat/2.40/apfloat.pdf
+[2]  http://gmplib.org/~tege/divcnst-pldi94.pdf
+     [Section 7: "Use of floating point"]
+
+
+
+(* Coq proof for (10) and (11) *)
+
+Require Import ZArith.
+Require Import QArith.
+Require Import Qpower.
+Require Import Qabs.
+Require Import Psatz.
+
+Open Scope Q_scope.
+
+
+Ltac qreduce T :=
+  rewrite <- (Qred_correct (T)); simpl (Qred (T)).
+
+Theorem Qlt_move_right :
+  forall x y z:Q, x + z < y <-> x < y - z.
+Proof.
+  intros.
+  split.
+    intros.
+    psatzl Q.
+    intros.
+    psatzl Q.
+Qed.
+
+Theorem Qlt_mult_by_z :
+  forall x y z:Q, 0 < z -> (x < y <-> x * z < y * z).
+Proof.
+  intros.
+  split.
+    intros.
+    apply Qmult_lt_compat_r. trivial. trivial.
+    intros.
+    rewrite <- (Qdiv_mult_l x z). rewrite <- (Qdiv_mult_l y z).
+    apply Qmult_lt_compat_r.
+    apply Qlt_shift_inv_l.
+    trivial. psatzl Q. trivial. psatzl Q. psatzl Q.
+Qed.
+
+Theorem Qle_mult_quad :
+  forall (a b c d:Q),
+    0 <= a -> a <= c ->
+    0 <= b -> b <= d ->
+      a * b <= c * d.
+  intros.
+  psatz Q.
+Qed.
+
+
+Theorem q_lt_qest:
+  forall (p q r:Q),
+    (0 < p) -> (p <= (2#1)^31 - 1) ->
+    (0 <= q) -> (q <= p - 1) ->
+    (1 <= r) -> (r <= p - 1) ->
+      q < (q * p + r) / (p * (1 + (2#1)^(-63))^2).
+Proof.
+  intros.
+  rewrite Qlt_mult_by_z with (z := (p * (1 + (2#1)^(-63))^2)).
+
+  unfold Qdiv.
+  rewrite <- Qmult_assoc.
+  rewrite (Qmult_comm (/ (p * (1 + (2 # 1) ^ (-63)) ^ 2)) (p * (1 + (2 # 1) ^ (-63)) ^ 2)).
+  rewrite Qmult_inv_r.
+  rewrite Qmult_1_r.
+
+  assert (q * (p * (1 + (2 # 1) ^ (-63)) ^ 2) == q * p + (q * p) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))).
+  qreduce ((1 + (2 # 1) ^ (-63)) ^ 2).
+  qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+  ring_simplify.
+  reflexivity.
+  rewrite H5.
+
+  rewrite Qplus_comm.
+  rewrite Qlt_move_right.
+  ring_simplify (q * p + r - q * p).
+  qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+
+  apply Qlt_le_trans with (y := 1).
+  rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617).
+  ring_simplify.
+
+  apply Qle_lt_trans with (y := ((2 # 1) ^ 31 - (2#1)) * ((2 # 1) ^ 31 - 1)).
+  apply Qle_mult_quad.
+  assumption. psatzl Q. psatzl Q. psatzl Q. psatzl Q. psatzl Q. assumption. psatzl Q. psatzl Q.
+Qed.
+
+Theorem qest_lt_qplus1:
+  forall (p q r:Q),
+    (0 < p) -> (p <= (2#1)^31 - 1) ->
+    (0 <= q) -> (q <= p - 1) ->
+    (1 <= r) -> (r <= p - 1) ->
+      ((q * p + r) * (1 + (2#1)^(-63))^2) / p < q + 1.
+Proof.
+  intros.
+  rewrite Qlt_mult_by_z with (z := p).
+
+  unfold Qdiv.
+  rewrite <- Qmult_assoc.
+  rewrite (Qmult_comm (/ p) p).
+  rewrite Qmult_inv_r.
+  rewrite Qmult_1_r.
+
+  assert ((q * p + r) * (1 + (2 # 1) ^ (-63)) ^ 2 == q * p + r + (q * p + r) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))).
+  qreduce ((1 + (2 # 1) ^ (-63)) ^ 2).
+  qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+  ring_simplify. reflexivity.
+  rewrite H5.
+
+  rewrite <- Qplus_assoc. rewrite <- Qplus_comm. rewrite Qlt_move_right.
+  ring_simplify ((q + 1) * p - q * p).
+
+  rewrite <- Qplus_comm. rewrite Qlt_move_right.
+
+  apply Qlt_le_trans with (y := 1).
+  qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+
+  rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617).
+  ring_simplify.
+
+  ring_simplify in H0.
+  apply Qle_lt_trans with (y := (2147483646 # 1) * (2147483647 # 1) + (2147483646 # 1)).
+
+  apply Qplus_le_compat.
+  apply Qle_mult_quad.
+  assumption. psatzl Q. auto with qarith. assumption. psatzl Q.
+  auto with qarith. auto with qarith.
+  psatzl Q. psatzl Q. assumption.
+Qed.
diff --git a/libmpdec/literature/six-step.txt b/libmpdec/literature/six-step.txt
new file mode 100644 (file)
index 0000000..a133813
--- /dev/null
@@ -0,0 +1,63 @@
+
+
+(* Copyright (c) 2011-2025 Stefan Krah. All rights reserved. *)
+
+
+The Six Step Transform:
+=======================
+
+In libmpdec, the six-step transform is the Matrix Fourier Transform (See
+matrix-transform.txt) in disguise. It is called six-step transform after
+a variant that appears in [1]. The algorithm requires that the input
+array can be viewed as an R*C matrix.
+
+
+Algorithm six-step (forward transform):
+---------------------------------------
+
+  1a) Transpose the matrix.
+
+  1b) Apply a length R FNT to each row.
+
+  1c) Transpose the matrix.
+
+  2) Multiply each matrix element (addressed by j*C+m) by r**(j*m).
+
+  3) Apply a length C FNT to each row.
+
+  4) Transpose the matrix.
+
+Note that steps 1a) - 1c) are exactly equivalent to step 1) of the Matrix
+Fourier Transform. For large R, it is faster to transpose twice and do
+a transform on the rows than to perform a column transpose directly.
+
+
+
+Algorithm six-step (inverse transform):
+---------------------------------------
+
+  0) View the matrix as a C*R matrix.
+
+  1) Transpose the matrix, producing an R*C matrix.
+
+  2) Apply a length C FNT to each row.
+
+  3) Multiply each matrix element (addressed by i*C+n) by r**(i*n).
+
+  4a) Transpose the matrix.
+
+  4b) Apply a length R FNT to each row.
+
+  4c) Transpose the matrix.
+
+Again, steps 4a) - 4c) are equivalent to step 4) of the Matrix Fourier
+Transform.
+
+
+
+--
+
+  [1] David H. Bailey: FFTs in External or Hierarchical Memory
+      http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
diff --git a/libmpdec/literature/umodarith.lisp b/libmpdec/literature/umodarith.lisp
new file mode 100644 (file)
index 0000000..0684bb2
--- /dev/null
@@ -0,0 +1,690 @@
+;
+; Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+;
+; 1. Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in the
+;    documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+
+(in-package "ACL2")
+
+(include-book "arithmetic/top-with-meta" :dir :system)
+(include-book "arithmetic-2/floor-mod/floor-mod" :dir :system)
+
+
+;; =====================================================================
+;;            Proofs for several functions in umodarith.h
+;; =====================================================================
+
+
+
+;; =====================================================================
+;;                          Helper theorems
+;; =====================================================================
+
+(defthm elim-mod-m<x<2*m
+  (implies (and (<= m x)
+                (< x (* 2 m))
+                (rationalp x) (rationalp m))
+           (equal (mod x m)
+                  (+ x (- m)))))
+
+(defthm modaux-1a
+  (implies (and (< x m) (< 0 x) (< 0 m)
+                (rationalp x) (rationalp m))
+           (equal (mod (- x) m)
+                  (+ (- x) m))))
+
+(defthm modaux-1b
+  (implies (and (< (- x) m) (< x 0) (< 0 m)
+                (rationalp x) (rationalp m))
+           (equal (mod x m)
+                  (+ x m)))
+  :hints (("Goal" :use ((:instance modaux-1a
+                                   (x (- x)))))))
+
+(defthm modaux-1c
+  (implies (and (< x m) (< 0 x) (< 0 m)
+                (rationalp x) (rationalp m))
+           (equal (mod x m)
+                  x)))
+
+(defthm modaux-2a
+  (implies (and (< 0 b) (< b m)
+                (natp x) (natp b) (natp m)
+                (< (mod (+ b x) m) b))
+           (equal (mod (+ (- m) b x) m)
+                  (+ (- m) b (mod x m)))))
+
+(defthm modaux-2b
+  (implies (and (< 0 b) (< b m)
+                (natp x) (natp b) (natp m)
+                (< (mod (+ b x) m) b))
+           (equal (mod (+ b x) m)
+                  (+ (- m) b (mod x m))))
+  :hints (("Goal" :use (modaux-2a))))
+
+(defthm linear-mod-1
+  (implies (and (< x m) (< b m)
+                (natp x) (natp b)
+                (rationalp m))
+         (equal (< x (mod (+ (- b) x) m))
+                (< x b)))
+  :hints (("Goal" :use ((:instance modaux-1a
+                                   (x (+ b (- x))))))))
+
+(defthm linear-mod-2
+  (implies (and (< 0 b) (< b m)
+                (natp x) (natp b)
+                (natp m))
+           (equal (< (mod x m)
+                     (mod (+ (- b) x) m))
+                  (< (mod x m) b))))
+
+(defthm linear-mod-3
+  (implies (and (< x m) (< b m)
+                (natp x) (natp b)
+                (rationalp m))
+           (equal (<= b (mod (+ b x) m))
+                  (< (+ b x) m)))
+  :hints (("Goal" :use ((:instance elim-mod-m<x<2*m
+                                   (x (+ b x)))))))
+
+(defthm modaux-2c
+  (implies (and (< 0 b) (< b m)
+                (natp x) (natp b) (natp m)
+                (<= b (mod (+ b x) m)))
+           (equal (mod (+ b x) m)
+                  (+ b (mod x m))))
+  :hints (("Subgoal *1/8''" :use (linear-mod-3))))
+
+(defthmd modaux-2d
+  (implies (and (< x m) (< 0 x) (< 0 m)
+                (< (- m) b) (< b 0) (rationalp m)
+                (<= x (mod (+ b x) m))
+                (rationalp x) (rationalp b))
+           (equal (+ (- m) (mod (+ b x) m))
+                  (+ b x)))
+  :hints (("Goal" :cases ((<= 0 (+ b x))))
+          ("Subgoal 2'" :use ((:instance modaux-1b
+                                        (x (+ b x)))))))
+
+(defthm mod-m-b
+  (implies (and (< 0 x) (< 0 b) (< 0 m)
+                (< x b) (< b m)
+                (natp x) (natp b) (natp m))
+           (equal (mod (+ (mod (- x) m) b) m)
+                  (mod (- x) b))))
+
+
+;; =====================================================================
+;;                          addmod, submod
+;; =====================================================================
+
+(defun addmod (a b m base)
+  (let* ((s (mod (+ a b) base))
+         (s (if (< s a) (mod (- s m) base) s))
+         (s (if (>= s m) (mod (- s m) base) s)))
+        s))
+
+(defthmd addmod-correct
+  (implies (and (< 0 m) (< m base)
+                (< a m) (<= b m)
+                (natp m) (natp base)
+                (natp a) (natp b))
+           (equal (addmod a b m base)
+                  (mod (+ a b) m)))
+  :hints (("Goal" :cases ((<= base (+ a b))))
+          ("Subgoal 2.1'" :use ((:instance elim-mod-m<x<2*m
+                                           (x (+ a b)))))))
+
+(defun submod (a b m base)
+  (let* ((d (mod (- a b) base))
+         (d (if (< a d) (mod (+ d m) base) d)))
+        d))
+
+(defthmd submod-aux1
+  (implies (and (< a (mod (+ a (- b)) base))
+                (< 0 base) (< a base) (<= b base)
+                (natp base) (natp a) (natp b))
+           (< a b))
+  :rule-classes :forward-chaining)
+
+(defthmd submod-aux2
+  (implies (and (<= (mod (+ a (- b)) base) a)
+                (< 0 base) (< a base) (< b base)
+                (natp base) (natp a) (natp b))
+           (<= b a))
+  :rule-classes :forward-chaining)
+
+(defthmd submod-correct
+  (implies (and (< 0 m) (< m base)
+                (< a m) (<= b m)
+                (natp m) (natp base)
+                (natp a) (natp b))
+           (equal (submod a b m base)
+                  (mod (- a b) m)))
+  :hints (("Goal" :cases ((<= base (+ a b))))
+          ("Subgoal 2.2" :use ((:instance submod-aux1)))
+          ("Subgoal 2.2'''" :cases ((and (< 0 (+ a (- b) m))
+                                         (< (+ a (- b) m) m))))
+          ("Subgoal 2.1" :use ((:instance submod-aux2)))
+          ("Subgoal 1.2" :use ((:instance submod-aux1)))
+          ("Subgoal 1.1" :use ((:instance submod-aux2)))))
+
+
+(defun submod-2 (a b m base)
+  (let* ((d (mod (- a b) base))
+         (d (if (< a b) (mod (+ d m) base) d)))
+        d))
+
+(defthm submod-2-correct
+  (implies (and (< 0 m) (< m base)
+                (< a m) (<= b m)
+                (natp m) (natp base)
+                (natp a) (natp b))
+           (equal (submod-2 a b m base)
+                  (mod (- a b) m)))
+  :hints (("Subgoal 2'" :cases ((and (< 0 (+ a (- b) m))
+                                     (< (+ a (- b) m) m))))))
+
+
+;; =========================================================================
+;;                         ext-submod is correct
+;; =========================================================================
+
+; a < 2*m, b < 2*m
+(defun ext-submod (a b m base)
+  (let* ((a (if (>= a m) (- a m) a))
+         (b (if (>= b m) (- b m) b))
+         (d (mod (- a b) base))
+         (d (if (< a b) (mod (+ d m) base) d)))
+        d))
+
+; a < 2*m, b < 2*m
+(defun ext-submod-2 (a b m base)
+  (let* ((a (mod a m))
+         (b (mod b m))
+         (d (mod (- a b) base))
+         (d (if (< a b) (mod (+ d m) base) d)))
+        d))
+
+(defthmd ext-submod-ext-submod-2-equal
+  (implies (and (< 0 m) (< m base)
+                (< a (* 2 m)) (< b (* 2 m))
+                (natp m) (natp base)
+                (natp a) (natp b))
+           (equal (ext-submod a b m base)
+                  (ext-submod-2 a b m base))))
+
+(defthmd ext-submod-2-correct
+  (implies (and (< 0 m) (< m base)
+                (< a (* 2 m)) (< b (* 2 m))
+                (natp m) (natp base)
+                (natp a) (natp b))
+           (equal (ext-submod-2 a b m base)
+                  (mod (- a b) m))))
+
+
+;; =========================================================================
+;;                            dw-reduce is correct
+;; =========================================================================
+
+(defun dw-reduce (hi lo m base)
+  (let* ((r1 (mod hi m))
+         (r2 (mod (+ (* r1 base) lo) m)))
+        r2))
+
+(defthmd dw-reduce-correct
+  (implies (and (< 0 m) (< m base)
+                (< hi base) (< lo base)
+                (natp m) (natp base)
+                (natp hi) (natp lo))
+           (equal (dw-reduce hi lo m base)
+                  (mod (+ (* hi base) lo) m))))
+
+(defthmd <=-multiply-both-sides-by-z
+  (implies (and (rationalp x) (rationalp y)
+                (< 0 z) (rationalp z))
+           (equal (<= x y)
+                  (<= (* z x) (* z y)))))
+
+(defthmd dw-reduce-aux1
+  (implies (and (< 0 m) (< m base)
+                (natp m) (natp base)
+                (< lo base) (natp lo)
+                (< x m) (natp x))
+           (< (+ lo (* base x)) (* base m)))
+  :hints (("Goal" :cases ((<= (+ x 1) m)))
+          ("Subgoal 1''" :cases ((<= (* base (+ x 1)) (* base m))))
+          ("subgoal 1.2" :use ((:instance <=-multiply-both-sides-by-z
+                                          (x (+ 1 x))
+                                          (y m)
+                                          (z base))))))
+
+(defthm dw-reduce-aux2
+  (implies (and (< x (* base m))
+                (< 0 m) (< m base)
+                (natp m) (natp base) (natp x))
+           (< (floor x m) base)))
+
+;; This is the necessary condition for using _mpd_div_words().
+(defthmd dw-reduce-second-quotient-fits-in-single-word
+  (implies (and (< 0 m) (< m base)
+                (< hi base) (< lo base)
+                (natp m) (natp base)
+                (natp hi) (natp lo)
+                (equal r1 (mod hi m)))
+           (< (floor (+ (* r1 base) lo) m)
+              base))
+  :hints (("Goal" :cases ((< r1 m)))
+          ("Subgoal 1''" :cases ((< (+ lo (* base (mod hi m))) (* base m))))
+          ("Subgoal 1.2" :use ((:instance dw-reduce-aux1
+                                          (x (mod hi m)))))))
+
+
+;; =========================================================================
+;;                            dw-submod is correct
+;; =========================================================================
+
+(defun dw-submod (a hi lo m base)
+  (let* ((r (dw-reduce hi lo m base))
+         (d (mod (- a r) base))
+         (d (if (< a r) (mod (+ d m) base) d)))
+        d))
+
+(defthmd dw-submod-aux1
+  (implies (and (natp a) (< 0 m) (natp m)
+                (natp x) (equal r (mod x m)))
+           (equal (mod (- a x) m)
+                  (mod (- a r) m))))
+
+(defthmd dw-submod-correct
+  (implies (and (< 0 m) (< m base)
+                (natp a) (< a m)
+                (< hi base) (< lo base)
+                (natp m) (natp base)
+                (natp hi) (natp lo))
+           (equal (dw-submod a hi lo m base)
+                  (mod (- a (+ (* base hi) lo)) m)))
+  :hints (("Goal" :in-theory (disable dw-reduce)
+                  :use ((:instance dw-submod-aux1
+                                   (x (+ lo (* base hi)))
+                                   (r (dw-reduce hi lo m base)))
+                        (:instance dw-reduce-correct)))))
+
+
+;; =========================================================================
+;;                      ANSI C arithmetic for uint64_t
+;; =========================================================================
+
+(defun add (a b)
+  (mod (+ a b)
+       (expt 2 64)))
+
+(defun sub (a b)
+  (mod (- a b)
+       (expt 2 64)))
+
+(defun << (w n)
+  (mod (* w (expt 2 n))
+       (expt 2 64)))
+
+(defun >> (w n)
+  (floor w (expt 2 n)))
+
+;; join upper and lower half of a double word, yielding a 128 bit number
+(defun join (hi lo)
+  (+ (* (expt 2 64) hi) lo))
+
+
+;; =============================================================================
+;;                           Fast modular reduction
+;; =============================================================================
+
+;; These are the three primes used in the Number Theoretic Transform.
+;; A fast modular reduction scheme exists for all of them.
+(defmacro p1 ()
+  (+ (expt 2 64) (- (expt 2 32)) 1))
+
+(defmacro p2 ()
+  (+ (expt 2 64) (- (expt 2 34)) 1))
+
+(defmacro p3 ()
+  (+ (expt 2 64) (- (expt 2 40)) 1))
+
+
+;; reduce the double word number hi*2**64 + lo (mod p1)
+(defun simple-mod-reduce-p1 (hi lo)
+  (+ (* (expt 2 32) hi) (- hi) lo))
+
+;; reduce the double word number hi*2**64 + lo (mod p2)
+(defun simple-mod-reduce-p2 (hi lo)
+  (+ (* (expt 2 34) hi) (- hi) lo))
+
+;; reduce the double word number hi*2**64 + lo (mod p3)
+(defun simple-mod-reduce-p3 (hi lo)
+  (+ (* (expt 2 40) hi) (- hi) lo))
+
+
+; ----------------------------------------------------------
+;      The modular reductions given above are correct
+; ----------------------------------------------------------
+
+(defthmd congruence-p1-aux
+  (equal (* (expt 2 64) hi)
+         (+ (* (p1) hi)
+            (* (expt 2 32) hi)
+            (- hi))))
+
+(defthmd congruence-p2-aux
+  (equal (* (expt 2 64) hi)
+         (+ (* (p2) hi)
+            (* (expt 2 34) hi)
+            (- hi))))
+
+(defthmd congruence-p3-aux
+  (equal (* (expt 2 64) hi)
+         (+ (* (p3) hi)
+            (* (expt 2 40) hi)
+            (- hi))))
+
+(defthmd mod-augment
+  (implies (and (rationalp x)
+                (rationalp y)
+                (rationalp m))
+           (equal (mod (+ x y) m)
+                  (mod (+ x (mod y m)) m))))
+
+(defthmd simple-mod-reduce-p1-congruent
+  (implies (and (integerp hi)
+                (integerp lo))
+           (equal (mod (simple-mod-reduce-p1 hi lo) (p1))
+                  (mod (join hi lo) (p1))))
+  :hints (("Goal''" :use ((:instance congruence-p1-aux)
+                          (:instance mod-augment
+                                     (m (p1))
+                                     (x (+ (- hi) lo (* (expt 2 32) hi)))
+                                     (y (* (p1) hi)))))))
+
+(defthmd simple-mod-reduce-p2-congruent
+  (implies (and (integerp hi)
+                (integerp lo))
+           (equal (mod (simple-mod-reduce-p2 hi lo) (p2))
+                  (mod (join hi lo) (p2))))
+  :hints (("Goal''" :use ((:instance congruence-p2-aux)
+                          (:instance mod-augment
+                                     (m (p2))
+                                     (x (+ (- hi) lo (* (expt 2 34) hi)))
+                                     (y (* (p2) hi)))))))
+
+(defthmd simple-mod-reduce-p3-congruent
+  (implies (and (integerp hi)
+                (integerp lo))
+           (equal (mod (simple-mod-reduce-p3 hi lo) (p3))
+                  (mod (join hi lo) (p3))))
+  :hints (("Goal''" :use ((:instance congruence-p3-aux)
+                          (:instance mod-augment
+                                     (m (p3))
+                                     (x (+ (- hi) lo (* (expt 2 40) hi)))
+                                     (y (* (p3) hi)))))))
+
+
+; ---------------------------------------------------------------------
+;  We need a number less than 2*p, so that we can use the trick from
+;  elim-mod-m<x<2*m for the final reduction.
+;  For p1, two modular reductions are sufficient, for p2 and p3 three.
+; ---------------------------------------------------------------------
+
+;; p1: the first reduction is less than 2**96
+(defthmd simple-mod-reduce-p1-<-2**96
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p1 hi lo)
+              (expt 2 96))))
+
+;; p1: the second reduction is less than 2*p1
+(defthmd simple-mod-reduce-p1-<-2*p1
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (< (join hi lo) (expt 2 96))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p1 hi lo)
+              (* 2 (p1)))))
+
+
+;; p2: the first reduction is less than 2**98
+(defthmd simple-mod-reduce-p2-<-2**98
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p2 hi lo)
+              (expt 2 98))))
+
+;; p2: the second reduction is less than 2**69
+(defthmd simple-mod-reduce-p2-<-2*69
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (< (join hi lo) (expt 2 98))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p2 hi lo)
+              (expt 2 69))))
+
+;; p3: the third reduction is less than 2*p2
+(defthmd simple-mod-reduce-p2-<-2*p2
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (< (join hi lo) (expt 2 69))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p2 hi lo)
+              (* 2 (p2)))))
+
+
+;; p3: the first reduction is less than 2**104
+(defthmd simple-mod-reduce-p3-<-2**104
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p3 hi lo)
+              (expt 2 104))))
+
+;; p3: the second reduction is less than 2**81
+(defthmd simple-mod-reduce-p3-<-2**81
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (< (join hi lo) (expt 2 104))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p3 hi lo)
+              (expt 2 81))))
+
+;; p3: the third reduction is less than 2*p3
+(defthmd simple-mod-reduce-p3-<-2*p3
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (< (join hi lo) (expt 2 81))
+                (natp hi) (natp lo))
+           (< (simple-mod-reduce-p3 hi lo)
+              (* 2 (p3)))))
+
+
+; -------------------------------------------------------------------------
+;      The simple modular reductions, adapted for compiler friendly C
+; -------------------------------------------------------------------------
+
+(defun mod-reduce-p1 (hi lo)
+  (let* ((y hi)
+         (x y)
+         (hi (>> hi 32))
+         (x (sub lo x))
+         (hi (if (> x lo) (+ hi -1) hi))
+         (y (<< y 32))
+         (lo (add y x))
+         (hi (if (< lo y) (+ hi 1) hi)))
+        (+ (* hi (expt 2 64)) lo)))
+
+(defun mod-reduce-p2 (hi lo)
+  (let* ((y hi)
+         (x y)
+         (hi (>> hi 30))
+         (x (sub lo x))
+         (hi (if (> x lo) (+ hi -1) hi))
+         (y (<< y 34))
+         (lo (add y x))
+         (hi (if (< lo y) (+ hi 1) hi)))
+        (+ (* hi (expt 2 64)) lo)))
+
+(defun mod-reduce-p3 (hi lo)
+  (let* ((y hi)
+         (x y)
+         (hi (>> hi 24))
+         (x (sub lo x))
+         (hi (if (> x lo) (+ hi -1) hi))
+         (y (<< y 40))
+         (lo (add y x))
+         (hi (if (< lo y) (+ hi 1) hi)))
+        (+ (* hi (expt 2 64)) lo)))
+
+
+; -------------------------------------------------------------------------
+;     The compiler friendly versions are equal to the simple versions
+; -------------------------------------------------------------------------
+
+(defthm mod-reduce-aux1
+  (implies (and (<= 0 a) (natp a) (natp m)
+                (< (- m) b) (<= b 0)
+                (integerp b)
+                (< (mod (+ b a) m)
+                   (mod a m)))
+           (equal (mod (+ b a) m)
+                  (+ b (mod a m))))
+  :hints (("Subgoal 2" :use ((:instance modaux-1b
+                                        (x (+ a b)))))))
+
+(defthm mod-reduce-aux2
+  (implies (and (<= 0 a) (natp a) (natp m)
+                (< b m) (natp b)
+                (< (mod (+ b a) m)
+                   (mod a m)))
+           (equal (+ m (mod (+ b a) m))
+                  (+ b (mod a m)))))
+
+
+(defthm mod-reduce-aux3
+  (implies (and (< 0 a) (natp a) (natp m)
+                (< (- m) b) (< b 0)
+                (integerp b)
+                (<= (mod a m)
+                    (mod (+ b a) m)))
+           (equal (+ (- m) (mod (+ b a) m))
+                  (+ b (mod a m))))
+  :hints (("Subgoal 1.2'" :use ((:instance modaux-1b
+                                           (x b))))
+          ("Subgoal 1''" :use ((:instance modaux-2d
+                                          (x I))))))
+
+
+(defthm mod-reduce-aux4
+  (implies (and (< 0 a) (natp a) (natp m)
+                (< b m) (natp b)
+                (<= (mod a m)
+                    (mod (+ b a) m)))
+           (equal (mod (+ b a) m)
+                  (+ b (mod a m)))))
+
+
+(defthm mod-reduce-p1==simple-mod-reduce-p1
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (equal (mod-reduce-p1 hi lo)
+                  (simple-mod-reduce-p1 hi lo)))
+  :hints (("Goal" :in-theory (disable expt)
+                  :cases ((< 0 hi)))
+          ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 32) hi)))))
+          ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 32) hi)))))
+          ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 32) hi)))))
+          ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 32) hi)))))))
+
+
+(defthm mod-reduce-p2==simple-mod-reduce-p2
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (equal (mod-reduce-p2 hi lo)
+                  (simple-mod-reduce-p2 hi lo)))
+  :hints (("Goal" :cases ((< 0 hi)))
+          ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 34) hi)))))
+          ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 34) hi)))))
+          ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 34) hi)))))
+          ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 34) hi)))))))
+
+
+(defthm mod-reduce-p3==simple-mod-reduce-p3
+  (implies (and (< hi (expt 2 64))
+                (< lo (expt 2 64))
+                (natp hi) (natp lo))
+           (equal (mod-reduce-p3 hi lo)
+                  (simple-mod-reduce-p3 hi lo)))
+  :hints (("Goal" :cases ((< 0 hi)))
+          ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 40) hi)))))
+          ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 40) hi)))))
+          ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 40) hi)))))
+          ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+                                             (m (expt 2 64))
+                                             (b (+ (- HI) LO))
+                                             (a (* (expt 2 40) hi)))))))
+
+
+
diff --git a/libmpdec/mpalloc.c b/libmpdec/mpalloc.c
new file mode 100644 (file)
index 0000000..2d63dab
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mpalloc.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+#if defined(_MSC_VER)
+  #pragma warning(disable : 4232)
+#endif
+
+
+/* Guaranteed minimum allocation for a coefficient. May be changed once
+   at program start using mpd_setminalloc(). */
+mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;
+
+/* Custom allocation and free functions */
+void *(* mpd_mallocfunc)(size_t size) = malloc;
+void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;
+void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;
+void (* mpd_free)(void *ptr) = free;
+
+
+/* emulate calloc if it is not available */
+void *
+mpd_callocfunc_em(size_t nmemb, size_t size)
+{
+    void *ptr;
+    size_t req;
+    mpd_size_t overflow;
+
+    req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
+                              &overflow);
+    if (overflow) {
+        return NULL;
+    }
+
+    ptr = mpd_mallocfunc(req);
+    if (ptr == NULL) {
+        return NULL;
+    }
+    /* used on uint32_t or uint64_t */
+    memset(ptr, 0, req);
+
+    return ptr;
+}
+
+
+/* malloc with overflow checking */
+void *
+mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
+{
+    mpd_size_t req, overflow;
+
+    req = mul_size_t_overflow(nmemb, size, &overflow);
+    if (overflow) {
+        return NULL;
+    }
+
+    return mpd_mallocfunc(req);
+}
+
+/* calloc with overflow checking */
+void *
+mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
+{
+    mpd_size_t overflow;
+
+    (void)mul_size_t_overflow(nmemb, size, &overflow);
+    if (overflow) {
+        return NULL;
+    }
+
+    return mpd_callocfunc(nmemb, size);
+}
+
+/* realloc with overflow checking */
+void *
+mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
+{
+    void *new;
+    mpd_size_t req, overflow;
+
+    req = mul_size_t_overflow(nmemb, size, &overflow);
+    if (overflow) {
+        *err = 1;
+        return ptr;
+    }
+
+    new = mpd_reallocfunc(ptr, req);
+    if (new == NULL) {
+        *err = 1;
+        return ptr;
+    }
+
+    return new;
+}
+
+/* struct hack malloc with overflow checking */
+void *
+mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
+{
+    mpd_size_t req, overflow;
+
+    req = mul_size_t_overflow(nmemb, size, &overflow);
+    if (overflow) {
+        return NULL;
+    }
+
+    req = add_size_t_overflow(req, struct_size, &overflow);
+    if (overflow) {
+        return NULL;
+    }
+
+    return mpd_mallocfunc(req);
+}
+
+
+/* Allocate a new decimal with a coefficient of length 'nwords'. In case
+   of an error the return value is NULL. */
+mpd_t *
+mpd_qnew_size(mpd_ssize_t nwords)
+{
+    mpd_t *result;
+
+    nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
+
+    result = mpd_alloc(1, sizeof *result);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    result->data = mpd_alloc(nwords, sizeof *result->data);
+    if (result->data == NULL) {
+        mpd_free(result);
+        return NULL;
+    }
+
+    result->flags = 0;
+    result->exp = 0;
+    result->digits = 0;
+    result->len = 0;
+    result->alloc = nwords;
+
+    return result;
+}
+
+/* Allocate a new decimal with a coefficient of length MPD_MINALLOC.
+   In case of an error the return value is NULL. */
+mpd_t *
+mpd_qnew(void)
+{
+    return mpd_qnew_size(MPD_MINALLOC);
+}
+
+/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
+   Raises on error. */
+mpd_t *
+mpd_new(mpd_context_t *ctx)
+{
+    mpd_t *result;
+
+    result = mpd_qnew();
+    if (result == NULL) {
+        mpd_addstatus_raise(ctx, MPD_Malloc_error);
+    }
+    return result;
+}
+
+/*
+ * Input: 'result' is a static mpd_t with a static coefficient.
+ * Assumption: 'nwords' >= result->alloc.
+ *
+ * Resize the static coefficient to a larger dynamic one and copy the
+ * existing data. If successful, the value of 'result' is unchanged.
+ * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.
+ */
+int
+mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+    mpd_uint_t *p = result->data;
+
+    assert(nwords >= result->alloc);
+
+    result->data = mpd_alloc(nwords, sizeof *result->data);
+    if (result->data == NULL) {
+        result->data = p;
+        mpd_set_qnan(result);
+        mpd_set_positive(result);
+        result->exp = result->digits = result->len = 0;
+        *status |= MPD_Malloc_error;
+        return 0;
+    }
+
+    memcpy(result->data, p, result->alloc * (sizeof *result->data));
+    result->alloc = nwords;
+    mpd_set_dynamic_data(result);
+    return 1;
+}
+
+/*
+ * Input: 'result' is a static mpd_t with a static coefficient.
+ *
+ * Convert the coefficient to a dynamic one that is initialized to zero. If
+ * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.
+ */
+int
+mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+    mpd_uint_t *p = result->data;
+
+    result->data = mpd_calloc(nwords, sizeof *result->data);
+    if (result->data == NULL) {
+        result->data = p;
+        mpd_set_qnan(result);
+        mpd_set_positive(result);
+        result->exp = result->digits = result->len = 0;
+        *status |= MPD_Malloc_error;
+        return 0;
+    }
+
+    result->alloc = nwords;
+    mpd_set_dynamic_data(result);
+
+    return 1;
+}
+
+/*
+ * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
+ * Resize the coefficient to length 'nwords':
+ *   Case nwords > result->alloc:
+ *     If realloc is successful:
+ *       'result' has a larger coefficient but the same value. Return 1.
+ *     Otherwise:
+ *       Set 'result' to NaN, update status with MPD_Malloc_error and return 0.
+ *   Case nwords < result->alloc:
+ *     If realloc is successful:
+ *       'result' has a smaller coefficient. result->len is undefined. Return 1.
+ *     Otherwise (unlikely):
+ *       'result' is unchanged. Reuse the now oversized coefficient. Return 1.
+ */
+int
+mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+    uint8_t err = 0;
+
+    result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
+    if (!err) {
+        result->alloc = nwords;
+    }
+    else if (nwords > result->alloc) {
+        mpd_set_qnan(result);
+        mpd_set_positive(result);
+        result->exp = result->digits = result->len = 0;
+        *status |= MPD_Malloc_error;
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * Input: 'result' is a static mpd_t with a static coefficient.
+ * Assumption: 'nwords' >= result->alloc.
+ *
+ * Resize the static coefficient to a larger dynamic one and copy the
+ * existing data.
+ *
+ * On failure the value of 'result' is unchanged.
+ */
+int
+mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
+{
+    assert(nwords >= result->alloc);
+
+    mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data);
+    if (data == NULL) {
+        return 0;
+    }
+
+    memcpy(data, result->data, result->alloc * (sizeof *result->data));
+    result->data = data;
+    result->alloc = nwords;
+    mpd_set_dynamic_data(result);
+    return 1;
+}
+
+/*
+ * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
+ * Resize the coefficient to length 'nwords':
+ *   Case nwords > result->alloc:
+ *     If realloc is successful:
+ *       'result' has a larger coefficient but the same value. Return 1.
+ *     Otherwise:
+ *       'result' has a the same coefficient. Return 0.
+ *   Case nwords < result->alloc:
+ *     If realloc is successful:
+ *       'result' has a smaller coefficient. result->len is undefined. Return 1.
+ *     Otherwise (unlikely):
+ *       'result' is unchanged. Reuse the now oversized coefficient. Return 1.
+ */
+int
+mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
+{
+    uint8_t err = 0;
+
+    mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
+    if (!err) {
+        result->data = p;
+        result->alloc = nwords;
+    }
+    else if (nwords > result->alloc) {
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/libmpdec/mpalloc.h b/libmpdec/mpalloc.h
new file mode 100644 (file)
index 0000000..b2b1ef0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_MPALLOC_H_
+#define LIBMPDEC_MPALLOC_H_
+
+
+#include <stdint.h>
+
+#include "mpdecimal.h"
+
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+int mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+int mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+int mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+
+int mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords);
+int mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_MPALLOC_H_ */
diff --git a/libmpdec/mpdecimal.c b/libmpdec/mpdecimal.c
new file mode 100644 (file)
index 0000000..56e27c2
--- /dev/null
@@ -0,0 +1,9155 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "basearith.h"
+#include "bits.h"
+#include "constants.h"
+#include "convolute.h"
+#include "crt.h"
+#include "mpalloc.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+#ifdef PPRO
+  #if defined(_MSC_VER)
+    #include <float.h>
+    #pragma float_control(precise, on)
+    #pragma fenv_access(on)
+  #elif !defined(__OpenBSD__) && !defined(__NetBSD__)
+    /* The C99 standard requires the pragma */
+    #include <fenv.h>
+    #pragma STDC FENV_ACCESS ON
+  #endif
+#endif
+
+
+/* Disable warning that is part of -Wextra since gcc 7.0 */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 7
+  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
+
+
+#if defined(_MSC_VER)
+  #define ALWAYS_INLINE __forceinline
+#elif defined (__IBMC__) || defined(__COMPCERT__) || defined(LEGACY_COMPILER)
+  #define ALWAYS_INLINE
+  #undef inline
+  #define inline
+#else
+  #ifdef TEST_COVERAGE
+    #define ALWAYS_INLINE
+  #else
+    #define ALWAYS_INLINE inline
+  #endif
+#endif
+
+
+#define MPD_NEWTONDIV_CUTOFF 1024L
+
+#define MPD_NEW_STATIC(name, flags, exp, digits, len) \
+        mpd_uint_t name##_data[MPD_MINALLOC_MAX];                    \
+        mpd_t name = {flags|MPD_STATIC|MPD_STATIC_DATA, exp, digits, \
+                      len, MPD_MINALLOC_MAX, name##_data}
+
+#define MPD_NEW_CONST(name, flags, exp, digits, len, alloc, initval) \
+        mpd_uint_t name##_data[alloc] = {initval};                   \
+        mpd_t name = {flags|MPD_STATIC|MPD_CONST_DATA, exp, digits,  \
+                      len, alloc, name##_data}
+
+#define MPD_NEW_SHARED(name, a) \
+        mpd_t name = {(a->flags&~MPD_DATAFLAGS)|MPD_STATIC|MPD_SHARED_DATA, \
+                      a->exp, a->digits, a->len, a->alloc, a->data}
+
+
+static mpd_uint_t data_one[1] = {1};
+static mpd_uint_t data_zero[1] = {0};
+static const mpd_t one = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_one};
+static const mpd_t minus_one = {MPD_NEG|MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1,
+                                data_one};
+static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero};
+
+static inline void _mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx,
+                                  uint32_t *status);
+static void _settriple(mpd_t *result, uint8_t sign, mpd_uint_t a,
+                       mpd_ssize_t exp);
+static inline mpd_ssize_t _mpd_real_size(mpd_uint_t *data, mpd_ssize_t size);
+
+static int _mpd_cmp_abs(const mpd_t *a, const mpd_t *b);
+
+static void _mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                      const mpd_context_t *ctx, uint32_t *status);
+static inline void _mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                             const mpd_context_t *ctx, uint32_t *status);
+static void _mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a,
+                              const mpd_t *b, uint32_t *status);
+static inline void _mpd_qpow_uint(mpd_t *result, const mpd_t *base,
+                                  mpd_uint_t exp, uint8_t resultsign,
+                                  const mpd_context_t *ctx, uint32_t *status);
+
+static mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n);
+
+
+/******************************************************************************/
+/*                                  Version                                   */
+/******************************************************************************/
+
+const char *
+mpd_version(void)
+{
+    return MPD_VERSION;
+}
+
+
+/******************************************************************************/
+/*                  Performance critical inline functions                     */
+/******************************************************************************/
+
+#ifdef CONFIG_64
+/* Digits in a word, primarily useful for the most significant word. */
+ALWAYS_INLINE int
+mpd_word_digits(mpd_uint_t word)
+{
+    if (word < mpd_pow10[9]) {
+        if (word < mpd_pow10[4]) {
+            if (word < mpd_pow10[2]) {
+                return (word < mpd_pow10[1]) ? 1 : 2;
+            }
+            return (word < mpd_pow10[3]) ? 3 : 4;
+        }
+        if (word < mpd_pow10[6]) {
+            return (word < mpd_pow10[5]) ? 5 : 6;
+        }
+        if (word < mpd_pow10[8]) {
+            return (word < mpd_pow10[7]) ? 7 : 8;
+        }
+        return 9;
+    }
+    if (word < mpd_pow10[14]) {
+        if (word < mpd_pow10[11]) {
+            return (word < mpd_pow10[10]) ? 10 : 11;
+        }
+        if (word < mpd_pow10[13]) {
+            return (word < mpd_pow10[12]) ? 12 : 13;
+        }
+        return 14;
+    }
+    if (word < mpd_pow10[18]) {
+        if (word < mpd_pow10[16]) {
+            return (word < mpd_pow10[15]) ? 15 : 16;
+        }
+        return (word < mpd_pow10[17]) ? 17 : 18;
+    }
+
+    return (word < mpd_pow10[19]) ? 19 : 20;
+}
+#else
+ALWAYS_INLINE int
+mpd_word_digits(mpd_uint_t word)
+{
+    if (word < mpd_pow10[4]) {
+        if (word < mpd_pow10[2]) {
+            return (word < mpd_pow10[1]) ? 1 : 2;
+        }
+        return (word < mpd_pow10[3]) ? 3 : 4;
+    }
+    if (word < mpd_pow10[6]) {
+        return (word < mpd_pow10[5]) ? 5 : 6;
+    }
+    if (word < mpd_pow10[8]) {
+        return (word < mpd_pow10[7]) ? 7 : 8;
+    }
+
+    return (word < mpd_pow10[9]) ? 9 : 10;
+}
+#endif
+
+
+/* Adjusted exponent */
+ALWAYS_INLINE mpd_ssize_t
+mpd_adjexp(const mpd_t *dec)
+{
+    return (dec->exp + dec->digits) - 1;
+}
+
+/* Etiny */
+ALWAYS_INLINE mpd_ssize_t
+mpd_etiny(const mpd_context_t *ctx)
+{
+    return ctx->emin - (ctx->prec - 1);
+}
+
+/* Etop: used for folding down in IEEE clamping */
+ALWAYS_INLINE mpd_ssize_t
+mpd_etop(const mpd_context_t *ctx)
+{
+    return ctx->emax - (ctx->prec - 1);
+}
+
+/* Most significant word */
+ALWAYS_INLINE mpd_uint_t
+mpd_msword(const mpd_t *dec)
+{
+    assert(dec->len > 0);
+    return dec->data[dec->len-1];
+}
+
+/* Most significant digit of a word */
+inline mpd_uint_t
+mpd_msd(mpd_uint_t word)
+{
+    int n;
+
+    n = mpd_word_digits(word);
+    return word / mpd_pow10[n-1];
+}
+
+/* Least significant digit of a word */
+ALWAYS_INLINE mpd_uint_t
+mpd_lsd(mpd_uint_t word)
+{
+    return word % 10;
+}
+
+/* Coefficient size needed to store 'digits' */
+mpd_ssize_t
+mpd_digits_to_size(mpd_ssize_t digits)
+{
+    mpd_ssize_t q, r;
+
+    _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS);
+    return (r == 0) ? q : q+1;
+}
+
+/* Number of digits in the exponent. Not defined for MPD_SSIZE_MIN. */
+inline int
+mpd_exp_digits(mpd_ssize_t exp)
+{
+    exp = (exp < 0) ? -exp : exp;
+    return mpd_word_digits(exp);
+}
+
+/* Canonical */
+ALWAYS_INLINE int
+mpd_iscanonical(const mpd_t *dec)
+{
+    (void)dec;
+    return 1;
+}
+
+/* Finite */
+ALWAYS_INLINE int
+mpd_isfinite(const mpd_t *dec)
+{
+    return !(dec->flags & MPD_SPECIAL);
+}
+
+/* Infinite */
+ALWAYS_INLINE int
+mpd_isinfinite(const mpd_t *dec)
+{
+    return dec->flags & MPD_INF;
+}
+
+/* NaN */
+ALWAYS_INLINE int
+mpd_isnan(const mpd_t *dec)
+{
+    return dec->flags & (MPD_NAN|MPD_SNAN);
+}
+
+/* Negative */
+ALWAYS_INLINE int
+mpd_isnegative(const mpd_t *dec)
+{
+    return dec->flags & MPD_NEG;
+}
+
+/* Positive */
+ALWAYS_INLINE int
+mpd_ispositive(const mpd_t *dec)
+{
+    return !(dec->flags & MPD_NEG);
+}
+
+/* qNaN */
+ALWAYS_INLINE int
+mpd_isqnan(const mpd_t *dec)
+{
+    return dec->flags & MPD_NAN;
+}
+
+/* Signed */
+ALWAYS_INLINE int
+mpd_issigned(const mpd_t *dec)
+{
+    return dec->flags & MPD_NEG;
+}
+
+/* sNaN */
+ALWAYS_INLINE int
+mpd_issnan(const mpd_t *dec)
+{
+    return dec->flags & MPD_SNAN;
+}
+
+/* Special */
+ALWAYS_INLINE int
+mpd_isspecial(const mpd_t *dec)
+{
+    return dec->flags & MPD_SPECIAL;
+}
+
+/* Zero */
+ALWAYS_INLINE int
+mpd_iszero(const mpd_t *dec)
+{
+    return !mpd_isspecial(dec) && mpd_msword(dec) == 0;
+}
+
+/* Test for zero when specials have been ruled out already */
+ALWAYS_INLINE int
+mpd_iszerocoeff(const mpd_t *dec)
+{
+    return mpd_msword(dec) == 0;
+}
+
+/* Normal */
+inline int
+mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx)
+{
+    if (mpd_isspecial(dec)) return 0;
+    if (mpd_iszerocoeff(dec)) return 0;
+
+    return mpd_adjexp(dec) >= ctx->emin;
+}
+
+/* Subnormal */
+inline int
+mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx)
+{
+    if (mpd_isspecial(dec)) return 0;
+    if (mpd_iszerocoeff(dec)) return 0;
+
+    return mpd_adjexp(dec) < ctx->emin;
+}
+
+/* Odd word */
+ALWAYS_INLINE int
+mpd_isoddword(mpd_uint_t word)
+{
+    return word & 1;
+}
+
+/* Odd coefficient */
+ALWAYS_INLINE int
+mpd_isoddcoeff(const mpd_t *dec)
+{
+    return mpd_isoddword(dec->data[0]);
+}
+
+/* 0 if dec is positive, 1 if dec is negative */
+ALWAYS_INLINE uint8_t
+mpd_sign(const mpd_t *dec)
+{
+    return dec->flags & MPD_NEG;
+}
+
+/* 1 if dec is positive, -1 if dec is negative */
+ALWAYS_INLINE int
+mpd_arith_sign(const mpd_t *dec)
+{
+    return 1 - 2 * mpd_isnegative(dec);
+}
+
+/* Radix */
+ALWAYS_INLINE long
+mpd_radix(void)
+{
+    return 10;
+}
+
+/* Dynamic decimal */
+ALWAYS_INLINE int
+mpd_isdynamic(const mpd_t *dec)
+{
+    return !(dec->flags & MPD_STATIC);
+}
+
+/* Static decimal */
+ALWAYS_INLINE int
+mpd_isstatic(const mpd_t *dec)
+{
+    return dec->flags & MPD_STATIC;
+}
+
+/* Data of decimal is dynamic */
+ALWAYS_INLINE int
+mpd_isdynamic_data(const mpd_t *dec)
+{
+    return !(dec->flags & MPD_DATAFLAGS);
+}
+
+/* Data of decimal is static */
+ALWAYS_INLINE int
+mpd_isstatic_data(const mpd_t *dec)
+{
+    return dec->flags & MPD_STATIC_DATA;
+}
+
+/* Data of decimal is shared */
+ALWAYS_INLINE int
+mpd_isshared_data(const mpd_t *dec)
+{
+    return dec->flags & MPD_SHARED_DATA;
+}
+
+/* Data of decimal is const */
+ALWAYS_INLINE int
+mpd_isconst_data(const mpd_t *dec)
+{
+    return dec->flags & MPD_CONST_DATA;
+}
+
+
+/******************************************************************************/
+/*                         Inline memory handling                             */
+/******************************************************************************/
+
+/* Fill destination with zeros */
+ALWAYS_INLINE void
+mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len)
+{
+    mpd_size_t i;
+
+    for (i = 0; i < len; i++) {
+        dest[i] = 0;
+    }
+}
+
+/* Free a decimal */
+ALWAYS_INLINE void
+mpd_del(mpd_t *dec)
+{
+    if (mpd_isdynamic_data(dec)) {
+        mpd_free(dec->data);
+    }
+    if (mpd_isdynamic(dec)) {
+        mpd_free(dec);
+    }
+}
+
+/*
+ * Resize the coefficient. Existing data up to 'nwords' is left untouched.
+ * Return 1 on success, 0 otherwise.
+ *
+ * Input invariant: MPD_MINALLOC <= result->alloc.
+ *
+ * Case nwords == result->alloc:
+ *     'result' is unchanged. Return 1.
+ *
+ * Case nwords > result->alloc:
+ *   Case realloc success:
+ *     The value of 'result' does not change. Return 1.
+ *   Case realloc failure:
+ *     'result' is NaN, status is updated with MPD_Malloc_error. Return 0.
+ *
+ * Case nwords < result->alloc:
+ *   Case is_static_data or realloc failure [1]:
+ *     'result' is unchanged. Return 1.
+ *   Case realloc success:
+ *     The value of result is undefined (expected). Return 1.
+ *
+ *
+ * [1] In that case the old (now oversized) area is still valid.
+ */
+ALWAYS_INLINE int
+mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+    assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+    assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+    assert(MPD_MINALLOC <= result->alloc);
+
+    nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords;
+    if (nwords == result->alloc) {
+        return 1;
+    }
+    if (mpd_isstatic_data(result)) {
+        if (nwords > result->alloc) {
+            return mpd_switch_to_dyn(result, nwords, status);
+        }
+        return 1;
+    }
+
+    return mpd_realloc_dyn(result, nwords, status);
+}
+
+/* Same as mpd_qresize, but do not set the result to NaN on failure. */
+static ALWAYS_INLINE int
+mpd_qresize_cxx(mpd_t *result, mpd_ssize_t nwords)
+{
+    assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+    assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+    assert(MPD_MINALLOC <= result->alloc);
+
+    nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords;
+    if (nwords == result->alloc) {
+        return 1;
+    }
+    if (mpd_isstatic_data(result)) {
+        if (nwords > result->alloc) {
+            return mpd_switch_to_dyn_cxx(result, nwords);
+        }
+        return 1;
+    }
+
+    return mpd_realloc_dyn_cxx(result, nwords);
+}
+
+/* Same as mpd_qresize, but the complete coefficient (including the old
+ * memory area!) is initialized to zero. */
+ALWAYS_INLINE int
+mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+    assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+    assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+    assert(MPD_MINALLOC <= result->alloc);
+
+    nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords;
+    if (nwords != result->alloc) {
+        if (mpd_isstatic_data(result)) {
+            if (nwords > result->alloc) {
+                return mpd_switch_to_dyn_zero(result, nwords, status);
+            }
+        }
+        else if (!mpd_realloc_dyn(result, nwords, status)) {
+            return 0;
+        }
+    }
+
+    mpd_uint_zero(result->data, nwords);
+    return 1;
+}
+
+/*
+ * Reduce memory size for the coefficient to MPD_MINALLOC. In theory,
+ * realloc may fail even when reducing the memory size. But in that case
+ * the old memory area is always big enough, so checking for MPD_Malloc_error
+ * is not imperative.
+ */
+ALWAYS_INLINE void
+mpd_minalloc(mpd_t *result)
+{
+    assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+    assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+
+    if (!mpd_isstatic_data(result) && result->alloc > MPD_MINALLOC) {
+        uint8_t err = 0;
+        result->data = mpd_realloc(result->data, MPD_MINALLOC,
+                                   sizeof *result->data, &err);
+        if (!err) {
+            result->alloc = MPD_MINALLOC;
+        }
+    }
+}
+
+int
+mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qresize(result, nwords, &status)) {
+        mpd_addstatus_raise(ctx, status);
+        return 0;
+    }
+    return 1;
+}
+
+int
+mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qresize_zero(result, nwords, &status)) {
+        mpd_addstatus_raise(ctx, status);
+        return 0;
+    }
+    return 1;
+}
+
+
+/******************************************************************************/
+/*                       Set attributes of a decimal                          */
+/******************************************************************************/
+
+/* Set digits. Assumption: result->len is initialized and > 0. */
+inline void
+mpd_setdigits(mpd_t *result)
+{
+    mpd_ssize_t wdigits = mpd_word_digits(mpd_msword(result));
+    result->digits = wdigits + (result->len-1) * MPD_RDIGITS;
+}
+
+/* Set sign */
+ALWAYS_INLINE void
+mpd_set_sign(mpd_t *result, uint8_t sign)
+{
+    result->flags &= ~MPD_NEG;
+    result->flags |= sign;
+}
+
+/* Copy sign from another decimal */
+ALWAYS_INLINE void
+mpd_signcpy(mpd_t *result, const mpd_t *a)
+{
+    uint8_t sign = a->flags&MPD_NEG;
+
+    result->flags &= ~MPD_NEG;
+    result->flags |= sign;
+}
+
+/* Set infinity */
+ALWAYS_INLINE void
+mpd_set_infinity(mpd_t *result)
+{
+    result->flags &= ~MPD_SPECIAL;
+    result->flags |= MPD_INF;
+}
+
+/* Set qNaN */
+ALWAYS_INLINE void
+mpd_set_qnan(mpd_t *result)
+{
+    result->flags &= ~MPD_SPECIAL;
+    result->flags |= MPD_NAN;
+}
+
+/* Set sNaN */
+ALWAYS_INLINE void
+mpd_set_snan(mpd_t *result)
+{
+    result->flags &= ~MPD_SPECIAL;
+    result->flags |= MPD_SNAN;
+}
+
+/* Set to negative */
+ALWAYS_INLINE void
+mpd_set_negative(mpd_t *result)
+{
+    result->flags |= MPD_NEG;
+}
+
+/* Set to positive */
+ALWAYS_INLINE void
+mpd_set_positive(mpd_t *result)
+{
+    result->flags &= ~MPD_NEG;
+}
+
+/* Set to dynamic */
+ALWAYS_INLINE void
+mpd_set_dynamic(mpd_t *result)
+{
+    result->flags &= ~MPD_STATIC;
+}
+
+/* Set to static */
+ALWAYS_INLINE void
+mpd_set_static(mpd_t *result)
+{
+    result->flags |= MPD_STATIC;
+}
+
+/* Set data to dynamic */
+ALWAYS_INLINE void
+mpd_set_dynamic_data(mpd_t *result)
+{
+    result->flags &= ~MPD_DATAFLAGS;
+}
+
+/* Set data to static */
+ALWAYS_INLINE void
+mpd_set_static_data(mpd_t *result)
+{
+    result->flags &= ~MPD_DATAFLAGS;
+    result->flags |= MPD_STATIC_DATA;
+}
+
+/* Set data to shared */
+ALWAYS_INLINE void
+mpd_set_shared_data(mpd_t *result)
+{
+    result->flags &= ~MPD_DATAFLAGS;
+    result->flags |= MPD_SHARED_DATA;
+}
+
+/* Set data to const */
+ALWAYS_INLINE void
+mpd_set_const_data(mpd_t *result)
+{
+    result->flags &= ~MPD_DATAFLAGS;
+    result->flags |= MPD_CONST_DATA;
+}
+
+/* Clear flags, preserving memory attributes. */
+ALWAYS_INLINE void
+mpd_clear_flags(mpd_t *result)
+{
+    result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+}
+
+/* Set flags, preserving memory attributes. */
+ALWAYS_INLINE void
+mpd_set_flags(mpd_t *result, uint8_t flags)
+{
+    result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+    result->flags |= flags;
+}
+
+/* Copy flags, preserving memory attributes of result. */
+ALWAYS_INLINE void
+mpd_copy_flags(mpd_t *result, const mpd_t *a)
+{
+    uint8_t aflags = a->flags;
+    result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+    result->flags |= (aflags & ~(MPD_STATIC|MPD_DATAFLAGS));
+}
+
+/* Initialize a workcontext from ctx. Set traps, flags and newtrap to 0. */
+static inline void
+mpd_workcontext(mpd_context_t *workctx, const mpd_context_t *ctx)
+{
+    workctx->prec = ctx->prec;
+    workctx->emax = ctx->emax;
+    workctx->emin = ctx->emin;
+    workctx->round = ctx->round;
+    workctx->traps = 0;
+    workctx->status = 0;
+    workctx->newtrap = 0;
+    workctx->clamp = ctx->clamp;
+    workctx->allcr = ctx->allcr;
+}
+
+
+/******************************************************************************/
+/*                  Getting and setting parts of decimals                     */
+/******************************************************************************/
+
+/* Flip the sign of a decimal */
+static inline void
+_mpd_negate(mpd_t *dec)
+{
+    dec->flags ^= MPD_NEG;
+}
+
+/* Set coefficient to zero */
+void
+mpd_zerocoeff(mpd_t *result)
+{
+    mpd_minalloc(result);
+    result->digits = 1;
+    result->len = 1;
+    result->data[0] = 0;
+}
+
+/* Set the coefficient to all nines. */
+void
+mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t len, r;
+
+    _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS);
+    len = (r == 0) ? len : len+1;
+
+    if (!mpd_qresize(result, len, status)) {
+        return;
+    }
+
+    result->len = len;
+    result->digits = ctx->prec;
+
+    --len;
+    if (r > 0) {
+        result->data[len--] = mpd_pow10[r]-1;
+    }
+    for (; len >= 0; --len) {
+        result->data[len] = MPD_RADIX-1;
+    }
+}
+
+/*
+ * Cut off the most significant digits so that the rest fits in ctx->prec.
+ * Cannot fail.
+ */
+static void
+_mpd_cap(mpd_t *result, const mpd_context_t *ctx)
+{
+    uint32_t dummy;
+    mpd_ssize_t len, r;
+
+    if (result->len > 0 && result->digits > ctx->prec) {
+        _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS);
+        len = (r == 0) ? len : len+1;
+
+        if (r != 0) {
+            result->data[len-1] %= mpd_pow10[r];
+        }
+
+        len = _mpd_real_size(result->data, len);
+        /* resize to fewer words cannot fail */
+        mpd_qresize(result, len, &dummy);
+        result->len = len;
+        mpd_setdigits(result);
+    }
+    if (mpd_iszero(result)) {
+        _settriple(result, mpd_sign(result), 0, result->exp);
+    }
+}
+
+/*
+ * Cut off the most significant digits of a NaN payload so that the rest
+ * fits in ctx->prec - ctx->clamp. Cannot fail.
+ */
+static void
+_mpd_fix_nan(mpd_t *result, const mpd_context_t *ctx)
+{
+    uint32_t dummy;
+    mpd_ssize_t prec;
+    mpd_ssize_t len, r;
+
+    prec = ctx->prec - ctx->clamp;
+    if (result->len > 0 && result->digits > prec) {
+        if (prec == 0) {
+            mpd_minalloc(result);
+            result->len = result->digits = 0;
+        }
+        else {
+            _mpd_idiv_word(&len, &r, prec, MPD_RDIGITS);
+            len = (r == 0) ? len : len+1;
+
+            if (r != 0) {
+                 result->data[len-1] %= mpd_pow10[r];
+            }
+
+            len = _mpd_real_size(result->data, len);
+            /* resize to fewer words cannot fail */
+            mpd_qresize(result, len, &dummy);
+            result->len = len;
+            mpd_setdigits(result);
+            if (mpd_iszerocoeff(result)) {
+                /* NaN0 is not a valid representation */
+                result->len = result->digits = 0;
+            }
+        }
+    }
+}
+
+/*
+ * Get n most significant digits from a decimal, where 0 < n <= MPD_UINT_DIGITS.
+ * Assumes MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for 32 and 64 bit
+ * machines.
+ *
+ * The result of the operation will be in lo. If the operation is impossible,
+ * hi will be nonzero. This is used to indicate an error.
+ */
+static inline void
+_mpd_get_msdigits(mpd_uint_t *hi, mpd_uint_t *lo, const mpd_t *dec,
+                  unsigned int n)
+{
+    mpd_uint_t r, tmp;
+
+    assert(0 < n && n <= MPD_RDIGITS+1);
+
+    _mpd_div_word(&tmp, &r, dec->digits, MPD_RDIGITS);
+    r = (r == 0) ? MPD_RDIGITS : r; /* digits in the most significant word */
+
+    *hi = 0;
+    *lo = dec->data[dec->len-1];
+    if (n <= r) {
+        *lo /= mpd_pow10[r-n];
+    }
+    else if (dec->len > 1) {
+        /* at this point 1 <= r < n <= MPD_RDIGITS+1 */
+        _mpd_mul_words(hi, lo, *lo, mpd_pow10[n-r]);
+        tmp = dec->data[dec->len-2] / mpd_pow10[MPD_RDIGITS-(n-r)];
+        *lo = *lo + tmp;
+        if (*lo < tmp) (*hi)++;
+    }
+}
+
+
+/******************************************************************************/
+/*                   Gathering information about a decimal                    */
+/******************************************************************************/
+
+/* The real size of the coefficient without leading zero words. */
+static inline mpd_ssize_t
+_mpd_real_size(mpd_uint_t *data, mpd_ssize_t size)
+{
+    while (size > 1 && data[size-1] == 0) {
+        size--;
+    }
+
+    return size;
+}
+
+/* Return number of trailing zeros. No errors are possible. */
+mpd_ssize_t
+mpd_trail_zeros(const mpd_t *dec)
+{
+    mpd_uint_t word;
+    mpd_ssize_t i, tz = 0;
+
+    for (i=0; i < dec->len; ++i) {
+        if (dec->data[i] != 0) {
+            word = dec->data[i];
+            tz = i * MPD_RDIGITS;
+            while (word % 10 == 0) {
+                word /= 10;
+                tz++;
+            }
+            break;
+        }
+    }
+
+    return tz;
+}
+
+/* Integer: Undefined for specials */
+static int
+_mpd_isint(const mpd_t *dec)
+{
+    mpd_ssize_t tz;
+
+    if (mpd_iszerocoeff(dec)) {
+        return 1;
+    }
+
+    tz = mpd_trail_zeros(dec);
+    return (dec->exp + tz >= 0);
+}
+
+/* Integer */
+int
+mpd_isinteger(const mpd_t *dec)
+{
+    if (mpd_isspecial(dec)) {
+        return 0;
+    }
+    return _mpd_isint(dec);
+}
+
+/* Word is a power of 10 */
+static int
+mpd_word_ispow10(mpd_uint_t word)
+{
+    int n;
+
+    n = mpd_word_digits(word);
+    if (word == mpd_pow10[n-1]) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Coefficient is a power of 10 */
+static int
+mpd_coeff_ispow10(const mpd_t *dec)
+{
+    if (mpd_word_ispow10(mpd_msword(dec))) {
+        if (_mpd_isallzero(dec->data, dec->len-1)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* All digits of a word are nines */
+static int
+mpd_word_isallnine(mpd_uint_t word)
+{
+    int n;
+
+    n = mpd_word_digits(word);
+    if (word == mpd_pow10[n]-1) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/* All digits of the coefficient are nines */
+static int
+mpd_coeff_isallnine(const mpd_t *dec)
+{
+    if (mpd_word_isallnine(mpd_msword(dec))) {
+        if (_mpd_isallnine(dec->data, dec->len-1)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* Odd decimal: Undefined for non-integers! */
+int
+mpd_isodd(const mpd_t *dec)
+{
+    mpd_uint_t q, r;
+    assert(mpd_isinteger(dec));
+    if (mpd_iszerocoeff(dec)) return 0;
+    if (dec->exp < 0) {
+        _mpd_div_word(&q, &r, -dec->exp, MPD_RDIGITS);
+        q = dec->data[q] / mpd_pow10[r];
+        return mpd_isoddword(q);
+    }
+    return dec->exp == 0 && mpd_isoddword(dec->data[0]);
+}
+
+/* Even: Undefined for non-integers! */
+int
+mpd_iseven(const mpd_t *dec)
+{
+    return !mpd_isodd(dec);
+}
+
+/******************************************************************************/
+/*                      Getting and setting decimals                          */
+/******************************************************************************/
+
+/* Internal function: Set a static decimal from a triple, no error checking. */
+static void
+_ssettriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp)
+{
+    mpd_set_flags(result, sign);
+    result->exp = exp;
+    _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX);
+    result->len = (result->data[1] == 0) ? 1 : 2;
+    mpd_setdigits(result);
+}
+
+/* Internal function: Set a decimal from a triple, no error checking. */
+static void
+_settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp)
+{
+    mpd_minalloc(result);
+    mpd_set_flags(result, sign);
+    result->exp = exp;
+    _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX);
+    result->len = (result->data[1] == 0) ? 1 : 2;
+    mpd_setdigits(result);
+}
+
+/* Set a special number from a triple */
+void
+mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type)
+{
+    mpd_minalloc(result);
+    result->flags &= ~(MPD_NEG|MPD_SPECIAL);
+    result->flags |= (sign|type);
+    result->exp = result->digits = result->len = 0;
+}
+
+/* Set result of NaN with an error status */
+void
+mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status)
+{
+    mpd_minalloc(result);
+    mpd_set_qnan(result);
+    mpd_set_positive(result);
+    result->exp = result->digits = result->len = 0;
+    *status |= flags;
+}
+
+/* quietly set a static decimal from an mpd_ssize_t */
+void
+mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx,
+                uint32_t *status)
+{
+    mpd_uint_t u;
+    uint8_t sign = MPD_POS;
+
+    if (a < 0) {
+        if (a == MPD_SSIZE_MIN) {
+            u = (mpd_uint_t)MPD_SSIZE_MAX +
+                (-(MPD_SSIZE_MIN+MPD_SSIZE_MAX));
+        }
+        else {
+            u = -a;
+        }
+        sign = MPD_NEG;
+    }
+    else {
+        u = a;
+    }
+    _ssettriple(result, sign, u, 0);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a static decimal from an mpd_uint_t */
+void
+mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx,
+               uint32_t *status)
+{
+    _ssettriple(result, MPD_POS, a, 0);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a static decimal from an int32_t */
+void
+mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a static decimal from a uint32_t */
+void
+mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    mpd_qsset_uint(result, a, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* quietly set a static decimal from an int64_t */
+void
+mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a static decimal from a uint64_t */
+void
+mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    mpd_qsset_uint(result, a, ctx, status);
+}
+#endif
+
+/* quietly set a decimal from an mpd_ssize_t */
+void
+mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx,
+               uint32_t *status)
+{
+    mpd_minalloc(result);
+    mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a decimal from an mpd_uint_t */
+void
+mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    _settriple(result, MPD_POS, a, 0);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a decimal from an int32_t */
+void
+mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx,
+             uint32_t *status)
+{
+    mpd_qset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a decimal from a uint32_t */
+void
+mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx,
+             uint32_t *status)
+{
+    mpd_qset_uint(result, a, ctx, status);
+}
+
+#if defined(CONFIG_32) && !defined(LEGACY_COMPILER)
+/* set a decimal from a uint64_t */
+static void
+_c32setu64(mpd_t *result, uint64_t u, uint8_t sign, uint32_t *status)
+{
+    mpd_uint_t w[3];
+    uint64_t q;
+    int i, len;
+
+    len = 0;
+    do {
+        q = u / MPD_RADIX;
+        w[len] = (mpd_uint_t)(u - q * MPD_RADIX);
+        u = q; len++;
+    } while (u != 0);
+
+    if (!mpd_qresize(result, len, status)) {
+        return;
+    }
+    for (i = 0; i < len; i++) {
+        result->data[i] = w[i];
+    }
+
+    mpd_set_flags(result, sign);
+    result->exp = 0;
+    result->len = len;
+    mpd_setdigits(result);
+}
+
+static void
+_c32_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    _c32setu64(result, a, MPD_POS, status);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* set a decimal from an int64_t */
+static void
+_c32_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    uint64_t u;
+    uint8_t sign = MPD_POS;
+
+    if (a < 0) {
+        if (a == INT64_MIN) {
+            u = (uint64_t)INT64_MAX + (-(INT64_MIN+INT64_MAX));
+        }
+        else {
+            u = -a;
+        }
+        sign = MPD_NEG;
+    }
+    else {
+        u = a;
+    }
+    _c32setu64(result, u, sign, status);
+    mpd_qfinalize(result, ctx, status);
+}
+#endif /* CONFIG_32 && !LEGACY_COMPILER */
+
+#ifndef LEGACY_COMPILER
+/* quietly set a decimal from an int64_t */
+void
+mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+             uint32_t *status)
+{
+#ifdef CONFIG_64
+    mpd_qset_ssize(result, a, ctx, status);
+#else
+    _c32_qset_i64(result, a, ctx, status);
+#endif
+}
+
+/* quietly set a decimal from an int64_t, use a maxcontext for conversion */
+void
+mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    uint32_t workstatus = 0;
+
+    mpd_maxcontext(&maxcontext);
+#ifdef CONFIG_64
+    mpd_qset_ssize(result, a, &maxcontext, &workstatus);
+#else
+    _c32_qset_i64(result, a, &maxcontext, &workstatus);
+#endif
+
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        /* we want exact results */
+        mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */
+    }
+    *status |= (workstatus&MPD_Errors);
+}
+
+/* quietly set a decimal from a uint64_t */
+void
+mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+             uint32_t *status)
+{
+#ifdef CONFIG_64
+    mpd_qset_uint(result, a, ctx, status);
+#else
+    _c32_qset_u64(result, a, ctx, status);
+#endif
+}
+
+/* quietly set a decimal from a uint64_t, use a maxcontext for conversion */
+void
+mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    uint32_t workstatus = 0;
+
+    mpd_maxcontext(&maxcontext);
+#ifdef CONFIG_64
+    mpd_qset_uint(result, a, &maxcontext, &workstatus);
+#else
+    _c32_qset_u64(result, a, &maxcontext, &workstatus);
+#endif
+
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        /* we want exact results */
+        mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */
+    }
+    *status |= (workstatus&MPD_Errors);
+}
+#endif /* !LEGACY_COMPILER */
+
+/*
+ * Quietly get an mpd_uint_t from a decimal. Assumes
+ * MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for
+ * 32 and 64 bit machines.
+ *
+ * If the operation is impossible, MPD_Invalid_operation is set.
+ */
+static mpd_uint_t
+_mpd_qget_uint(int use_sign, const mpd_t *a, uint32_t *status)
+{
+    mpd_t tmp;
+    mpd_uint_t tmp_data[2];
+    mpd_uint_t lo, hi;
+
+    if (mpd_isspecial(a)) {
+        *status |= MPD_Invalid_operation;
+        return MPD_UINT_MAX;
+    }
+    if (mpd_iszero(a)) {
+        return 0;
+    }
+    if (use_sign && mpd_isnegative(a)) {
+        *status |= MPD_Invalid_operation;
+        return MPD_UINT_MAX;
+    }
+
+    if (a->digits+a->exp > MPD_RDIGITS+1) {
+        *status |= MPD_Invalid_operation;
+        return MPD_UINT_MAX;
+    }
+
+    if (a->exp < 0) {
+        if (!_mpd_isint(a)) {
+            *status |= MPD_Invalid_operation;
+            return MPD_UINT_MAX;
+        }
+        /* At this point a->digits+a->exp <= MPD_RDIGITS+1,
+         * so the shift fits. */
+        tmp.data = tmp_data;
+        tmp.flags = MPD_STATIC|MPD_STATIC_DATA;
+        tmp.alloc = 2;
+        mpd_qsshiftr(&tmp, a, -a->exp);
+        tmp.exp = 0;
+        a = &tmp;
+    }
+
+    _mpd_get_msdigits(&hi, &lo, a, MPD_RDIGITS+1);
+    if (hi) {
+        *status |= MPD_Invalid_operation;
+        return MPD_UINT_MAX;
+    }
+
+    if (a->exp > 0) {
+        _mpd_mul_words(&hi, &lo, lo, mpd_pow10[a->exp]);
+        if (hi) {
+            *status |= MPD_Invalid_operation;
+            return MPD_UINT_MAX;
+        }
+    }
+
+    return lo;
+}
+
+/*
+ * Sets Invalid_operation for:
+ *   - specials
+ *   - negative numbers (except negative zero)
+ *   - non-integers
+ *   - overflow
+ */
+mpd_uint_t
+mpd_qget_uint(const mpd_t *a, uint32_t *status)
+{
+    return _mpd_qget_uint(1, a, status);
+}
+
+/* Same as above, but gets the absolute value, i.e. the sign is ignored. */
+mpd_uint_t
+mpd_qabs_uint(const mpd_t *a, uint32_t *status)
+{
+    return _mpd_qget_uint(0, a, status);
+}
+
+/* quietly get an mpd_ssize_t from a decimal */
+mpd_ssize_t
+mpd_qget_ssize(const mpd_t *a, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_uint_t u;
+    int isneg;
+
+    u = mpd_qabs_uint(a, &workstatus);
+    if (workstatus&MPD_Invalid_operation) {
+        *status |= workstatus;
+        return MPD_SSIZE_MAX;
+    }
+
+    isneg = mpd_isnegative(a);
+    if (u <= MPD_SSIZE_MAX) {
+        return isneg ? -((mpd_ssize_t)u) : (mpd_ssize_t)u;
+    }
+    else if (isneg && u+(mpd_uint_t)(MPD_SSIZE_MIN+MPD_SSIZE_MAX) == MPD_SSIZE_MAX) {
+        return MPD_SSIZE_MIN;
+    }
+
+    *status |= MPD_Invalid_operation;
+    return MPD_SSIZE_MAX;
+}
+
+#if defined(CONFIG_32) && !defined(LEGACY_COMPILER)
+/*
+ * Quietly get a uint64_t from a decimal. If the operation is impossible,
+ * MPD_Invalid_operation is set.
+ */
+static uint64_t
+_c32_qget_u64(int use_sign, const mpd_t *a, uint32_t *status)
+{
+    MPD_NEW_STATIC(tmp,0,0,20,3);
+    mpd_context_t maxcontext;
+    uint64_t ret;
+
+    tmp_data[0] = 709551615;
+    tmp_data[1] = 446744073;
+    tmp_data[2] = 18;
+
+    if (mpd_isspecial(a)) {
+        *status |= MPD_Invalid_operation;
+        return UINT64_MAX;
+    }
+    if (mpd_iszero(a)) {
+        return 0;
+    }
+    if (use_sign && mpd_isnegative(a)) {
+        *status |= MPD_Invalid_operation;
+        return UINT64_MAX;
+    }
+    if (!_mpd_isint(a)) {
+        *status |= MPD_Invalid_operation;
+        return UINT64_MAX;
+    }
+
+    if (_mpd_cmp_abs(a, &tmp) > 0) {
+        *status |= MPD_Invalid_operation;
+        return UINT64_MAX;
+    }
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qrescale(&tmp, a, 0, &maxcontext, &maxcontext.status);
+    maxcontext.status &= ~MPD_Rounded;
+    if (maxcontext.status != 0) {
+        *status |= (maxcontext.status|MPD_Invalid_operation); /* GCOV_NOT_REACHED */
+        return UINT64_MAX; /* GCOV_NOT_REACHED */
+    }
+
+    ret = 0;
+    switch (tmp.len) {
+    case 3:
+        ret += (uint64_t)tmp_data[2] * 1000000000000000000ULL;
+    case 2:
+        ret += (uint64_t)tmp_data[1] * 1000000000ULL;
+    case 1:
+        ret += tmp_data[0];
+        break;
+    default:
+        abort(); /* GCOV_NOT_REACHED */
+    }
+
+    return ret;
+}
+
+static int64_t
+_c32_qget_i64(const mpd_t *a, uint32_t *status)
+{
+    uint64_t u;
+    int isneg;
+
+    u = _c32_qget_u64(0, a, status);
+    if (*status&MPD_Invalid_operation) {
+        return INT64_MAX;
+    }
+
+    isneg = mpd_isnegative(a);
+    if (u <= INT64_MAX) {
+        return isneg ? -((int64_t)u) : (int64_t)u;
+    }
+    else if (isneg && u+(uint64_t)(INT64_MIN+INT64_MAX) == INT64_MAX) {
+        return INT64_MIN;
+    }
+
+    *status |= MPD_Invalid_operation;
+    return INT64_MAX;
+}
+#endif /* CONFIG_32 && !LEGACY_COMPILER */
+
+#ifdef CONFIG_64
+/* quietly get a uint64_t from a decimal */
+uint64_t
+mpd_qget_u64(const mpd_t *a, uint32_t *status)
+{
+    return mpd_qget_uint(a, status);
+}
+
+/* quietly get an int64_t from a decimal */
+int64_t
+mpd_qget_i64(const mpd_t *a, uint32_t *status)
+{
+    return mpd_qget_ssize(a, status);
+}
+
+/* quietly get a uint32_t from a decimal */
+uint32_t
+mpd_qget_u32(const mpd_t *a, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    uint64_t x = mpd_qget_uint(a, &workstatus);
+
+    if (workstatus&MPD_Invalid_operation) {
+        *status |= workstatus;
+        return UINT32_MAX;
+    }
+    if (x > UINT32_MAX) {
+        *status |= MPD_Invalid_operation;
+        return UINT32_MAX;
+    }
+
+    return (uint32_t)x;
+}
+
+/* quietly get an int32_t from a decimal */
+int32_t
+mpd_qget_i32(const mpd_t *a, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    int64_t x = mpd_qget_ssize(a, &workstatus);
+
+    if (workstatus&MPD_Invalid_operation) {
+        *status |= workstatus;
+        return INT32_MAX;
+    }
+    if (x < INT32_MIN || x > INT32_MAX) {
+        *status |= MPD_Invalid_operation;
+        return INT32_MAX;
+    }
+
+    return (int32_t)x;
+}
+#else
+#ifndef LEGACY_COMPILER
+/* quietly get a uint64_t from a decimal */
+uint64_t
+mpd_qget_u64(const mpd_t *a, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    uint64_t x = _c32_qget_u64(1, a, &workstatus);
+    *status |= workstatus;
+    return x;
+}
+
+/* quietly get an int64_t from a decimal */
+int64_t
+mpd_qget_i64(const mpd_t *a, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    int64_t x = _c32_qget_i64(a, &workstatus);
+    *status |= workstatus;
+    return x;
+}
+#endif
+
+/* quietly get a uint32_t from a decimal */
+uint32_t
+mpd_qget_u32(const mpd_t *a, uint32_t *status)
+{
+    return mpd_qget_uint(a, status);
+}
+
+/* quietly get an int32_t from a decimal */
+int32_t
+mpd_qget_i32(const mpd_t *a, uint32_t *status)
+{
+    return mpd_qget_ssize(a, status);
+}
+#endif
+
+
+/******************************************************************************/
+/*         Filtering input of functions, finalizing output of functions       */
+/******************************************************************************/
+
+/*
+ * Check if the operand is NaN, copy to result and return 1 if this is
+ * the case. Copying can fail since NaNs are allowed to have a payload that
+ * does not fit in MPD_MINALLOC.
+ */
+int
+mpd_qcheck_nan(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+               uint32_t *status)
+{
+    if (mpd_isnan(a)) {
+        *status |= mpd_issnan(a) ? MPD_Invalid_operation : 0;
+        mpd_qcopy(result, a, status);
+        mpd_set_qnan(result);
+        _mpd_fix_nan(result, ctx);
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * Check if either operand is NaN, copy to result and return 1 if this
+ * is the case. Copying can fail since NaNs are allowed to have a payload
+ * that does not fit in MPD_MINALLOC.
+ */
+int
+mpd_qcheck_nans(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    if ((a->flags|b->flags)&(MPD_NAN|MPD_SNAN)) {
+        const mpd_t *choice = b;
+        if (mpd_issnan(a)) {
+            choice = a;
+            *status |= MPD_Invalid_operation;
+        }
+        else if (mpd_issnan(b)) {
+            *status |= MPD_Invalid_operation;
+        }
+        else if (mpd_isqnan(a)) {
+            choice = a;
+        }
+        mpd_qcopy(result, choice, status);
+        mpd_set_qnan(result);
+        _mpd_fix_nan(result, ctx);
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * Check if one of the operands is NaN, copy to result and return 1 if this
+ * is the case. Copying can fail since NaNs are allowed to have a payload
+ * that does not fit in MPD_MINALLOC.
+ */
+static int
+mpd_qcheck_3nans(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
+                 const mpd_context_t *ctx, uint32_t *status)
+{
+    if ((a->flags|b->flags|c->flags)&(MPD_NAN|MPD_SNAN)) {
+        const mpd_t *choice = c;
+        if (mpd_issnan(a)) {
+            choice = a;
+            *status |= MPD_Invalid_operation;
+        }
+        else if (mpd_issnan(b)) {
+            choice = b;
+            *status |= MPD_Invalid_operation;
+        }
+        else if (mpd_issnan(c)) {
+            *status |= MPD_Invalid_operation;
+        }
+        else if (mpd_isqnan(a)) {
+            choice = a;
+        }
+        else if (mpd_isqnan(b)) {
+            choice = b;
+        }
+        mpd_qcopy(result, choice, status);
+        mpd_set_qnan(result);
+        _mpd_fix_nan(result, ctx);
+        return 1;
+    }
+    return 0;
+}
+
+/* Check if rounding digit 'rnd' leads to an increment. */
+static inline int
+_mpd_rnd_incr(const mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx)
+{
+    int ld;
+
+    switch (ctx->round) {
+    case MPD_ROUND_DOWN: case MPD_ROUND_TRUNC:
+        return 0;
+    case MPD_ROUND_HALF_UP:
+        return (rnd >= 5);
+    case MPD_ROUND_HALF_EVEN:
+        return (rnd > 5) || ((rnd == 5) && mpd_isoddcoeff(dec));
+    case MPD_ROUND_CEILING:
+        return !(rnd == 0 || mpd_isnegative(dec));
+    case MPD_ROUND_FLOOR:
+        return !(rnd == 0 || mpd_ispositive(dec));
+    case MPD_ROUND_HALF_DOWN:
+        return (rnd > 5);
+    case MPD_ROUND_UP:
+        return !(rnd == 0);
+    case MPD_ROUND_05UP:
+        ld = (int)mpd_lsd(dec->data[0]);
+        return (!(rnd == 0) && (ld == 0 || ld == 5));
+    default:
+        /* Without a valid context, further results will be undefined. */
+        return 0; /* GCOV_NOT_REACHED */
+    }
+}
+
+/*
+ * Apply rounding to a decimal that has been right-shifted into a full
+ * precision decimal. If an increment leads to an overflow of the precision,
+ * adjust the coefficient and the exponent and check the new exponent for
+ * overflow.
+ */
+static inline void
+_mpd_apply_round(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+                 uint32_t *status)
+{
+    if (_mpd_rnd_incr(dec, rnd, ctx)) {
+        /* We have a number with exactly ctx->prec digits. The increment
+         * can only lead to an overflow if the decimal is all nines. In
+         * that case, the result is a power of ten with prec+1 digits.
+         *
+         * If the precision is a multiple of MPD_RDIGITS, this situation is
+         * detected by _mpd_baseincr returning a carry.
+         * If the precision is not a multiple of MPD_RDIGITS, we have to
+         * check if the result has one digit too many.
+         */
+        mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+        if (carry) {
+            dec->data[dec->len-1] = mpd_pow10[MPD_RDIGITS-1];
+            dec->exp += 1;
+            _mpd_check_exp(dec, ctx, status);
+            return;
+        }
+        mpd_setdigits(dec);
+        if (dec->digits > ctx->prec) {
+            mpd_qshiftr_inplace(dec, 1);
+            dec->exp += 1;
+            dec->digits = ctx->prec;
+            _mpd_check_exp(dec, ctx, status);
+        }
+    }
+}
+
+/*
+ * Apply rounding to a decimal. Allow overflow of the precision.
+ */
+static inline void
+_mpd_apply_round_excess(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+                        uint32_t *status)
+{
+    if (_mpd_rnd_incr(dec, rnd, ctx)) {
+        mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+        if (carry) {
+            if (!mpd_qresize(dec, dec->len+1, status)) {
+                return;
+            }
+            dec->data[dec->len] = 1;
+            dec->len += 1;
+        }
+        mpd_setdigits(dec);
+    }
+}
+
+/*
+ * Apply rounding to a decimal that has been right-shifted into a decimal
+ * with full precision or less. Return failure if an increment would
+ * overflow the precision.
+ */
+static inline int
+_mpd_apply_round_fit(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+                     uint32_t *status)
+{
+    if (_mpd_rnd_incr(dec, rnd, ctx)) {
+        mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+        if (carry) {
+            if (!mpd_qresize(dec, dec->len+1, status)) {
+                return 0;
+            }
+            dec->data[dec->len] = 1;
+            dec->len += 1;
+        }
+        mpd_setdigits(dec);
+        if (dec->digits > ctx->prec) {
+            mpd_seterror(dec, MPD_Invalid_operation, status);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/* Check a normal number for overflow, underflow, clamping. If the operand
+   is modified, it will be zero, special or (sub)normal with a coefficient
+   that fits into the current context precision. */
+static inline void
+_mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t adjexp, etiny, shift;
+    int rnd;
+
+    adjexp = mpd_adjexp(dec);
+    if (adjexp > ctx->emax) {
+
+        if (mpd_iszerocoeff(dec)) {
+            dec->exp = ctx->emax;
+            if (ctx->clamp) {
+                dec->exp -= (ctx->prec-1);
+            }
+            mpd_zerocoeff(dec);
+            *status |= MPD_Clamped;
+            return;
+        }
+
+        switch (ctx->round) {
+        case MPD_ROUND_HALF_UP: case MPD_ROUND_HALF_EVEN:
+        case MPD_ROUND_HALF_DOWN: case MPD_ROUND_UP:
+        case MPD_ROUND_TRUNC:
+            mpd_setspecial(dec, mpd_sign(dec), MPD_INF);
+            break;
+        case MPD_ROUND_DOWN: case MPD_ROUND_05UP:
+            mpd_qmaxcoeff(dec, ctx, status);
+            dec->exp = ctx->emax - ctx->prec + 1;
+            break;
+        case MPD_ROUND_CEILING:
+            if (mpd_isnegative(dec)) {
+                mpd_qmaxcoeff(dec, ctx, status);
+                dec->exp = ctx->emax - ctx->prec + 1;
+            }
+            else {
+                mpd_setspecial(dec, MPD_POS, MPD_INF);
+            }
+            break;
+        case MPD_ROUND_FLOOR:
+            if (mpd_ispositive(dec)) {
+                mpd_qmaxcoeff(dec, ctx, status);
+                dec->exp = ctx->emax - ctx->prec + 1;
+            }
+            else {
+                mpd_setspecial(dec, MPD_NEG, MPD_INF);
+            }
+            break;
+        default: /* debug */
+            abort(); /* GCOV_NOT_REACHED */
+        }
+
+        *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+
+    } /* fold down */
+    else if (ctx->clamp && dec->exp > mpd_etop(ctx)) {
+        /* At this point adjexp=exp+digits-1 <= emax and exp > etop=emax-prec+1:
+         *   (1) shift = exp -emax+prec-1 > 0
+         *   (2) digits+shift = exp+digits-1 - emax + prec <= prec */
+        shift = dec->exp - mpd_etop(ctx);
+        if (!mpd_qshiftl(dec, dec, shift, status)) {
+            return;
+        }
+        dec->exp -= shift;
+        *status |= MPD_Clamped;
+        if (!mpd_iszerocoeff(dec) && adjexp < ctx->emin) {
+            /* Underflow is impossible, since exp < etiny=emin-prec+1
+             * and exp > etop=emax-prec+1 would imply emax < emin. */
+            *status |= MPD_Subnormal;
+        }
+    }
+    else if (adjexp < ctx->emin) {
+
+        etiny = mpd_etiny(ctx);
+
+        if (mpd_iszerocoeff(dec)) {
+            if (dec->exp < etiny) {
+                dec->exp = etiny;
+                mpd_zerocoeff(dec);
+                *status |= MPD_Clamped;
+            }
+            return;
+        }
+
+        *status |= MPD_Subnormal;
+        if (dec->exp < etiny) {
+            /* At this point adjexp=exp+digits-1 < emin and exp < etiny=emin-prec+1:
+             *   (1) shift = emin-prec+1 - exp > 0
+             *   (2) digits-shift = exp+digits-1 - emin + prec < prec */
+            shift = etiny - dec->exp;
+            rnd = (int)mpd_qshiftr_inplace(dec, shift);
+            dec->exp = etiny;
+            /* We always have a spare digit in case of an increment. */
+            _mpd_apply_round_excess(dec, rnd, ctx, status);
+            *status |= MPD_Rounded;
+            if (rnd) {
+                *status |= (MPD_Inexact|MPD_Underflow);
+                if (mpd_iszerocoeff(dec)) {
+                    mpd_zerocoeff(dec);
+                    *status |= MPD_Clamped;
+                }
+            }
+        }
+        /* Case exp >= etiny=emin-prec+1:
+         *   (1) adjexp=exp+digits-1 < emin
+         *   (2) digits < emin-exp+1 <= prec */
+    }
+}
+
+/* Transcendental functions do not always set Underflow reliably,
+ * since they only use as much precision as is necessary for correct
+ * rounding. If a result like 1.0000000000e-101 is finalized, there
+ * is no rounding digit that would trigger Underflow. But we can
+ * assume Inexact, so a short check suffices. */
+static inline void
+mpd_check_underflow(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+    if (mpd_adjexp(dec) < ctx->emin && !mpd_iszero(dec) &&
+        dec->exp < mpd_etiny(ctx)) {
+        *status |= MPD_Underflow;
+    }
+}
+
+/* Check if a normal number must be rounded after the exponent has been checked. */
+static inline void
+_mpd_check_round(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_uint_t rnd;
+    mpd_ssize_t shift;
+
+    /* must handle specials: _mpd_check_exp() can produce infinities or NaNs */
+    if (mpd_isspecial(dec)) {
+        return;
+    }
+
+    if (dec->digits > ctx->prec) {
+        shift = dec->digits - ctx->prec;
+        rnd = mpd_qshiftr_inplace(dec, shift);
+        dec->exp += shift;
+        _mpd_apply_round(dec, rnd, ctx, status);
+        *status |= MPD_Rounded;
+        if (rnd) {
+            *status |= MPD_Inexact;
+        }
+    }
+}
+
+/* Finalize all operations. */
+void
+mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status)
+{
+    if (mpd_isspecial(result)) {
+        if (mpd_isnan(result)) {
+            _mpd_fix_nan(result, ctx);
+        }
+        return;
+    }
+
+    _mpd_check_exp(result, ctx, status);
+    _mpd_check_round(result, ctx, status);
+}
+
+
+/******************************************************************************/
+/*                                 Copying                                    */
+/******************************************************************************/
+
+/* Internal function: Copy a decimal, share data with src: USE WITH CARE! */
+static inline void
+_mpd_copy_shared(mpd_t *dest, const mpd_t *src)
+{
+    dest->flags = src->flags;
+    dest->exp = src->exp;
+    dest->digits = src->digits;
+    dest->len = src->len;
+    dest->alloc = src->alloc;
+    dest->data = src->data;
+
+    mpd_set_shared_data(dest);
+}
+
+/*
+ * Copy a decimal. In case of an error, status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+    if (result == a) return 1;
+
+    if (!mpd_qresize(result, a->len, status)) {
+        return 0;
+    }
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+    result->digits = a->digits;
+    result->len = a->len;
+    memcpy(result->data, a->data, a->len * (sizeof *result->data));
+
+    return 1;
+}
+
+/* Same as mpd_qcopy, but do not set the result to NaN on failure. */
+int
+mpd_qcopy_cxx(mpd_t *result, const mpd_t *a)
+{
+    if (result == a) return 1;
+
+    if (!mpd_qresize_cxx(result, a->len)) {
+        return 0;
+    }
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+    result->digits = a->digits;
+    result->len = a->len;
+    memcpy(result->data, a->data, a->len * (sizeof *result->data));
+
+    return 1;
+}
+
+/*
+ * Copy to a decimal with a static buffer. The caller has to make sure that
+ * the buffer is big enough. Cannot fail.
+ */
+static void
+mpd_qcopy_static(mpd_t *result, const mpd_t *a)
+{
+    if (result == a) return;
+
+    memcpy(result->data, a->data, a->len * (sizeof *result->data));
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+    result->digits = a->digits;
+    result->len = a->len;
+}
+
+/*
+ * Return a newly allocated copy of the operand. In case of an error,
+ * status is set to MPD_Malloc_error and the return value is NULL.
+ */
+mpd_t *
+mpd_qncopy(const mpd_t *a)
+{
+    mpd_t *result;
+
+    if ((result = mpd_qnew_size(a->len)) == NULL) {
+        return NULL;
+    }
+    memcpy(result->data, a->data, a->len * (sizeof *result->data));
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+    result->digits = a->digits;
+    result->len = a->len;
+
+    return result;
+}
+
+/*
+ * Copy a decimal and set the sign to positive. In case of an error, the
+ * status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+    if (!mpd_qcopy(result, a, status)) {
+        return 0;
+    }
+    mpd_set_positive(result);
+    return 1;
+}
+
+/*
+ * Copy a decimal and negate the sign. In case of an error, the
+ * status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+    if (!mpd_qcopy(result, a, status)) {
+        return 0;
+    }
+    _mpd_negate(result);
+    return 1;
+}
+
+/*
+ * Copy a decimal, setting the sign of the first operand to the sign of the
+ * second operand. In case of an error, the status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+    uint8_t sign_b = mpd_sign(b); /* result may equal b! */
+
+    if (!mpd_qcopy(result, a, status)) {
+        return 0;
+    }
+    mpd_set_sign(result, sign_b);
+    return 1;
+}
+
+
+/******************************************************************************/
+/*                                Comparisons                                 */
+/******************************************************************************/
+
+/*
+ * For all functions that compare two operands and return an int the usual
+ * convention applies to the return value:
+ *
+ * -1 if op1 < op2
+ *  0 if op1 == op2
+ *  1 if op1 > op2
+ *
+ *  INT_MAX for error
+ */
+
+
+/* Convenience macro. If a and b are not equal, return from the calling
+ * function with the correct comparison value. */
+#define CMP_EQUAL_OR_RETURN(a, b)  \
+        if (a != b) {              \
+                if (a < b) {       \
+                        return -1; \
+                }                  \
+                return 1;          \
+        }
+
+/*
+ * Compare the data of big and small. This function does the equivalent
+ * of first shifting small to the left and then comparing the data of
+ * big and small, except that no allocation for the left shift is needed.
+ */
+static int
+_mpd_basecmp(mpd_uint_t *big, mpd_uint_t *small, mpd_size_t n, mpd_size_t m,
+             mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+    /* spurious uninitialized warnings */
+    mpd_uint_t l=l, lprev=lprev, h=h;
+#else
+    mpd_uint_t l, lprev, h;
+#endif
+    mpd_uint_t q, r;
+    mpd_uint_t ph, x;
+
+    assert(m > 0 && n >= m && shift > 0);
+
+    _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+    if (r != 0) {
+
+        ph = mpd_pow10[r];
+
+        --m; --n;
+        _mpd_divmod_pow10(&h, &lprev, small[m--], MPD_RDIGITS-r);
+        if (h != 0) {
+            CMP_EQUAL_OR_RETURN(big[n], h)
+            --n;
+        }
+        for (; m != MPD_SIZE_MAX; m--,n--) {
+            _mpd_divmod_pow10(&h, &l, small[m], MPD_RDIGITS-r);
+            x = ph * lprev + h;
+            CMP_EQUAL_OR_RETURN(big[n], x)
+            lprev = l;
+        }
+        x = ph * lprev;
+        CMP_EQUAL_OR_RETURN(big[q], x)
+    }
+    else {
+        while (--m != MPD_SIZE_MAX) {
+            CMP_EQUAL_OR_RETURN(big[m+q], small[m])
+        }
+    }
+
+    return !_mpd_isallzero(big, q);
+}
+
+/* Compare two decimals with the same adjusted exponent. */
+static int
+_mpd_cmp_same_adjexp(const mpd_t *a, const mpd_t *b)
+{
+    mpd_ssize_t shift, i;
+
+    if (a->exp != b->exp) {
+        /* Cannot wrap: a->exp + a->digits = b->exp + b->digits, so
+         * a->exp - b->exp = b->digits - a->digits. */
+        shift = a->exp - b->exp;
+        if (shift > 0) {
+            return -1 * _mpd_basecmp(b->data, a->data, b->len, a->len, shift);
+        }
+        else {
+            return _mpd_basecmp(a->data, b->data, a->len, b->len, -shift);
+        }
+    }
+
+    /*
+     * At this point adjexp(a) == adjexp(b) and a->exp == b->exp,
+     * so a->digits == b->digits, therefore a->len == b->len.
+     */
+    for (i = a->len-1; i >= 0; --i) {
+        CMP_EQUAL_OR_RETURN(a->data[i], b->data[i])
+    }
+
+    return 0;
+}
+
+/* Compare two numerical values. */
+static int
+_mpd_cmp(const mpd_t *a, const mpd_t *b)
+{
+    mpd_ssize_t adjexp_a, adjexp_b;
+
+    /* equal pointers */
+    if (a == b) {
+        return 0;
+    }
+
+    /* infinities */
+    if (mpd_isinfinite(a)) {
+        if (mpd_isinfinite(b)) {
+            return mpd_isnegative(b) - mpd_isnegative(a);
+        }
+        return mpd_arith_sign(a);
+    }
+    if (mpd_isinfinite(b)) {
+        return -mpd_arith_sign(b);
+    }
+
+    /* zeros */
+    if (mpd_iszerocoeff(a)) {
+        if (mpd_iszerocoeff(b)) {
+            return 0;
+        }
+        return -mpd_arith_sign(b);
+    }
+    if (mpd_iszerocoeff(b)) {
+        return mpd_arith_sign(a);
+    }
+
+    /* different signs */
+    if (mpd_sign(a) != mpd_sign(b)) {
+        return mpd_sign(b) - mpd_sign(a);
+    }
+
+    /* different adjusted exponents */
+    adjexp_a = mpd_adjexp(a);
+    adjexp_b = mpd_adjexp(b);
+    if (adjexp_a != adjexp_b) {
+        if (adjexp_a < adjexp_b) {
+            return -1 * mpd_arith_sign(a);
+        }
+        return mpd_arith_sign(a);
+    }
+
+    /* same adjusted exponents */
+    return _mpd_cmp_same_adjexp(a, b) * mpd_arith_sign(a);
+}
+
+/* Compare the absolutes of two numerical values. */
+static int
+_mpd_cmp_abs(const mpd_t *a, const mpd_t *b)
+{
+    mpd_ssize_t adjexp_a, adjexp_b;
+
+    /* equal pointers */
+    if (a == b) {
+        return 0;
+    }
+
+    /* infinities */
+    if (mpd_isinfinite(a)) {
+        if (mpd_isinfinite(b)) {
+            return 0;
+        }
+        return 1;
+    }
+    if (mpd_isinfinite(b)) {
+        return -1;
+    }
+
+    /* zeros */
+    if (mpd_iszerocoeff(a)) {
+        if (mpd_iszerocoeff(b)) {
+            return 0;
+        }
+        return -1;
+    }
+    if (mpd_iszerocoeff(b)) {
+        return 1;
+    }
+
+    /* different adjusted exponents */
+    adjexp_a = mpd_adjexp(a);
+    adjexp_b = mpd_adjexp(b);
+    if (adjexp_a != adjexp_b) {
+        if (adjexp_a < adjexp_b) {
+            return -1;
+        }
+        return 1;
+    }
+
+    /* same adjusted exponents */
+    return _mpd_cmp_same_adjexp(a, b);
+}
+
+/* Compare two values and return an integer result. */
+int
+mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_isnan(a) || mpd_isnan(b)) {
+            *status |= MPD_Invalid_operation;
+            return INT_MAX;
+        }
+    }
+
+    return _mpd_cmp(a, b);
+}
+
+/*
+ * Compare a and b, convert the usual integer result to a decimal and
+ * store it in 'result'. For convenience, the integer result of the comparison
+ * is returned. Comparisons involving NaNs return NaN/INT_MAX.
+ */
+int
+mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return INT_MAX;
+        }
+    }
+
+    c = _mpd_cmp(a, b);
+    _settriple(result, (c < 0), (c != 0), 0);
+    return c;
+}
+
+/* Same as mpd_compare(), but signal for all NaNs, i.e. also for quiet NaNs. */
+int
+mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                    const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            *status |= MPD_Invalid_operation;
+            return INT_MAX;
+        }
+    }
+
+    c = _mpd_cmp(a, b);
+    _settriple(result, (c < 0), (c != 0), 0);
+    return c;
+}
+
+/* Compare the operands using a total order. */
+int
+mpd_cmp_total(const mpd_t *a, const mpd_t *b)
+{
+    mpd_t aa, bb;
+    int nan_a, nan_b;
+    int c;
+
+    if (mpd_sign(a) != mpd_sign(b)) {
+        return mpd_sign(b) - mpd_sign(a);
+    }
+
+
+    if (mpd_isnan(a)) {
+        c = 1;
+        if (mpd_isnan(b)) {
+            nan_a = (mpd_isqnan(a)) ? 1 : 0;
+            nan_b = (mpd_isqnan(b)) ? 1 : 0;
+            if (nan_b == nan_a) {
+                if (a->len > 0 && b->len > 0) {
+                    _mpd_copy_shared(&aa, a);
+                    _mpd_copy_shared(&bb, b);
+                    aa.exp = bb.exp = 0;
+                    /* compare payload */
+                    c = _mpd_cmp_abs(&aa, &bb);
+                }
+                else {
+                    c = (a->len > 0) - (b->len > 0);
+                }
+            }
+            else {
+                c = nan_a - nan_b;
+            }
+        }
+    }
+    else if (mpd_isnan(b)) {
+        c = -1;
+    }
+    else {
+        c = _mpd_cmp_abs(a, b);
+        if (c == 0 && a->exp != b->exp) {
+            c = (a->exp < b->exp) ? -1 : 1;
+        }
+    }
+
+    return c * mpd_arith_sign(a);
+}
+
+/*
+ * Compare a and b according to a total order, convert the usual integer result
+ * to a decimal and store it in 'result'. For convenience, the integer result
+ * of the comparison is returned.
+ */
+int
+mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b)
+{
+    int c;
+
+    c = mpd_cmp_total(a, b);
+    _settriple(result, (c < 0), (c != 0), 0);
+    return c;
+}
+
+/* Compare the magnitude of the operands using a total order. */
+int
+mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b)
+{
+    mpd_t aa, bb;
+
+    _mpd_copy_shared(&aa, a);
+    _mpd_copy_shared(&bb, b);
+
+    mpd_set_positive(&aa);
+    mpd_set_positive(&bb);
+
+    return mpd_cmp_total(&aa, &bb);
+}
+
+/*
+ * Compare the magnitude of a and b according to a total order, convert the
+ * the usual integer result to a decimal and store it in 'result'.
+ * For convenience, the integer result of the comparison is returned.
+ */
+int
+mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b)
+{
+    int c;
+
+    c = mpd_cmp_total_mag(a, b);
+    _settriple(result, (c < 0), (c != 0), 0);
+    return c;
+}
+
+/* Determine an ordering for operands that are numerically equal. */
+static inline int
+_mpd_cmp_numequal(const mpd_t *a, const mpd_t *b)
+{
+    int sign_a, sign_b;
+    int c;
+
+    sign_a = mpd_sign(a);
+    sign_b = mpd_sign(b);
+    if (sign_a != sign_b) {
+        c = sign_b - sign_a;
+    }
+    else {
+        c = (a->exp < b->exp) ? -1 : 1;
+        c *= mpd_arith_sign(a);
+    }
+
+    return c;
+}
+
+
+/******************************************************************************/
+/*                         Shifting the coefficient                           */
+/******************************************************************************/
+
+/*
+ * Shift the coefficient of the operand to the left, no check for specials.
+ * Both operands may be the same pointer. If the result length has to be
+ * increased, mpd_qresize() might fail with MPD_Malloc_error.
+ */
+int
+mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status)
+{
+    mpd_ssize_t size;
+
+    assert(!mpd_isspecial(a));
+    assert(n >= 0);
+
+    if (mpd_iszerocoeff(a) || n == 0) {
+        return mpd_qcopy(result, a, status);
+    }
+
+    size = mpd_digits_to_size(a->digits+n);
+    if (!mpd_qresize(result, size, status)) {
+        return 0; /* result is NaN */
+    }
+
+    _mpd_baseshiftl(result->data, a->data, size, a->len, n);
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+    result->digits = a->digits+n;
+    result->len = size;
+
+    return 1;
+}
+
+/* Determine the rounding indicator if all digits of the coefficient are shifted
+ * out of the picture. */
+static mpd_uint_t
+_mpd_get_rnd(const mpd_uint_t *data, mpd_ssize_t len, int use_msd)
+{
+    mpd_uint_t rnd = 0, rest = 0, word;
+
+    word = data[len-1];
+    /* special treatment for the most significant digit if shift == digits */
+    if (use_msd) {
+        _mpd_divmod_pow10(&rnd, &rest, word, mpd_word_digits(word)-1);
+        if (len > 1 && rest == 0) {
+             rest = !_mpd_isallzero(data, len-1);
+        }
+    }
+    else {
+        rest = !_mpd_isallzero(data, len);
+    }
+
+    return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd;
+}
+
+/*
+ * Same as mpd_qshiftr(), but 'result' is an mpd_t with a static coefficient.
+ * It is the caller's responsibility to ensure that the coefficient is big
+ * enough. The function cannot fail.
+ */
+static mpd_uint_t
+mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n)
+{
+    mpd_uint_t rnd;
+    mpd_ssize_t size;
+
+    assert(!mpd_isspecial(a));
+    assert(n >= 0);
+
+    if (mpd_iszerocoeff(a) || n == 0) {
+        mpd_qcopy_static(result, a);
+        return 0;
+    }
+
+    if (n >= a->digits) {
+        rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits));
+        mpd_zerocoeff(result);
+    }
+    else {
+        result->digits = a->digits-n;
+        size = mpd_digits_to_size(result->digits);
+        rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+        result->len = size;
+    }
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+
+    return rnd;
+}
+
+/*
+ * Inplace shift of the coefficient to the right, no check for specials.
+ * Returns the rounding indicator for mpd_rnd_incr().
+ * The function cannot fail.
+ */
+mpd_uint_t
+mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n)
+{
+    uint32_t dummy;
+    mpd_uint_t rnd;
+    mpd_ssize_t size;
+
+    assert(!mpd_isspecial(result));
+    assert(n >= 0);
+
+    if (mpd_iszerocoeff(result) || n == 0) {
+        return 0;
+    }
+
+    if (n >= result->digits) {
+        rnd = _mpd_get_rnd(result->data, result->len, (n==result->digits));
+        mpd_zerocoeff(result);
+    }
+    else {
+        rnd = _mpd_baseshiftr(result->data, result->data, result->len, n);
+        result->digits -= n;
+        size = mpd_digits_to_size(result->digits);
+        /* reducing the size cannot fail */
+        mpd_qresize(result, size, &dummy);
+        result->len = size;
+    }
+
+    return rnd;
+}
+
+/*
+ * Shift the coefficient of the operand to the right, no check for specials.
+ * Both operands may be the same pointer. Returns the rounding indicator to
+ * be used by mpd_rnd_incr(). If the result length has to be increased,
+ * mpd_qcopy() or mpd_qresize() might fail with MPD_Malloc_error. In those
+ * cases, MPD_UINT_MAX is returned.
+ */
+mpd_uint_t
+mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status)
+{
+    mpd_uint_t rnd;
+    mpd_ssize_t size;
+
+    assert(!mpd_isspecial(a));
+    assert(n >= 0);
+
+    if (mpd_iszerocoeff(a) || n == 0) {
+        if (!mpd_qcopy(result, a, status)) {
+            return MPD_UINT_MAX;
+        }
+        return 0;
+    }
+
+    if (n >= a->digits) {
+        rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits));
+        mpd_zerocoeff(result);
+    }
+    else {
+        result->digits = a->digits-n;
+        size = mpd_digits_to_size(result->digits);
+        if (result == a) {
+            rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+            /* reducing the size cannot fail */
+            mpd_qresize(result, size, status);
+        }
+        else {
+            if (!mpd_qresize(result, size, status)) {
+                return MPD_UINT_MAX;
+            }
+            rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+        }
+        result->len = size;
+    }
+
+    mpd_copy_flags(result, a);
+    result->exp = a->exp;
+
+    return rnd;
+}
+
+
+/******************************************************************************/
+/*                         Miscellaneous operations                           */
+/******************************************************************************/
+
+/* Logical And */
+void
+mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    const mpd_t *big = a, *small = b;
+    mpd_uint_t x, y, z, xbit, ybit;
+    int k, mswdigits;
+    mpd_ssize_t i;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b) ||
+        mpd_isnegative(a) || mpd_isnegative(b) ||
+        a->exp != 0 || b->exp != 0) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (b->digits > a->digits) {
+        big = b;
+        small = a;
+    }
+    if (!mpd_qresize(result, big->len, status)) {
+        return;
+    }
+
+
+    /* full words */
+    for (i = 0; i < small->len-1; i++) {
+        x = small->data[i];
+        y = big->data[i];
+        z = 0;
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            xbit = x % 10;
+            x /= 10;
+            ybit = y % 10;
+            y /= 10;
+            if (xbit > 1 || ybit > 1) {
+                goto invalid_operation;
+            }
+            z += (xbit&ybit) ? mpd_pow10[k] : 0;
+        }
+        result->data[i] = z;
+    }
+    /* most significant word of small */
+    x = small->data[i];
+    y = big->data[i];
+    z = 0;
+    mswdigits = mpd_word_digits(x);
+    for (k = 0; k < mswdigits; k++) {
+        xbit = x % 10;
+        x /= 10;
+        ybit = y % 10;
+        y /= 10;
+        if (xbit > 1 || ybit > 1) {
+            goto invalid_operation;
+        }
+        z += (xbit&ybit) ? mpd_pow10[k] : 0;
+    }
+    result->data[i++] = z;
+
+    /* scan the rest of y for digits > 1 */
+    for (; k < MPD_RDIGITS; k++) {
+        ybit = y % 10;
+        y /= 10;
+        if (ybit > 1) {
+            goto invalid_operation;
+        }
+    }
+    /* scan the rest of big for digits > 1 */
+    for (; i < big->len; i++) {
+        y = big->data[i];
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            ybit = y % 10;
+            y /= 10;
+            if (ybit > 1) {
+                goto invalid_operation;
+            }
+        }
+    }
+
+    mpd_clear_flags(result);
+    result->exp = 0;
+    result->len = _mpd_real_size(result->data, small->len);
+    mpd_qresize(result, result->len, status);
+    mpd_setdigits(result);
+    _mpd_cap(result, ctx);
+    return;
+
+invalid_operation:
+    mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/* Class of an operand. Returns a pointer to the constant name. */
+const char *
+mpd_class(const mpd_t *a, const mpd_context_t *ctx)
+{
+    if (mpd_isnan(a)) {
+        if (mpd_isqnan(a))
+            return "NaN";
+        else
+            return "sNaN";
+    }
+    else if (mpd_ispositive(a)) {
+        if (mpd_isinfinite(a))
+            return "+Infinity";
+        else if (mpd_iszero(a))
+            return "+Zero";
+        else if (mpd_isnormal(a, ctx))
+            return "+Normal";
+        else
+            return "+Subnormal";
+    }
+    else {
+        if (mpd_isinfinite(a))
+            return "-Infinity";
+        else if (mpd_iszero(a))
+            return "-Zero";
+        else if (mpd_isnormal(a, ctx))
+            return "-Normal";
+        else
+            return "-Subnormal";
+    }
+}
+
+/* Logical Xor */
+void
+mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+            uint32_t *status)
+{
+    mpd_uint_t x, z, xbit;
+    mpd_ssize_t i, digits, len;
+    mpd_ssize_t q, r;
+    int k;
+
+    if (mpd_isspecial(a) || mpd_isnegative(a) || a->exp != 0) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    digits = (a->digits < ctx->prec) ? ctx->prec : a->digits;
+    _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS);
+    len = (r == 0) ? q : q+1;
+    if (!mpd_qresize(result, len, status)) {
+        return;
+    }
+
+    for (i = 0; i < len; i++) {
+        x = (i < a->len) ? a->data[i] : 0;
+        z = 0;
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            xbit = x % 10;
+            x /= 10;
+            if (xbit > 1) {
+                goto invalid_operation;
+            }
+            z += !xbit ? mpd_pow10[k] : 0;
+        }
+        result->data[i] = z;
+    }
+
+    mpd_clear_flags(result);
+    result->exp = 0;
+    result->len = _mpd_real_size(result->data, len);
+    mpd_qresize(result, result->len, status);
+    mpd_setdigits(result);
+    _mpd_cap(result, ctx);
+    return;
+
+invalid_operation:
+    mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/* Exponent of the magnitude of the most significant digit of the operand. */
+void
+mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+          uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        mpd_setspecial(result, MPD_POS, MPD_INF);
+    }
+    else if (mpd_iszerocoeff(a)) {
+        mpd_setspecial(result, MPD_NEG, MPD_INF);
+        *status |= MPD_Division_by_zero;
+    }
+    else {
+        mpd_qset_ssize(result, mpd_adjexp(a), ctx, status);
+    }
+}
+
+/* Logical Or */
+void
+mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b,
+        const mpd_context_t *ctx, uint32_t *status)
+{
+    const mpd_t *big = a, *small = b;
+    mpd_uint_t x, y, z, xbit, ybit;
+    int k, mswdigits;
+    mpd_ssize_t i;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b) ||
+        mpd_isnegative(a) || mpd_isnegative(b) ||
+        a->exp != 0 || b->exp != 0) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (b->digits > a->digits) {
+        big = b;
+        small = a;
+    }
+    if (!mpd_qresize(result, big->len, status)) {
+        return;
+    }
+
+
+    /* full words */
+    for (i = 0; i < small->len-1; i++) {
+        x = small->data[i];
+        y = big->data[i];
+        z = 0;
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            xbit = x % 10;
+            x /= 10;
+            ybit = y % 10;
+            y /= 10;
+            if (xbit > 1 || ybit > 1) {
+                goto invalid_operation;
+            }
+            z += (xbit|ybit) ? mpd_pow10[k] : 0;
+        }
+        result->data[i] = z;
+    }
+    /* most significant word of small */
+    x = small->data[i];
+    y = big->data[i];
+    z = 0;
+    mswdigits = mpd_word_digits(x);
+    for (k = 0; k < mswdigits; k++) {
+        xbit = x % 10;
+        x /= 10;
+        ybit = y % 10;
+        y /= 10;
+        if (xbit > 1 || ybit > 1) {
+            goto invalid_operation;
+        }
+        z += (xbit|ybit) ? mpd_pow10[k] : 0;
+    }
+
+    /* scan for digits > 1 and copy the rest of y */
+    for (; k < MPD_RDIGITS; k++) {
+        ybit = y % 10;
+        y /= 10;
+        if (ybit > 1) {
+            goto invalid_operation;
+        }
+        z += ybit*mpd_pow10[k];
+    }
+    result->data[i++] = z;
+    /* scan for digits > 1 and copy the rest of big */
+    for (; i < big->len; i++) {
+        y = big->data[i];
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            ybit = y % 10;
+            y /= 10;
+            if (ybit > 1) {
+                goto invalid_operation;
+            }
+        }
+        result->data[i] = big->data[i];
+    }
+
+    mpd_clear_flags(result);
+    result->exp = 0;
+    result->len = _mpd_real_size(result->data, big->len);
+    mpd_qresize(result, result->len, status);
+    mpd_setdigits(result);
+    _mpd_cap(result, ctx);
+    return;
+
+invalid_operation:
+    mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/*
+ * Rotate the coefficient of 'a' by 'b' digits. 'b' must be an integer with
+ * exponent 0.
+ */
+void
+mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    MPD_NEW_STATIC(tmp,0,0,0,0);
+    MPD_NEW_STATIC(big,0,0,0,0);
+    MPD_NEW_STATIC(small,0,0,0,0);
+    mpd_ssize_t n, lshift, rshift;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+    }
+    if (b->exp != 0 || mpd_isinfinite(b)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    n = mpd_qget_ssize(b, &workstatus);
+    if (workstatus&MPD_Invalid_operation) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (n > ctx->prec || n < -ctx->prec) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_isinfinite(a)) {
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+    if (n >= 0) {
+        lshift = n;
+        rshift = ctx->prec-n;
+    }
+    else {
+        lshift = ctx->prec+n;
+        rshift = -n;
+    }
+
+    if (a->digits > ctx->prec) {
+        if (!mpd_qcopy(&tmp, a, status)) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            goto finish;
+        }
+        _mpd_cap(&tmp, ctx);
+        a = &tmp;
+    }
+
+    if (!mpd_qshiftl(&big, a, lshift, status)) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        goto finish;
+    }
+    _mpd_cap(&big, ctx);
+
+    if (mpd_qshiftr(&small, a, rshift, status) == MPD_UINT_MAX) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        goto finish;
+    }
+    _mpd_qadd(result, &big, &small, ctx, status);
+
+
+finish:
+    mpd_del(&tmp);
+    mpd_del(&big);
+    mpd_del(&small);
+}
+
+/*
+ * b must be an integer with exponent 0 and in the range +-2*(emax + prec).
+ * XXX: In my opinion +-(2*emax + prec) would be more sensible.
+ * The result is a with the value of b added to its exponent.
+ */
+void
+mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_uint_t n, maxjump;
+#ifndef LEGACY_COMPILER
+    int64_t exp;
+#else
+    mpd_uint_t x;
+    int x_sign, n_sign;
+    mpd_ssize_t exp;
+#endif
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+    }
+    if (b->exp != 0 || mpd_isinfinite(b)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    n = mpd_qabs_uint(b, &workstatus);
+    /* the spec demands this */
+    maxjump = 2 * (mpd_uint_t)(ctx->emax + ctx->prec);
+
+    if (n > maxjump || workstatus&MPD_Invalid_operation) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_isinfinite(a)) {
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+#ifndef LEGACY_COMPILER
+    exp = a->exp + (int64_t)n * mpd_arith_sign(b);
+    exp = (exp > MPD_EXP_INF) ? MPD_EXP_INF : exp;
+    exp = (exp < MPD_EXP_CLAMP) ? MPD_EXP_CLAMP : exp;
+#else
+    x = (a->exp < 0) ? -a->exp : a->exp;
+    x_sign = (a->exp < 0) ? 1 : 0;
+    n_sign = mpd_isnegative(b) ? 1 : 0;
+
+    if (x_sign == n_sign) {
+        x = x + n;
+        if (x < n) x = MPD_UINT_MAX;
+    }
+    else {
+        x_sign = (x >= n) ? x_sign : n_sign;
+        x = (x >= n) ? x - n : n - x;
+    }
+    if (!x_sign && x > MPD_EXP_INF) x = MPD_EXP_INF;
+    if (x_sign && x > -MPD_EXP_CLAMP) x = -MPD_EXP_CLAMP;
+    exp = x_sign ? -((mpd_ssize_t)x) : (mpd_ssize_t)x;
+#endif
+
+    mpd_qcopy(result, a, status);
+    result->exp = (mpd_ssize_t)exp;
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Shift the coefficient by n digits, positive n is a left shift. In the case
+ * of a left shift, the result is decapitated to fit the context precision. If
+ * you don't want that, use mpd_shiftl().
+ */
+void
+mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx,
+            uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+    if (n >= 0 && n <= ctx->prec) {
+        mpd_qshiftl(result, a, n, status);
+        _mpd_cap(result, ctx);
+    }
+    else if (n < 0 && n >= -ctx->prec) {
+        if (!mpd_qcopy(result, a, status)) {
+            return;
+        }
+        _mpd_cap(result, ctx);
+        mpd_qshiftr_inplace(result, -n);
+    }
+    else {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+    }
+}
+
+/*
+ * Same as mpd_shiftn(), but the shift is specified by the decimal b, which
+ * must be an integer with a zero exponent. Infinities remain infinities.
+ */
+void
+mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_ssize_t n;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+    }
+    if (b->exp != 0 || mpd_isinfinite(b)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    n = mpd_qget_ssize(b, &workstatus);
+    if (workstatus&MPD_Invalid_operation) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (n > ctx->prec || n < -ctx->prec) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_isinfinite(a)) {
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+    if (n >= 0) {
+        mpd_qshiftl(result, a, n, status);
+        _mpd_cap(result, ctx);
+    }
+    else {
+        if (!mpd_qcopy(result, a, status)) {
+            return;
+        }
+        _mpd_cap(result, ctx);
+        mpd_qshiftr_inplace(result, -n);
+    }
+}
+
+/* Logical Xor */
+void
+mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b,
+        const mpd_context_t *ctx, uint32_t *status)
+{
+    const mpd_t *big = a, *small = b;
+    mpd_uint_t x, y, z, xbit, ybit;
+    int k, mswdigits;
+    mpd_ssize_t i;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b) ||
+        mpd_isnegative(a) || mpd_isnegative(b) ||
+        a->exp != 0 || b->exp != 0) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (b->digits > a->digits) {
+        big = b;
+        small = a;
+    }
+    if (!mpd_qresize(result, big->len, status)) {
+        return;
+    }
+
+
+    /* full words */
+    for (i = 0; i < small->len-1; i++) {
+        x = small->data[i];
+        y = big->data[i];
+        z = 0;
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            xbit = x % 10;
+            x /= 10;
+            ybit = y % 10;
+            y /= 10;
+            if (xbit > 1 || ybit > 1) {
+                goto invalid_operation;
+            }
+            z += (xbit^ybit) ? mpd_pow10[k] : 0;
+        }
+        result->data[i] = z;
+    }
+    /* most significant word of small */
+    x = small->data[i];
+    y = big->data[i];
+    z = 0;
+    mswdigits = mpd_word_digits(x);
+    for (k = 0; k < mswdigits; k++) {
+        xbit = x % 10;
+        x /= 10;
+        ybit = y % 10;
+        y /= 10;
+        if (xbit > 1 || ybit > 1) {
+            goto invalid_operation;
+        }
+        z += (xbit^ybit) ? mpd_pow10[k] : 0;
+    }
+
+    /* scan for digits > 1 and copy the rest of y */
+    for (; k < MPD_RDIGITS; k++) {
+        ybit = y % 10;
+        y /= 10;
+        if (ybit > 1) {
+            goto invalid_operation;
+        }
+        z += ybit*mpd_pow10[k];
+    }
+    result->data[i++] = z;
+    /* scan for digits > 1 and copy the rest of big */
+    for (; i < big->len; i++) {
+        y = big->data[i];
+        for (k = 0; k < MPD_RDIGITS; k++) {
+            ybit = y % 10;
+            y /= 10;
+            if (ybit > 1) {
+                goto invalid_operation;
+            }
+        }
+        result->data[i] = big->data[i];
+    }
+
+    mpd_clear_flags(result);
+    result->exp = 0;
+    result->len = _mpd_real_size(result->data, big->len);
+    mpd_qresize(result, result->len, status);
+    mpd_setdigits(result);
+    _mpd_cap(result, ctx);
+    return;
+
+invalid_operation:
+    mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+
+/******************************************************************************/
+/*                         Arithmetic operations                              */
+/******************************************************************************/
+
+/*
+ * The absolute value of a. If a is negative, the result is the same
+ * as the result of the minus operation. Otherwise, the result is the
+ * result of the plus operation.
+ */
+void
+mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+         uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+    }
+
+    if (mpd_isnegative(a)) {
+        mpd_qminus(result, a, ctx, status);
+    }
+    else {
+        mpd_qplus(result, a, ctx, status);
+    }
+}
+
+static inline void
+_mpd_ptrswap(const mpd_t **a, const mpd_t **b)
+{
+    const mpd_t *t = *a;
+    *a = *b;
+    *b = t;
+}
+
+/* Add or subtract infinities. */
+static void
+_mpd_qaddsub_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b,
+                 uint32_t *status)
+{
+    if (mpd_isinfinite(a)) {
+        if (mpd_sign(a) != sign_b && mpd_isinfinite(b)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+        }
+        else {
+            mpd_setspecial(result, mpd_sign(a), MPD_INF);
+        }
+        return;
+    }
+    assert(mpd_isinfinite(b));
+    mpd_setspecial(result, sign_b, MPD_INF);
+}
+
+/* Add or subtract non-special numbers. */
+static void
+_mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    const mpd_t *big, *small;
+    MPD_NEW_STATIC(big_aligned,0,0,0,0);
+    MPD_NEW_CONST(tiny,0,0,1,1,1,1);
+    mpd_uint_t carry;
+    mpd_ssize_t newsize, shift;
+    mpd_ssize_t exp, i;
+    int swap = 0;
+
+
+    /* compare exponents */
+    big = a; small = b;
+    if (big->exp != small->exp) {
+        if (small->exp > big->exp) {
+            _mpd_ptrswap(&big, &small);
+            swap++;
+        }
+        /* align the coefficients */
+        if (!mpd_iszerocoeff(big)) {
+            exp = big->exp - 1;
+            exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1;
+            if (mpd_adjexp(small) < exp) {
+                /*
+                 * Avoid huge shifts by substituting a value for small that is
+                 * guaranteed to produce the same results.
+                 *
+                 * adjexp(small) < exp if and only if:
+                 *
+                 *   bdigits <= prec AND
+                 *   bdigits+shift >= prec+2+sdigits AND
+                 *   exp = bexp+bdigits-prec-2
+                 *
+                 *     1234567000000000  ->  bdigits + shift
+                 *     ----------XX1234  ->  sdigits
+                 *     ----------X1      ->  tiny-digits
+                 *     |- prec -|
+                 *
+                 *      OR
+                 *
+                 *   bdigits > prec AND
+                 *   shift > sdigits AND
+                 *   exp = bexp-1
+                 *
+                 *     1234567892100000  ->  bdigits + shift
+                 *     ----------XX1234  ->  sdigits
+                 *     ----------X1      ->  tiny-digits
+                 *     |- prec -|
+                 *
+                 * If tiny is zero, adding or subtracting is a no-op.
+                 * Otherwise, adding tiny generates a non-zero digit either
+                 * below the rounding digit or the least significant digit
+                 * of big. When subtracting, tiny is in the same position as
+                 * the carry that would be generated by subtracting sdigits.
+                 */
+                mpd_copy_flags(&tiny, small);
+                tiny.exp = exp;
+                tiny.digits = 1;
+                tiny.len = 1;
+                tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1;
+                small = &tiny;
+            }
+            /* This cannot wrap: the difference is positive and <= maxprec */
+            shift = big->exp - small->exp;
+            if (!mpd_qshiftl(&big_aligned, big, shift, status)) {
+                mpd_seterror(result, MPD_Malloc_error, status);
+                goto finish;
+            }
+            big = &big_aligned;
+        }
+    }
+    result->exp = small->exp;
+
+
+    /* compare length of coefficients */
+    if (big->len < small->len) {
+        _mpd_ptrswap(&big, &small);
+        swap++;
+    }
+
+    newsize = big->len;
+    if (!mpd_qresize(result, newsize, status)) {
+        goto finish;
+    }
+
+    if (mpd_sign(a) == sign_b) {
+
+        carry = _mpd_baseadd(result->data, big->data, small->data,
+                             big->len, small->len);
+
+        if (carry) {
+            newsize = big->len + 1;
+            if (!mpd_qresize(result, newsize, status)) {
+                goto finish;
+            }
+            result->data[newsize-1] = carry;
+        }
+
+        result->len = newsize;
+        mpd_set_flags(result, sign_b);
+    }
+    else {
+        if (big->len == small->len) {
+            for (i=big->len-1; i >= 0; --i) {
+                if (big->data[i] != small->data[i]) {
+                    if (big->data[i] < small->data[i]) {
+                        _mpd_ptrswap(&big, &small);
+                        swap++;
+                    }
+                    break;
+                }
+            }
+        }
+
+        _mpd_basesub(result->data, big->data, small->data,
+                     big->len, small->len);
+        newsize = _mpd_real_size(result->data, big->len);
+        /* resize to smaller cannot fail */
+        (void)mpd_qresize(result, newsize, status);
+
+        result->len = newsize;
+        sign_b = (swap & 1) ? sign_b : mpd_sign(a);
+        mpd_set_flags(result, sign_b);
+
+        if (mpd_iszerocoeff(result)) {
+            mpd_set_positive(result);
+            if (ctx->round == MPD_ROUND_FLOOR) {
+                mpd_set_negative(result);
+            }
+        }
+    }
+
+    mpd_setdigits(result);
+
+finish:
+    mpd_del(&big_aligned);
+}
+
+/* Add a and b. No specials, no finalizing. */
+static void
+_mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+          const mpd_context_t *ctx, uint32_t *status)
+{
+    _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status);
+}
+
+/* Subtract b from a. No specials, no finalizing. */
+static void
+_mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b,
+          const mpd_context_t *ctx, uint32_t *status)
+{
+     _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status);
+}
+
+/* Add a and b. */
+void
+mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+        _mpd_qaddsub_inf(result, a, b, mpd_sign(b), status);
+        return;
+    }
+
+    _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* Add a and b. Set NaN/Invalid_operation if the result is inexact. */
+static void
+_mpd_qadd_exact(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+
+    mpd_qadd(result, a, b, ctx, &workstatus);
+    *status |= workstatus;
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+    }
+}
+
+/* Subtract b from a. */
+void
+mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+        _mpd_qaddsub_inf(result, a, b, !mpd_sign(b), status);
+        return;
+    }
+
+    _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* Subtract b from a. Set NaN/Invalid_operation if the result is inexact. */
+static void
+_mpd_qsub_exact(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+
+    mpd_qsub(result, a, b, ctx, &workstatus);
+    *status |= workstatus;
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+    }
+}
+
+/* Add decimal and mpd_ssize_t. */
+void
+mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+               const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_ssize(&bb, b, &maxcontext, status);
+    mpd_qadd(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Add decimal and mpd_uint_t. */
+void
+mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_uint(&bb, b, &maxcontext, status);
+    mpd_qadd(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Subtract mpd_ssize_t from decimal. */
+void
+mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+               const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_ssize(&bb, b, &maxcontext, status);
+    mpd_qsub(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Subtract mpd_uint_t from decimal. */
+void
+mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_uint(&bb, b, &maxcontext, status);
+    mpd_qsub(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Add decimal and int32_t. */
+void
+mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qadd_ssize(result, a, b, ctx, status);
+}
+
+/* Add decimal and uint32_t. */
+void
+mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qadd_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Add decimal and int64_t. */
+void
+mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qadd_ssize(result, a, b, ctx, status);
+}
+
+/* Add decimal and uint64_t. */
+void
+mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qadd_uint(result, a, b, ctx, status);
+}
+#elif !defined(LEGACY_COMPILER)
+/* Add decimal and int64_t. */
+void
+mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_i64(&bb, b, &maxcontext, status);
+    mpd_qadd(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Add decimal and uint64_t. */
+void
+mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_u64(&bb, b, &maxcontext, status);
+    mpd_qadd(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+#endif
+
+/* Subtract int32_t from decimal. */
+void
+mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qsub_ssize(result, a, b, ctx, status);
+}
+
+/* Subtract uint32_t from decimal. */
+void
+mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qsub_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Subtract int64_t from decimal. */
+void
+mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qsub_ssize(result, a, b, ctx, status);
+}
+
+/* Subtract uint64_t from decimal. */
+void
+mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qsub_uint(result, a, b, ctx, status);
+}
+#elif !defined(LEGACY_COMPILER)
+/* Subtract int64_t from decimal. */
+void
+mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_i64(&bb, b, &maxcontext, status);
+    mpd_qsub(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Subtract uint64_t from decimal. */
+void
+mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_u64(&bb, b, &maxcontext, status);
+    mpd_qsub(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+#endif
+
+
+/* Divide infinities. */
+static void
+_mpd_qdiv_inf(mpd_t *result, const mpd_t *a, const mpd_t *b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    if (mpd_isinfinite(a)) {
+        if (mpd_isinfinite(b)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+        return;
+    }
+    assert(mpd_isinfinite(b));
+    _settriple(result, mpd_sign(a)^mpd_sign(b), 0, mpd_etiny(ctx));
+    *status |= MPD_Clamped;
+}
+
+enum {NO_IDEAL_EXP, SET_IDEAL_EXP};
+/* Divide a by b. */
+static void
+_mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b,
+          const mpd_context_t *ctx, uint32_t *status)
+{
+    MPD_NEW_STATIC(aligned,0,0,0,0);
+    mpd_uint_t ld;
+    mpd_ssize_t shift, exp, tz;
+    mpd_ssize_t newsize;
+    mpd_ssize_t ideal_exp;
+    mpd_uint_t rem;
+    uint8_t sign_a = mpd_sign(a);
+    uint8_t sign_b = mpd_sign(b);
+
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+            return;
+        }
+        _mpd_qdiv_inf(q, a, b, ctx, status);
+        return;
+    }
+    if (mpd_iszerocoeff(b)) {
+        if (mpd_iszerocoeff(a)) {
+            mpd_seterror(q, MPD_Division_undefined, status);
+        }
+        else {
+            mpd_setspecial(q, sign_a^sign_b, MPD_INF);
+            *status |= MPD_Division_by_zero;
+        }
+        return;
+    }
+    if (mpd_iszerocoeff(a)) {
+        exp = a->exp - b->exp;
+        _settriple(q, sign_a^sign_b, 0, exp);
+        mpd_qfinalize(q, ctx, status);
+        return;
+    }
+
+    shift = (b->digits - a->digits) + ctx->prec + 1;
+    ideal_exp = a->exp - b->exp;
+    exp = ideal_exp - shift;
+    if (shift > 0) {
+        if (!mpd_qshiftl(&aligned, a, shift, status)) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto finish;
+        }
+        a = &aligned;
+    }
+    else if (shift < 0) {
+        shift = -shift;
+        if (!mpd_qshiftl(&aligned, b, shift, status)) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto finish;
+        }
+        b = &aligned;
+    }
+
+
+    newsize = a->len - b->len + 1;
+    if ((q != b && q != a) || (q == b && newsize > b->len)) {
+        if (!mpd_qresize(q, newsize, status)) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto finish;
+        }
+    }
+
+
+    if (b->len == 1) {
+        rem = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]);
+    }
+    else if (b->len <= MPD_NEWTONDIV_CUTOFF) {
+        int ret = _mpd_basedivmod(q->data, NULL, a->data, b->data,
+                                  a->len, b->len);
+        if (ret < 0) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto finish;
+        }
+        rem = ret;
+    }
+    else {
+        MPD_NEW_STATIC(r,0,0,0,0);
+        _mpd_base_ndivmod(q, &r, a, b, status);
+        if (mpd_isspecial(q) || mpd_isspecial(&r)) {
+            mpd_setspecial(q, MPD_POS, MPD_NAN);
+            mpd_del(&r);
+            goto finish;
+        }
+        rem = !mpd_iszerocoeff(&r);
+        mpd_del(&r);
+        newsize = q->len;
+    }
+
+    newsize = _mpd_real_size(q->data, newsize);
+    /* resize to smaller cannot fail */
+    mpd_qresize(q, newsize, status);
+    mpd_set_flags(q, sign_a^sign_b);
+    q->len = newsize;
+    mpd_setdigits(q);
+
+    shift = ideal_exp - exp;
+    if (rem) {
+        ld = mpd_lsd(q->data[0]);
+        if (ld == 0 || ld == 5) {
+            q->data[0] += 1;
+        }
+    }
+    else if (action == SET_IDEAL_EXP && shift > 0) {
+        tz = mpd_trail_zeros(q);
+        shift = (tz > shift) ? shift : tz;
+        mpd_qshiftr_inplace(q, shift);
+        exp += shift;
+    }
+
+    q->exp = exp;
+
+
+finish:
+    mpd_del(&aligned);
+    mpd_qfinalize(q, ctx, status);
+}
+
+/* Divide a by b. */
+void
+mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    MPD_NEW_STATIC(aa,0,0,0,0);
+    MPD_NEW_STATIC(bb,0,0,0,0);
+    uint32_t xstatus = 0;
+
+    if (q == a) {
+        if (!mpd_qcopy(&aa, a, status)) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto out;
+        }
+        a = &aa;
+    }
+
+    if (q == b) {
+        if (!mpd_qcopy(&bb, b, status)) {
+            mpd_seterror(q, MPD_Malloc_error, status);
+            goto out;
+        }
+        b = &bb;
+    }
+
+    _mpd_qdiv(SET_IDEAL_EXP, q, a, b, ctx, &xstatus);
+
+    if (xstatus & (MPD_Malloc_error|MPD_Division_impossible)) {
+        /* Inexact quotients (the usual case) fill the entire context precision,
+         * which can lead to the above errors for very high precisions. Retry
+         * the operation with a lower precision in case the result is exact.
+         *
+         * We need an upper bound for the number of digits of a_coeff / b_coeff
+         * when the result is exact.  If a_coeff' * 1 / b_coeff' is in lowest
+         * terms, then maxdigits(a_coeff') + maxdigits(1 / b_coeff') is a suitable
+         * bound.
+         *
+         * 1 / b_coeff' is exact iff b_coeff' exclusively has prime factors 2 or 5.
+         * The largest amount of digits is generated if b_coeff' is a power of 2 or
+         * a power of 5 and is less than or equal to log5(b_coeff') <= log2(b_coeff').
+         *
+         * We arrive at a total upper bound:
+         *
+         *   maxdigits(a_coeff') + maxdigits(1 / b_coeff') <=
+         *   log10(a_coeff) + log2(b_coeff) =
+         *   log10(a_coeff) + log10(b_coeff) / log10(2) <=
+         *   a->digits + b->digits * 4;
+         */
+        uint32_t ystatus = 0;
+        mpd_context_t workctx;
+        mpd_workcontext(&workctx, ctx);
+
+        workctx.prec = a->digits + b->digits * 4;
+        if (workctx.prec >= ctx->prec) {
+            *status |= (xstatus&MPD_Errors);
+            goto out;  /* No point in retrying, keep the original error. */
+        }
+
+        _mpd_qdiv(SET_IDEAL_EXP, q, a, b, &workctx, &ystatus);
+        if (ystatus != 0) {
+            ystatus = *status | ((ystatus|xstatus)&MPD_Errors);
+            mpd_seterror(q, ystatus, status);
+        }
+    }
+    else {
+        *status |= xstatus;
+    }
+
+
+out:
+    mpd_del(&aa);
+    mpd_del(&bb);
+}
+
+/* Internal function. */
+static void
+_mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    MPD_NEW_STATIC(aligned,0,0,0,0);
+    mpd_ssize_t qsize, rsize;
+    mpd_ssize_t ideal_exp, expdiff, shift;
+    uint8_t sign_a = mpd_sign(a);
+    uint8_t sign_ab = mpd_sign(a)^mpd_sign(b);
+
+
+    ideal_exp = (a->exp > b->exp) ?  b->exp : a->exp;
+    if (mpd_iszerocoeff(a)) {
+        if (!mpd_qcopy(r, a, status)) {
+            goto nanresult; /* GCOV_NOT_REACHED */
+        }
+        r->exp = ideal_exp;
+        _settriple(q, sign_ab, 0, 0);
+        return;
+    }
+
+    expdiff = mpd_adjexp(a) - mpd_adjexp(b);
+    if (expdiff < 0) {
+        if (a->exp > b->exp) {
+            /* positive and less than b->digits - a->digits */
+            shift = a->exp - b->exp;
+            if (!mpd_qshiftl(r, a, shift, status)) {
+                goto nanresult;
+            }
+            r->exp = ideal_exp;
+        }
+        else {
+            if (!mpd_qcopy(r, a, status)) {
+                goto nanresult;
+            }
+        }
+        _settriple(q, sign_ab, 0, 0);
+        return;
+    }
+    if (expdiff > ctx->prec) {
+        *status |= MPD_Division_impossible;
+        goto nanresult;
+    }
+
+
+    /*
+     * At this point we have:
+     *   (1) 0 <= a->exp + a->digits - b->exp - b->digits <= prec
+     *   (2) a->exp - b->exp >= b->digits - a->digits
+     *   (3) a->exp - b->exp <= prec + b->digits - a->digits
+     */
+    if (a->exp != b->exp) {
+        shift = a->exp - b->exp;
+        if (shift > 0) {
+            /* by (3), after the shift a->digits <= prec + b->digits */
+            if (!mpd_qshiftl(&aligned, a, shift, status)) {
+                goto nanresult;
+            }
+            a = &aligned;
+        }
+        else  {
+            shift = -shift;
+            /* by (2), after the shift b->digits <= a->digits */
+            if (!mpd_qshiftl(&aligned, b, shift, status)) {
+                goto nanresult;
+            }
+            b = &aligned;
+        }
+    }
+
+
+    qsize = a->len - b->len + 1;
+    if (!(q == a && qsize < a->len) && !(q == b && qsize < b->len)) {
+        if (!mpd_qresize(q, qsize, status)) {
+            goto nanresult;
+        }
+    }
+
+    rsize = b->len;
+    if (!(r == a && rsize < a->len)) {
+        if (!mpd_qresize(r, rsize, status)) {
+            goto nanresult;
+        }
+    }
+
+    if (b->len == 1) {
+        assert(b->data[0] != 0); /* annotation for scan-build */
+        if (a->len == 1) {
+            _mpd_div_word(&q->data[0], &r->data[0], a->data[0], b->data[0]);
+        }
+        else {
+            r->data[0] = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]);
+        }
+    }
+    else if (b->len <= MPD_NEWTONDIV_CUTOFF) {
+        int ret;
+        ret = _mpd_basedivmod(q->data, r->data, a->data, b->data,
+                              a->len, b->len);
+        if (ret == -1) {
+            *status |= MPD_Malloc_error;
+            goto nanresult;
+        }
+    }
+    else {
+        _mpd_base_ndivmod(q, r, a, b, status);
+        if (mpd_isspecial(q) || mpd_isspecial(r)) {
+            goto nanresult;
+        }
+        qsize = q->len;
+        rsize = r->len;
+    }
+
+    qsize = _mpd_real_size(q->data, qsize);
+    /* resize to smaller cannot fail */
+    mpd_qresize(q, qsize, status);
+    q->len = qsize;
+    mpd_setdigits(q);
+    mpd_set_flags(q, sign_ab);
+    q->exp = 0;
+    if (q->digits > ctx->prec) {
+        *status |= MPD_Division_impossible;
+        goto nanresult;
+    }
+
+    rsize = _mpd_real_size(r->data, rsize);
+    /* resize to smaller cannot fail */
+    mpd_qresize(r, rsize, status);
+    r->len = rsize;
+    mpd_setdigits(r);
+    mpd_set_flags(r, sign_a);
+    r->exp = ideal_exp;
+
+out:
+    mpd_del(&aligned);
+    return;
+
+nanresult:
+    mpd_setspecial(q, MPD_POS, MPD_NAN);
+    mpd_setspecial(r, MPD_POS, MPD_NAN);
+    goto out;
+}
+
+/* Integer division with remainder. */
+void
+mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    uint8_t sign = mpd_sign(a)^mpd_sign(b);
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+            mpd_qcopy(r, q, status);
+            return;
+        }
+        if (mpd_isinfinite(a)) {
+            if (mpd_isinfinite(b)) {
+                mpd_setspecial(q, MPD_POS, MPD_NAN);
+            }
+            else {
+                mpd_setspecial(q, sign, MPD_INF);
+            }
+            mpd_setspecial(r, MPD_POS, MPD_NAN);
+            *status |= MPD_Invalid_operation;
+            return;
+        }
+        if (mpd_isinfinite(b)) {
+            if (!mpd_qcopy(r, a, status)) {
+                mpd_seterror(q, MPD_Malloc_error, status);
+                return;
+            }
+            mpd_qfinalize(r, ctx, status);
+            _settriple(q, sign, 0, 0);
+            return;
+        }
+        /* debug */
+        abort(); /* GCOV_NOT_REACHED */
+    }
+    if (mpd_iszerocoeff(b)) {
+        if (mpd_iszerocoeff(a)) {
+            mpd_setspecial(q, MPD_POS, MPD_NAN);
+            mpd_setspecial(r, MPD_POS, MPD_NAN);
+            *status |= MPD_Division_undefined;
+        }
+        else {
+            mpd_setspecial(q, sign, MPD_INF);
+            mpd_setspecial(r, MPD_POS, MPD_NAN);
+            *status |= (MPD_Division_by_zero|MPD_Invalid_operation);
+        }
+        return;
+    }
+
+    _mpd_qdivmod(q, r, a, b, ctx, status);
+    mpd_qfinalize(q, ctx, status);
+    mpd_qfinalize(r, ctx, status);
+}
+
+void
+mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    MPD_NEW_STATIC(r,0,0,0,0);
+    uint8_t sign = mpd_sign(a)^mpd_sign(b);
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+            return;
+        }
+        if (mpd_isinfinite(a) && mpd_isinfinite(b)) {
+            mpd_seterror(q, MPD_Invalid_operation, status);
+            return;
+        }
+        if (mpd_isinfinite(a)) {
+            mpd_setspecial(q, sign, MPD_INF);
+            return;
+        }
+        if (mpd_isinfinite(b)) {
+            _settriple(q, sign, 0, 0);
+            return;
+        }
+        /* debug */
+        abort(); /* GCOV_NOT_REACHED */
+    }
+    if (mpd_iszerocoeff(b)) {
+        if (mpd_iszerocoeff(a)) {
+            mpd_seterror(q, MPD_Division_undefined, status);
+        }
+        else {
+            mpd_setspecial(q, sign, MPD_INF);
+            *status |= MPD_Division_by_zero;
+        }
+        return;
+    }
+
+
+    _mpd_qdivmod(q, &r, a, b, ctx, status);
+    mpd_del(&r);
+    mpd_qfinalize(q, ctx, status);
+}
+
+/* Divide decimal by mpd_ssize_t. */
+void
+mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+               const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_ssize(&bb, b, &maxcontext, status);
+    mpd_qdiv(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Divide decimal by mpd_uint_t. */
+void
+mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_uint(&bb, b, &maxcontext, status);
+    mpd_qdiv(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Divide decimal by int32_t. */
+void
+mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qdiv_ssize(result, a, b, ctx, status);
+}
+
+/* Divide decimal by uint32_t. */
+void
+mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qdiv_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Divide decimal by int64_t. */
+void
+mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qdiv_ssize(result, a, b, ctx, status);
+}
+
+/* Divide decimal by uint64_t. */
+void
+mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qdiv_uint(result, a, b, ctx, status);
+}
+#elif !defined(LEGACY_COMPILER)
+/* Divide decimal by int64_t. */
+void
+mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_i64(&bb, b, &maxcontext, status);
+    mpd_qdiv(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Divide decimal by uint64_t. */
+void
+mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_u64(&bb, b, &maxcontext, status);
+    mpd_qdiv(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+#endif
+
+/* Pad the result with trailing zeros if it has fewer digits than prec. */
+static void
+_mpd_zeropad(mpd_t *result, const mpd_context_t *ctx, uint32_t *status)
+{
+    if (!mpd_isspecial(result) && !mpd_iszero(result) &&
+        result->digits < ctx->prec) {
+       mpd_ssize_t shift = ctx->prec - result->digits;
+       mpd_qshiftl(result, result, shift, status);
+       result->exp -= shift;
+    }
+}
+
+/* Check if the result is guaranteed to be one. */
+static int
+_mpd_qexp_check_one(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+                    uint32_t *status)
+{
+    MPD_NEW_CONST(lim,0,-(ctx->prec+1),1,1,1,9);
+    MPD_NEW_SHARED(aa, a);
+
+    mpd_set_positive(&aa);
+
+    /* abs(a) <= 9 * 10**(-prec-1) */
+    if (_mpd_cmp(&aa, &lim) <= 0) {
+        _settriple(result, 0, 1, 0);
+        *status |= MPD_Rounded|MPD_Inexact;
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Get the number of iterations for the Horner scheme in _mpd_qexp().
+ */
+static inline mpd_ssize_t
+_mpd_get_exp_iterations(const mpd_t *r, mpd_ssize_t p)
+{
+    mpd_ssize_t log10pbyr; /* lower bound for log10(p / abs(r)) */
+    mpd_ssize_t n;
+
+    assert(p >= 10);
+    assert(!mpd_iszero(r));
+    assert(-p < mpd_adjexp(r) && mpd_adjexp(r) <= -1);
+
+#ifdef CONFIG_64
+    if (p > (mpd_ssize_t)(1ULL<<52)) {
+        return MPD_SSIZE_MAX;
+    }
+#endif
+
+    /*
+     * Lower bound for log10(p / abs(r)): adjexp(p) - (adjexp(r) + 1)
+     * At this point (for CONFIG_64, CONFIG_32 is not problematic):
+     *    1) 10 <= p <= 2**52
+     *    2) -p < adjexp(r) <= -1
+     *    3) 1 <= log10pbyr <= 2**52 + 14
+     */
+    log10pbyr = (mpd_word_digits(p)-1) - (mpd_adjexp(r)+1);
+
+    /*
+     * The numerator in the paper is 1.435 * p - 1.182, calculated
+     * exactly. We compensate for rounding errors by using 1.43503.
+     * ACL2 proofs:
+     *    1) exp-iter-approx-lower-bound: The term below evaluated
+     *       in 53-bit floating point arithmetic is greater than or
+     *       equal to the exact term used in the paper.
+     *    2) exp-iter-approx-upper-bound: The term below is less than
+     *       or equal to 3/2 * p <= 3/2 * 2**52.
+     */
+    n = (mpd_ssize_t)ceil((1.43503*(double)p - 1.182) / (double)log10pbyr);
+    return n >= 3 ? n : 3;
+}
+
+/*
+ * Internal function, specials have been dealt with. Apart from Overflow
+ * and Underflow, two cases must be considered for the error of the result:
+ *
+ *   1) abs(a) <= 9 * 10**(-prec-1)  ==>  result == 1
+ *
+ *      Absolute error: abs(1 - e**x) < 10**(-prec)
+ *      -------------------------------------------
+ *
+ *   2) abs(a) > 9 * 10**(-prec-1)
+ *
+ *      Relative error: abs(result - e**x) < 0.5 * 10**(-prec) * e**x
+ *      -------------------------------------------------------------
+ *
+ * The algorithm is from Hull&Abrham, Variable Precision Exponential Function,
+ * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986.
+ *
+ * Main differences:
+ *
+ *  - The number of iterations for the Horner scheme is calculated using
+ *    53-bit floating point arithmetic.
+ *
+ *  - In the error analysis for ER (relative error accumulated in the
+ *    evaluation of the truncated series) the reduced operand r may
+ *    have any number of digits.
+ *    ACL2 proof: exponent-relative-error
+ *
+ *  - The analysis for early abortion has been adapted for the mpd_t
+ *    ranges.
+ */
+static int
+_mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+          uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_STATIC(tmp,0,0,0,0);
+    MPD_NEW_STATIC(sum,0,0,0,0);
+    MPD_NEW_CONST(word,0,0,1,1,1,1);
+    mpd_ssize_t j, n, t;
+
+    assert(!mpd_isspecial(a));
+
+    if (mpd_iszerocoeff(a)) {
+        _settriple(result, MPD_POS, 1, 0);
+        return 1;
+    }
+
+    /*
+     * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where abs(r) < 1 and t >= 0.
+     *
+     * If t > 0, we have:
+     *
+     *   (1) 0.1 <= r < 1, so e^0.1 <= e^r. If t > MAX_T, overflow occurs:
+     *
+     *     MAX-EMAX+1 < log10(e^(0.1*10*t)) <= log10(e^(r*10^t)) < adjexp(e^(r*10^t))+1
+     *
+     *   (2) -1 < r <= -0.1, so e^r <= e^-0.1. If t > MAX_T, underflow occurs:
+     *
+     *     adjexp(e^(r*10^t)) <= log10(e^(r*10^t)) <= log10(e^(-0.1*10^t)) < MIN-ETINY
+     */
+#if defined(CONFIG_64)
+    #define MPD_EXP_MAX_T 19
+#elif defined(CONFIG_32)
+    #define MPD_EXP_MAX_T 10
+#endif
+    t = a->digits + a->exp;
+    t = (t > 0) ? t : 0;
+    if (t > MPD_EXP_MAX_T) {
+        if (mpd_ispositive(a)) {
+            mpd_setspecial(result, MPD_POS, MPD_INF);
+            *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+        }
+        else {
+            _settriple(result, MPD_POS, 0, mpd_etiny(ctx));
+            *status |= (MPD_Inexact|MPD_Rounded|MPD_Subnormal|
+                        MPD_Underflow|MPD_Clamped);
+        }
+        return 1;
+    }
+
+    /* abs(a) <= 9 * 10**(-prec-1) */
+    if (_mpd_qexp_check_one(result, a, ctx, status)) {
+        return 1;
+    }
+
+    mpd_maxcontext(&workctx);
+    workctx.prec = ctx->prec + t + 2;
+    workctx.prec = (workctx.prec < 10) ? 10 : workctx.prec;
+    workctx.round = MPD_ROUND_HALF_EVEN;
+
+    if (!mpd_qcopy(result, a, status)) {
+        return 1;
+    }
+    result->exp -= t;
+
+    /*
+     * At this point:
+     *    1) 9 * 10**(-prec-1) < abs(a)
+     *    2) 9 * 10**(-prec-t-1) < abs(r)
+     *    3) log10(9) - prec - t - 1 < log10(abs(r)) < adjexp(abs(r)) + 1
+     *    4) - prec - t - 2 < adjexp(abs(r)) <= -1
+     */
+    n = _mpd_get_exp_iterations(result, workctx.prec);
+    if (n == MPD_SSIZE_MAX) {
+        mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_UNLIKELY */
+        return 1; /* GCOV_UNLIKELY */
+    }
+
+    _settriple(&sum, MPD_POS, 1, 0);
+
+    for (j = n-1; j >= 1; j--) {
+        word.data[0] = j;
+        mpd_setdigits(&word);
+        mpd_qdiv(&tmp, result, &word, &workctx, &workctx.status);
+        mpd_qfma(&sum, &sum, &tmp, &one, &workctx, &workctx.status);
+    }
+
+#ifdef CONFIG_64
+    _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status);
+#else
+    if (t <= MPD_MAX_POW10) {
+        _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status);
+    }
+    else {
+        t -= MPD_MAX_POW10;
+        _mpd_qpow_uint(&tmp, &sum, mpd_pow10[MPD_MAX_POW10], MPD_POS,
+                       &workctx, status);
+        _mpd_qpow_uint(result, &tmp, mpd_pow10[t], MPD_POS, &workctx, status);
+    }
+#endif
+
+    mpd_del(&tmp);
+    mpd_del(&sum);
+    *status |= (workctx.status&MPD_Errors);
+    *status |= (MPD_Inexact|MPD_Rounded);
+
+    return 0;
+}
+
+/* exp(a) */
+void
+mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+         uint32_t *status)
+{
+    mpd_context_t workctx;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        if (mpd_isnegative(a)) {
+            _settriple(result, MPD_POS, 0, 0);
+        }
+        else {
+            mpd_setspecial(result, MPD_POS, MPD_INF);
+        }
+        return;
+    }
+    if (mpd_iszerocoeff(a)) {
+        _settriple(result, MPD_POS, 1, 0);
+        return;
+    }
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.round = MPD_ROUND_HALF_EVEN;
+
+    if (ctx->allcr) {
+        MPD_NEW_STATIC(hi, 0,0,0,0);
+        MPD_NEW_STATIC(lo, 0,0,0,0);
+        MPD_NEW_STATIC(ulp, 0,0,0,0);
+        MPD_NEW_STATIC(aa, 0,0,0,0);
+        uint32_t loop_protect = 0;
+        mpd_ssize_t prec;
+        mpd_ssize_t ulpexp;
+
+        if (result == a) {
+            if (!mpd_qcopy(&aa, a, status)) {
+                mpd_seterror(result, MPD_Malloc_error, status);
+                return;
+            }
+            a = &aa;
+        }
+
+        workctx.clamp = 0;
+        prec = ctx->prec + 3;
+        while (1) {
+            uint32_t status_res = 0;
+            uint32_t status_hi = 0;
+            uint32_t status_lo = 0;
+            int shortcut;
+            int bounds_eq;
+            int subnormal_eq;
+
+            workctx.prec = prec;
+            shortcut = _mpd_qexp(result, a, &workctx, &status_res);
+            if (mpd_isnan(result)) {
+                mpd_seterror(result, status_res, status);
+                break;
+            }
+            if (shortcut || mpd_isinfinite(result) || mpd_iszero(result)) {
+                *status |= status_res;
+                workctx.prec = ctx->prec;
+                workctx.clamp = ctx->clamp;
+                _mpd_zeropad(result, &workctx, status);
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            ulpexp = result->exp + result->digits - workctx.prec;
+            if (status_res & MPD_Underflow) {
+                /* The effective work precision is result->digits. */
+                ulpexp = result->exp;
+            }
+            _ssettriple(&ulp, MPD_POS, 1, ulpexp);
+
+            /*
+             * At this point [1]:
+             *   1) abs(result - e**x) < 0.5 * 10**(-prec) * e**x
+             *   2) result - ulp < e**x < result + ulp
+             *   3) result - ulp < result < result + ulp
+             *
+             * If round(result-ulp)==round(result+ulp), then
+             * round(result)==round(e**x). Therefore the result
+             * is correctly rounded.
+             *
+             * [1] If abs(a) <= 9 * 10**(-prec-1), use the absolute
+             *     error for a similar argument.
+             */
+            workctx.prec = ctx->prec;
+            mpd_qadd(&hi, result, &ulp, &workctx, &status_hi);
+            mpd_qsub(&lo, result, &ulp, &workctx, &status_lo);
+            if (mpd_isnan(&hi) || mpd_isnan(&lo)) {
+                mpd_seterror(result, status_hi|status_lo, status);
+                break;
+            }
+
+            subnormal_eq = (status_hi&MPD_Subnormal) == (status_lo&MPD_Subnormal);
+            bounds_eq = mpd_qcmp(&hi, &lo, status) == 0;
+            if (bounds_eq && ++loop_protect > 5) {
+                /* If the bounds are equal, the result is always correctly rounded.
+
+                   Resolving the subnormal status can take more iterations (around
+                   three) in extremely rare cases. 'hi' and 'lo' are so close that
+                   subnormal/underflow is largely cosmetic, so allow a maximum of
+                   five additional iterations. */
+                subnormal_eq = 1; /* GCOV_NOT_REACHED */
+            }
+
+            if (subnormal_eq && bounds_eq) {
+                *status |= status_lo;
+                workctx.clamp = ctx->clamp;
+                _mpd_zeropad(result, &workctx, status);
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            if (subnormal_eq) {
+                prec += MPD_RDIGITS;
+            }
+            else {
+                prec *= 2;
+            }
+            if (prec > MPD_MAX_PREC) {
+                mpd_seterror(result, MPD_Invalid_operation, status);
+                break;
+            }
+        }
+        mpd_del(&hi);
+        mpd_del(&lo);
+        mpd_del(&ulp);
+        mpd_del(&aa);
+    }
+    else {
+        _mpd_qexp(result, a, &workctx, status);
+        _mpd_zeropad(result, &workctx, status);
+        mpd_check_underflow(result, &workctx, status);
+        mpd_qfinalize(result, &workctx, status);
+    }
+}
+
+/* Fused multiply-add: (a * b) + c, with a single final rounding. */
+void
+mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_t *cc = NULL;
+
+    if (result == c) {
+        if ((cc = mpd_qncopy(c)) == NULL) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            return;
+        }
+        c = cc;
+    }
+
+    _mpd_qmul(result, a, b, ctx, &workstatus);
+    if (!(workstatus&MPD_Invalid_operation)) {
+        mpd_qadd(result, result, c, ctx, &workstatus);
+    }
+
+    if (cc) mpd_del(cc);
+    *status |= workstatus;
+}
+
+/*
+ * Schedule the optimal precision increase for the Newton iteration.
+ *   v := input operand
+ *   z_0 := initial approximation
+ *   initprec := natural number such that abs(log(v) - z_0) < 10**-initprec
+ *   maxprec := target precision
+ *
+ * For convenience the output klist contains the elements in reverse order:
+ *   klist := [k_n-1, ..., k_0], where
+ *     1) k_0 <= initprec and
+ *     2) abs(log(v) - result) < 10**(-2*k_n-1 + 1) <= 10**-maxprec.
+ */
+static inline int
+ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec,
+                 mpd_ssize_t initprec)
+{
+    mpd_ssize_t k;
+    int i;
+
+    assert(maxprec >= 2 && initprec >= 2);
+    if (maxprec <= initprec) return -1;
+
+    i = 0; k = maxprec;
+    do {
+        k = (k+2) / 2;
+        klist[i++] = k;
+    } while (k > initprec);
+
+    return i-1;
+}
+
+/* The constants have been verified with both decimal.py and mpfr. */
+#ifdef CONFIG_64
+#if MPD_RDIGITS != 19
+  #error "mpdecimal.c: MPD_RDIGITS must be 19."
+#endif
+static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = {
+  6983716328982174407ULL, 9089704281976336583ULL, 1515961135648465461ULL,
+  4416816335727555703ULL, 2900988039194170265ULL, 2307925037472986509ULL,
+   107598438319191292ULL, 3466624107184669231ULL, 4450099781311469159ULL,
+  9807828059751193854ULL, 7713456862091670584ULL, 1492198849978748873ULL,
+  6528728696511086257ULL, 2385392051446341972ULL, 8692180205189339507ULL,
+  6518769751037497088ULL, 2375253577097505395ULL, 9095610299291824318ULL,
+   982748238504564801ULL, 5438635917781170543ULL, 7547331541421808427ULL,
+   752371033310119785ULL, 3171643095059950878ULL, 9785265383207606726ULL,
+  2932258279850258550ULL, 5497347726624257094ULL, 2976979522110718264ULL,
+  9221477656763693866ULL, 1979650047149510504ULL, 6674183485704422507ULL,
+  9702766860595249671ULL, 9278096762712757753ULL, 9314848524948644871ULL,
+  6826928280848118428ULL,  754403708474699401ULL,  230105703089634572ULL,
+  1929203337658714166ULL, 7589402567763113569ULL, 4208241314695689016ULL,
+  2922455440575892572ULL, 9356734206705811364ULL, 2684916746550586856ULL,
+   644507064800027750ULL, 9476834636167921018ULL, 5659121373450747856ULL,
+  2835522011480466371ULL, 6470806855677432162ULL, 7141748003688084012ULL,
+  9619404400222105101ULL, 5504893431493939147ULL, 6674744042432743651ULL,
+  2287698219886746543ULL, 7773262884616336622ULL, 1985283935053089653ULL,
+  4680843799894826233ULL, 8168948290720832555ULL, 8067566662873690987ULL,
+  6248633409525465082ULL, 9829834196778404228ULL, 3524802359972050895ULL,
+  3327900967572609677ULL,  110148862877297603ULL,  179914546843642076ULL,
+  2302585092994045684ULL
+};
+#else
+#if MPD_RDIGITS != 9
+  #error "mpdecimal.c: MPD_RDIGITS must be 9."
+#endif
+static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = {
+  401682692UL, 708474699UL, 720754403UL,  30896345UL, 602301057UL, 765871416UL,
+  192920333UL, 763113569UL, 589402567UL, 956890167UL,  82413146UL, 589257242UL,
+  245544057UL, 811364292UL, 734206705UL, 868569356UL, 167465505UL, 775026849UL,
+  706480002UL,  18064450UL, 636167921UL, 569476834UL, 734507478UL, 156591213UL,
+  148046637UL, 283552201UL, 677432162UL, 470806855UL, 880840126UL, 417480036UL,
+  210510171UL, 940440022UL, 939147961UL, 893431493UL, 436515504UL, 440424327UL,
+  654366747UL, 821988674UL, 622228769UL, 884616336UL, 537773262UL, 350530896UL,
+  319852839UL, 989482623UL, 468084379UL, 720832555UL, 168948290UL, 736909878UL,
+  675666628UL, 546508280UL, 863340952UL, 404228624UL, 834196778UL, 508959829UL,
+   23599720UL, 967735248UL,  96757260UL, 603332790UL, 862877297UL, 760110148UL,
+  468436420UL, 401799145UL, 299404568UL, 230258509UL
+};
+#endif
+/* _mpd_ln10 is used directly for precisions smaller than MINALLOC_MAX*RDIGITS.
+   Otherwise, it serves as the initial approximation for calculating ln(10). */
+static const mpd_t _mpd_ln10 = {
+  MPD_STATIC|MPD_CONST_DATA, -(MPD_MINALLOC_MAX*MPD_RDIGITS-1),
+  MPD_MINALLOC_MAX*MPD_RDIGITS, MPD_MINALLOC_MAX, MPD_MINALLOC_MAX,
+  (mpd_uint_t *)mpd_ln10_data
+};
+
+/*
+ * Set 'result' to log(10).
+ *   Ulp error: abs(result - log(10)) < ulp(log(10))
+ *   Relative error: abs(result - log(10)) < 5 * 10**-prec * log(10)
+ *
+ * NOTE: The relative error is not derived from the ulp error, but
+ * calculated separately using the fact that 23/10 < log(10) < 24/10.
+ */
+void
+mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status)
+{
+    mpd_context_t varcontext, maxcontext;
+    MPD_NEW_STATIC(tmp, 0,0,0,0);
+    MPD_NEW_CONST(static10, 0,0,2,1,1,10);
+    mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+    mpd_uint_t rnd;
+    mpd_ssize_t shift;
+    int i;
+
+    assert(prec >= 1);
+
+    shift = MPD_MINALLOC_MAX*MPD_RDIGITS-prec;
+    shift = shift < 0 ? 0 : shift;
+
+    rnd = mpd_qshiftr(result, &_mpd_ln10, shift, status);
+    if (rnd == MPD_UINT_MAX) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        return;
+    }
+    result->exp = -(result->digits-1);
+
+    mpd_maxcontext(&maxcontext);
+    if (prec < MPD_MINALLOC_MAX*MPD_RDIGITS) {
+        maxcontext.prec = prec;
+        _mpd_apply_round_excess(result, rnd, &maxcontext, status);
+        *status |= (MPD_Inexact|MPD_Rounded);
+        return;
+    }
+
+    mpd_maxcontext(&varcontext);
+    varcontext.round = MPD_ROUND_TRUNC;
+
+    i = ln_schedule_prec(klist, prec+2, -result->exp);
+    for (; i >= 0; i--) {
+        varcontext.prec = 2*klist[i]+3;
+        result->flags ^= MPD_NEG;
+        _mpd_qexp(&tmp, result, &varcontext, status);
+        result->flags ^= MPD_NEG;
+        mpd_qmul(&tmp, &static10, &tmp, &varcontext, status);
+        mpd_qsub(&tmp, &tmp, &one, &maxcontext, status);
+        mpd_qadd(result, result, &tmp, &maxcontext, status);
+        if (mpd_isspecial(result)) {
+            break;
+        }
+    }
+
+    mpd_del(&tmp);
+    maxcontext.prec = prec;
+    mpd_qfinalize(result, &maxcontext, status);
+}
+
+/*
+ * Initial approximations for the ln() iteration. The values have the
+ * following properties (established with both decimal.py and mpfr):
+ *
+ * Index 0 - 400, logarithms of x in [1.00, 5.00]:
+ *   abs(lnapprox[i] * 10**-3 - log((i+100)/100)) < 10**-2
+ *   abs(lnapprox[i] * 10**-3 - log((i+1+100)/100)) < 10**-2
+ *
+ * Index 401 - 899, logarithms of x in (0.500, 0.999]:
+ *   abs(-lnapprox[i] * 10**-3 - log((i+100)/1000)) < 10**-2
+ *   abs(-lnapprox[i] * 10**-3 - log((i+1+100)/1000)) < 10**-2
+ */
+static const uint16_t lnapprox[900] = {
+  /* index 0 - 400: log((i+100)/100) * 1000 */
+  0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157,
+  166, 174, 182, 191, 199, 207, 215, 223, 231, 239, 247, 255, 262, 270, 278,
+  285, 293, 300, 308, 315, 322, 329, 336, 344, 351, 358, 365, 372, 378, 385,
+  392, 399, 406, 412, 419, 425, 432, 438, 445, 451, 457, 464, 470, 476, 482,
+  489, 495, 501, 507, 513, 519, 525, 531, 536, 542, 548, 554, 560, 565, 571,
+  577, 582, 588, 593, 599, 604, 610, 615, 621, 626, 631, 637, 642, 647, 652,
+  658, 663, 668, 673, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728,
+  732, 737, 742, 747, 751, 756, 761, 766, 770, 775, 779, 784, 788, 793, 798,
+  802, 806, 811, 815, 820, 824, 829, 833, 837, 842, 846, 850, 854, 859, 863,
+  867, 871, 876, 880, 884, 888, 892, 896, 900, 904, 908, 912, 916, 920, 924,
+  928, 932, 936, 940, 944, 948, 952, 956, 959, 963, 967, 971, 975, 978, 982,
+  986, 990, 993, 997, 1001, 1004, 1008, 1012, 1015, 1019, 1022, 1026, 1030,
+  1033, 1037, 1040, 1044, 1047, 1051, 1054, 1058, 1061, 1065, 1068, 1072, 1075,
+  1078, 1082, 1085, 1089, 1092, 1095, 1099, 1102, 1105, 1109, 1112, 1115, 1118,
+  1122, 1125, 1128, 1131, 1135, 1138, 1141, 1144, 1147, 1151, 1154, 1157, 1160,
+  1163, 1166, 1169, 1172, 1176, 1179, 1182, 1185, 1188, 1191, 1194, 1197, 1200,
+  1203, 1206, 1209, 1212, 1215, 1218, 1221, 1224, 1227, 1230, 1233, 1235, 1238,
+  1241, 1244, 1247, 1250, 1253, 1256, 1258, 1261, 1264, 1267, 1270, 1273, 1275,
+  1278, 1281, 1284, 1286, 1289, 1292, 1295, 1297, 1300, 1303, 1306, 1308, 1311,
+  1314, 1316, 1319, 1322, 1324, 1327, 1330, 1332, 1335, 1338, 1340, 1343, 1345,
+  1348, 1351, 1353, 1356, 1358, 1361, 1364, 1366, 1369, 1371, 1374, 1376, 1379,
+  1381, 1384, 1386, 1389, 1391, 1394, 1396, 1399, 1401, 1404, 1406, 1409, 1411,
+  1413, 1416, 1418, 1421, 1423, 1426, 1428, 1430, 1433, 1435, 1437, 1440, 1442,
+  1445, 1447, 1449, 1452, 1454, 1456, 1459, 1461, 1463, 1466, 1468, 1470, 1472,
+  1475, 1477, 1479, 1482, 1484, 1486, 1488, 1491, 1493, 1495, 1497, 1500, 1502,
+  1504, 1506, 1509, 1511, 1513, 1515, 1517, 1520, 1522, 1524, 1526, 1528, 1530,
+  1533, 1535, 1537, 1539, 1541, 1543, 1545, 1548, 1550, 1552, 1554, 1556, 1558,
+  1560, 1562, 1564, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585,
+  1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609,
+  /* index 401 - 899: -log((i+100)/1000) * 1000 */
+  691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 668, 666, 664,
+  662, 660, 658, 656, 654, 652, 650, 648, 646, 644, 642, 641, 639, 637, 635,
+  633, 631, 629, 627, 626, 624, 622, 620, 618, 616, 614, 612, 611, 609, 607,
+  605, 603, 602, 600, 598, 596, 594, 592, 591, 589, 587, 585, 583, 582, 580,
+  578, 576, 574, 573, 571, 569, 567, 566, 564, 562, 560, 559, 557, 555, 553,
+  552, 550, 548, 546, 545, 543, 541, 540, 538, 536, 534, 533, 531, 529, 528,
+  526, 524, 523, 521, 519, 518, 516, 514, 512, 511, 509, 508, 506, 504, 502,
+  501, 499, 498, 496, 494, 493, 491, 489, 488, 486, 484, 483, 481, 480, 478,
+  476, 475, 473, 472, 470, 468, 467, 465, 464, 462, 460, 459, 457, 456, 454,
+  453, 451, 449, 448, 446, 445, 443, 442, 440, 438, 437, 435, 434, 432, 431,
+  429, 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 412, 411, 410, 408,
+  406, 405, 404, 402, 400, 399, 398, 396, 394, 393, 392, 390, 389, 387, 386,
+  384, 383, 381, 380, 378, 377, 375, 374, 372, 371, 370, 368, 367, 365, 364,
+  362, 361, 360, 358, 357, 355, 354, 352, 351, 350, 348, 347, 345, 344, 342,
+  341, 340, 338, 337, 336, 334, 333, 331, 330, 328, 327, 326, 324, 323, 322,
+  320, 319, 318, 316, 315, 313, 312, 311, 309, 308, 306, 305, 304, 302, 301,
+  300, 298, 297, 296, 294, 293, 292, 290, 289, 288, 286, 285, 284, 282, 281,
+  280, 278, 277, 276, 274, 273, 272, 270, 269, 268, 267, 265, 264, 263, 261,
+  260, 259, 258, 256, 255, 254, 252, 251, 250, 248, 247, 246, 245, 243, 242,
+  241, 240, 238, 237, 236, 234, 233, 232, 231, 229, 228, 227, 226, 224, 223,
+  222, 221, 219, 218, 217, 216, 214, 213, 212, 211, 210, 208, 207, 206, 205,
+  203, 202, 201, 200, 198, 197, 196, 195, 194, 192, 191, 190, 189, 188, 186,
+  185, 184, 183, 182, 180, 179, 178, 177, 176, 174, 173, 172, 171, 170, 168,
+  167, 166, 165, 164, 162, 161, 160, 159, 158, 157, 156, 154, 153, 152, 151,
+  150, 148, 147, 146, 145, 144, 143, 142, 140, 139, 138, 137, 136, 135, 134,
+  132, 131, 130, 129, 128, 127, 126, 124, 123, 122, 121, 120, 119, 118, 116,
+  115, 114, 113, 112, 111, 110, 109, 108, 106, 105, 104, 103, 102, 101, 100,
+  99, 98, 97, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 84, 83, 82, 81, 80, 79,
+  78, 77, 76, 75, 74, 73, 72, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59,
+  58, 57, 56, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39,
+  38, 37, 36, 35, 34, 33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+  18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+/*
+ * Internal ln() function that does not check for specials, zero or one.
+ * Relative error: abs(result - log(a)) < 0.1 * 10**-prec * abs(log(a))
+ */
+static int
+_mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+         uint32_t *status)
+{
+    mpd_context_t varcontext, maxcontext;
+    mpd_t *z = result;
+    MPD_NEW_STATIC(v,0,0,0,0);
+    MPD_NEW_STATIC(vtmp,0,0,0,0);
+    MPD_NEW_STATIC(tmp,0,0,0,0);
+    mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+    mpd_ssize_t maxprec, shift, t;
+    mpd_ssize_t a_digits, a_exp;
+    mpd_uint_t dummy, x;
+    int ret = 1;
+    int i;
+
+    assert(!mpd_isspecial(a) && !mpd_iszerocoeff(a));
+
+    /*
+     * We are calculating ln(a) = ln(v * 10^t) = ln(v) + t*ln(10),
+     * where 0.5 < v <= 5.
+     */
+    if (!mpd_qcopy(&v, a, status)) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        goto finish;
+    }
+
+    /* Initial approximation: we have at least one non-zero digit */
+    _mpd_get_msdigits(&dummy, &x, &v, 3);
+    if (x < 10) x *= 10;
+    if (x < 100) x *= 10;
+    x -= 100;
+
+    /* a may equal z */
+    a_digits = a->digits;
+    a_exp = a->exp;
+
+    mpd_minalloc(z);
+    mpd_clear_flags(z);
+    z->data[0] = lnapprox[x];
+    z->len = 1;
+    z->exp = -3;
+    mpd_setdigits(z);
+
+    if (x <= 400) {
+        /* Reduce the input operand to 1.00 <= v <= 5.00. Let y = x + 100,
+         * so 100 <= y <= 500. Since y contains the most significant digits
+         * of v, y/100 <= v < (y+1)/100 and abs(z - log(v)) < 10**-2. */
+        v.exp = -(a_digits - 1);
+        t = a_exp + a_digits - 1;
+    }
+    else {
+        /* Reduce the input operand to 0.500 < v <= 0.999. Let y = x + 100,
+         * so 500 < y <= 999. Since y contains the most significant digits
+         * of v, y/1000 <= v < (y+1)/1000 and abs(z - log(v)) < 10**-2. */
+        v.exp = -a_digits;
+        t = a_exp + a_digits;
+        mpd_set_negative(z);
+    }
+
+    mpd_maxcontext(&maxcontext);
+    mpd_maxcontext(&varcontext);
+    varcontext.round = MPD_ROUND_TRUNC;
+
+    maxprec = ctx->prec + 2;
+    if (t == 0 && (x <= 15 || x >= 800)) {
+        /* 0.900 <= v <= 1.15: Estimate the magnitude of the logarithm.
+         * If ln(v) will underflow, skip the loop. Otherwise, adjust the
+         * precision upwards in order to obtain a sufficient number of
+         * significant digits.
+         *
+         *   Case v > 1:
+         *      abs((v-1)/10) < abs((v-1)/v) < abs(ln(v)) < abs(v-1)
+         *   Case v < 1:
+         *      abs(v-1) < abs(ln(v)) < abs((v-1)/v) < abs((v-1)*10)
+         */
+        int cmp = _mpd_cmp(&v, &one);
+
+        /* Upper bound (assume v > 1): abs(v-1), unrounded */
+        _mpd_qsub(&tmp, &v, &one, &maxcontext, &maxcontext.status);
+        if (maxcontext.status & MPD_Errors) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            goto finish;
+        }
+
+        if (cmp < 0) {
+            /* v < 1: abs((v-1)*10) */
+            tmp.exp += 1;
+        }
+        if (mpd_adjexp(&tmp) < mpd_etiny(ctx)) {
+            /* The upper bound is less than etiny: Underflow to zero */
+            _settriple(result, (cmp<0), 1, mpd_etiny(ctx)-1);
+            goto finish;
+        }
+        /* Lower bound: abs((v-1)/10) or abs(v-1) */
+        tmp.exp -= 1;
+        if (mpd_adjexp(&tmp) < 0) {
+            /* Absolute error of the loop: abs(z - log(v)) < 10**-p. If
+             * p = ctx->prec+2-adjexp(lower), then the relative error of
+             * the result is (using 10**adjexp(x) <= abs(x)):
+             *
+             *   abs(z - log(v)) / abs(log(v)) < 10**-p / abs(log(v))
+             *                                 <= 10**(-ctx->prec-2)
+             */
+            maxprec = maxprec - mpd_adjexp(&tmp);
+        }
+    }
+
+    i = ln_schedule_prec(klist, maxprec, 2);
+    for (; i >= 0; i--) {
+        varcontext.prec = 2*klist[i]+3;
+        z->flags ^= MPD_NEG;
+        _mpd_qexp(&tmp, z, &varcontext, status);
+        z->flags ^= MPD_NEG;
+
+        if (v.digits > varcontext.prec) {
+            shift = v.digits - varcontext.prec;
+            mpd_qshiftr(&vtmp, &v, shift, status);
+            vtmp.exp += shift;
+            mpd_qmul(&tmp, &vtmp, &tmp, &varcontext, status);
+        }
+        else {
+            mpd_qmul(&tmp, &v, &tmp, &varcontext, status);
+        }
+
+        mpd_qsub(&tmp, &tmp, &one, &maxcontext, status);
+        mpd_qadd(z, z, &tmp, &maxcontext, status);
+        if (mpd_isspecial(z)) {
+            break;
+        }
+    }
+
+    /*
+     * Case t == 0:
+     *    t * log(10) == 0, the result does not change and the analysis
+     *    above applies. If v < 0.900 or v > 1.15, the relative error is
+     *    less than 10**(-ctx.prec-1).
+     * Case t != 0:
+     *      z := approx(log(v))
+     *      y := approx(log(10))
+     *      p := maxprec = ctx->prec + 2
+     *   Absolute errors:
+     *      1) abs(z - log(v)) < 10**-p
+     *      2) abs(y - log(10)) < 10**-p
+     *   The multiplication is exact, so:
+     *      3) abs(t*y - t*log(10)) < t*10**-p
+     *   The sum is exact, so:
+     *      4) abs((z + t*y) - (log(v) + t*log(10))) < (abs(t) + 1) * 10**-p
+     *   Bounds for log(v) and log(10):
+     *      5) -7/10 < log(v) < 17/10
+     *      6) 23/10 < log(10) < 24/10
+     *   Using 4), 5), 6) and t != 0, the relative error is:
+     *
+     *      7) relerr < ((abs(t) + 1)*10**-p) / abs(log(v) + t*log(10))
+     *                < 0.5 * 10**(-p + 1) = 0.5 * 10**(-ctx->prec-1)
+     */
+    mpd_qln10(&v, maxprec+1, status);
+    mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status);
+    mpd_qadd(result, &tmp, z, &maxcontext, status);
+
+    ret = 0;
+
+finish:
+    *status |= (MPD_Inexact|MPD_Rounded);
+    mpd_del(&v);
+    mpd_del(&vtmp);
+    mpd_del(&tmp);
+    return ret;
+}
+
+/* ln(a) */
+void
+mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+        uint32_t *status)
+{
+    mpd_context_t workctx;
+    mpd_ssize_t adjexp, t;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        if (mpd_isnegative(a)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        mpd_setspecial(result, MPD_POS, MPD_INF);
+        return;
+    }
+    if (mpd_iszerocoeff(a)) {
+        mpd_setspecial(result, MPD_NEG, MPD_INF);
+        return;
+    }
+    if (mpd_isnegative(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (_mpd_cmp(a, &one) == 0) {
+        _settriple(result, MPD_POS, 0, 0);
+        return;
+    }
+    /*
+     * Check if the result will overflow (0 < x, x != 1):
+     *   1) log10(x) < 0 iff adjexp(x) < 0
+     *   2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y)
+     *   3) 0 < x /\ x != 1 ==> 2 * abs(log10(x)) < abs(log(x))
+     *   4) adjexp(x) <= log10(x) < adjexp(x) + 1
+     *
+     * Case adjexp(x) >= 0:
+     *   5) 2 * adjexp(x) < abs(log(x))
+     *   Case adjexp(x) > 0:
+     *     6) adjexp(2 * adjexp(x)) <= adjexp(abs(log(x)))
+     *   Case adjexp(x) == 0:
+     *     mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered)
+     *
+     * Case adjexp(x) < 0:
+     *   7) 2 * (-adjexp(x) - 1) < abs(log(x))
+     *   Case adjexp(x) < -1:
+     *     8) adjexp(2 * (-adjexp(x) - 1)) <= adjexp(abs(log(x)))
+     *   Case adjexp(x) == -1:
+     *     mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered)
+     */
+    adjexp = mpd_adjexp(a);
+    t = (adjexp < 0) ? -adjexp-1 : adjexp;
+    t *= 2;
+    if (mpd_exp_digits(t)-1 > ctx->emax) {
+        *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+        mpd_setspecial(result, (adjexp<0), MPD_INF);
+        return;
+    }
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.round = MPD_ROUND_HALF_EVEN;
+
+    if (ctx->allcr) {
+        MPD_NEW_STATIC(hi, 0,0,0,0);
+        MPD_NEW_STATIC(lo, 0,0,0,0);
+        MPD_NEW_STATIC(ulp, 0,0,0,0);
+        MPD_NEW_STATIC(aa, 0,0,0,0);
+        uint32_t loop_protect = 0;
+        mpd_ssize_t prec;
+
+        if (result == a) {
+            if (!mpd_qcopy(&aa, a, status)) {
+                mpd_seterror(result, MPD_Malloc_error, status);
+                return;
+            }
+            a = &aa;
+        }
+
+        workctx.clamp = 0;
+        prec = ctx->prec + 3;
+        while (1) {
+            uint32_t status_res = 0;
+            uint32_t status_hi = 0;
+            uint32_t status_lo = 0;
+            int shortcut;
+            int bounds_eq;
+            int subnormal_eq;
+
+            workctx.prec = prec;
+            shortcut = _mpd_qln(result, a, &workctx, &status_res);
+            if (mpd_isnan(result)) {
+                mpd_seterror(result, status_res, status);
+                break;
+            }
+            if (shortcut || mpd_isinfinite(result) || mpd_iszero(result)) {
+                *status |= status_res;
+                workctx.prec = ctx->prec;
+                workctx.clamp = ctx->clamp;
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            _ssettriple(&ulp, MPD_POS, 1,
+                        result->exp + result->digits-workctx.prec);
+
+            workctx.prec = ctx->prec;
+            mpd_qadd(&hi, result, &ulp, &workctx, &status_hi);
+            mpd_qsub(&lo, result, &ulp, &workctx, &status_lo);
+            if (mpd_isnan(&hi) || mpd_isnan(&lo)) {
+                mpd_seterror(result, status_hi|status_lo, status);
+                break;
+            }
+            subnormal_eq = (status_hi&MPD_Subnormal) == (status_lo&MPD_Subnormal);
+            bounds_eq = mpd_qcmp(&hi, &lo, status) == 0;
+            if (bounds_eq && ++loop_protect > 5) {
+                /* If the bounds are equal, the result is always correctly rounded.
+
+                   Resolving the subnormal status can take more iterations (around
+                   three) in extremely rare cases. 'hi' and 'lo' are so close that
+                   subnormal/underflow is largely cosmetic, so allow a maximum of
+                   five additional iterations. */
+                subnormal_eq = 1; /* GCOV_NOT_REACHED */
+            }
+
+            if (subnormal_eq && bounds_eq) {
+                *status |= status_lo;
+                workctx.clamp = ctx->clamp;
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            if (subnormal_eq) {
+                prec += MPD_RDIGITS;
+            }
+            else {
+                prec *= 2;
+            }
+
+            if (prec > MPD_MAX_PREC) {
+                mpd_seterror(result, MPD_Invalid_operation, status);
+                break;
+            }
+        }
+        mpd_del(&hi);
+        mpd_del(&lo);
+        mpd_del(&ulp);
+        mpd_del(&aa);
+    }
+    else {
+        _mpd_qln(result, a, &workctx, status);
+        mpd_check_underflow(result, &workctx, status);
+        mpd_qfinalize(result, &workctx, status);
+    }
+}
+
+/*
+ * Internal log10() function that does not check for specials, zero or one.
+ * Case SKIP_FINALIZE:
+ *   Relative error: abs(result - log10(a)) < 0.1 * 10**-prec * abs(log10(a))
+ * Case DO_FINALIZE:
+ *   Ulp error: abs(result - log10(a)) < ulp(log10(a))
+ */
+enum {SKIP_FINALIZE, DO_FINALIZE};
+static int
+_mpd_qlog10(int action, mpd_t *result, const mpd_t *a,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_STATIC(ln10,0,0,0,0);
+    int ret;
+
+    mpd_maxcontext(&workctx);
+    workctx.prec = ctx->prec + 3;
+    /* relative error: 0.1 * 10**(-p-3). The specific underflow shortcut
+     * in _mpd_qln() does not change the final result. */
+    ret = _mpd_qln(result, a, &workctx, status);
+    /* relative error: 5 * 10**(-p-3) */
+    mpd_qln10(&ln10, workctx.prec, status);
+
+    if (action == DO_FINALIZE) {
+        mpd_workcontext(&workctx, ctx);
+        workctx.round = MPD_ROUND_HALF_EVEN;
+    }
+    /* SKIP_FINALIZE: relative error: 5 * 10**(-p-3) */
+    _mpd_qdiv(NO_IDEAL_EXP, result, result, &ln10, &workctx, status);
+
+    mpd_del(&ln10);
+    return ret;
+}
+
+/* log10(a) */
+void
+mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    mpd_context_t workctx;
+    mpd_ssize_t adjexp, t;
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.round = MPD_ROUND_HALF_EVEN;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        if (mpd_isnegative(a)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        mpd_setspecial(result, MPD_POS, MPD_INF);
+        return;
+    }
+    if (mpd_iszerocoeff(a)) {
+        mpd_setspecial(result, MPD_NEG, MPD_INF);
+        return;
+    }
+    if (mpd_isnegative(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_coeff_ispow10(a)) {
+        uint8_t sign = 0;
+        adjexp = mpd_adjexp(a);
+        if (adjexp < 0) {
+            sign = 1;
+            adjexp = -adjexp;
+        }
+        _settriple(result, sign, adjexp, 0);
+        mpd_qfinalize(result, &workctx, status);
+        return;
+    }
+    /*
+     * Check if the result will overflow (0 < x, x != 1):
+     *   1) log10(x) < 0 iff adjexp(x) < 0
+     *   2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y)
+     *   3) adjexp(x) <= log10(x) < adjexp(x) + 1
+     *
+     * Case adjexp(x) >= 0:
+     *   4) adjexp(x) <= abs(log10(x))
+     *   Case adjexp(x) > 0:
+     *     5) adjexp(adjexp(x)) <= adjexp(abs(log10(x)))
+     *   Case adjexp(x) == 0:
+     *     mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered)
+     *
+     * Case adjexp(x) < 0:
+     *   6) -adjexp(x) - 1 < abs(log10(x))
+     *   Case adjexp(x) < -1:
+     *     7) adjexp(-adjexp(x) - 1) <= adjexp(abs(log(x)))
+     *   Case adjexp(x) == -1:
+     *     mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered)
+     */
+    adjexp = mpd_adjexp(a);
+    t = (adjexp < 0) ? -adjexp-1 : adjexp;
+    if (mpd_exp_digits(t)-1 > ctx->emax) {
+        *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+        mpd_setspecial(result, (adjexp<0), MPD_INF);
+        return;
+    }
+
+    if (ctx->allcr) {
+        MPD_NEW_STATIC(hi, 0,0,0,0);
+        MPD_NEW_STATIC(lo, 0,0,0,0);
+        MPD_NEW_STATIC(ulp, 0,0,0,0);
+        MPD_NEW_STATIC(aa, 0,0,0,0);
+        uint32_t loop_protect = 0;
+        mpd_ssize_t prec;
+
+        if (result == a) {
+            if (!mpd_qcopy(&aa, a, status)) {
+                mpd_seterror(result, MPD_Malloc_error, status);
+                return;
+            }
+            a = &aa;
+        }
+
+        workctx.clamp = 0;
+        prec = ctx->prec + 3;
+        while (1) {
+            uint32_t status_res = 0;
+            uint32_t status_hi = 0;
+            uint32_t status_lo = 0;
+            int shortcut;
+            int bounds_eq;
+            int subnormal_eq;
+
+            workctx.prec = prec;
+            shortcut = _mpd_qlog10(SKIP_FINALIZE, result, a, &workctx, &status_res);
+            if (mpd_isnan(result)) {
+                mpd_seterror(result, status_res, status);
+                break;
+            }
+            if (shortcut || mpd_isinfinite(result) || mpd_iszero(result)) {
+                *status |= status_res;
+                workctx.prec = ctx->prec;
+                workctx.clamp = ctx->clamp;
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            _ssettriple(&ulp, MPD_POS, 1,
+                        result->exp + result->digits-workctx.prec);
+
+            workctx.prec = ctx->prec;
+            mpd_qadd(&hi, result, &ulp, &workctx, &status_hi);
+            mpd_qsub(&lo, result, &ulp, &workctx, &status_lo);
+            if (mpd_isnan(&hi) || mpd_isnan(&lo)) {
+                mpd_seterror(result, status_hi|status_lo, status);
+                break;
+            }
+            subnormal_eq = (status_hi&MPD_Subnormal) == (status_lo&MPD_Subnormal);
+            bounds_eq = mpd_qcmp(&hi, &lo, status) == 0;
+            if (bounds_eq && ++loop_protect > 5) {
+                /* If the bounds are equal, the result is always correctly rounded.
+
+                   Resolving the subnormal status can take more iterations (around
+                   three) in extremely rare cases. 'hi' and 'lo' are so close that
+                   subnormal/underflow is largely cosmetic, so allow a maximum of
+                   five additional iterations. */
+                subnormal_eq = 1; /* GCOV_NOT_REACHED */
+            }
+
+            if (subnormal_eq && bounds_eq) {
+                *status |= status_lo;
+                workctx.clamp = ctx->clamp;
+                mpd_qfinalize(result, &workctx, status);
+                break;
+            }
+
+            if (subnormal_eq) {
+                prec += MPD_RDIGITS;
+            }
+            else {
+                prec *= 2;
+            }
+
+            if (prec > MPD_MAX_PREC) {
+                mpd_seterror(result, MPD_Invalid_operation, status);
+                break;
+            }
+        }
+        mpd_del(&hi);
+        mpd_del(&lo);
+        mpd_del(&ulp);
+        mpd_del(&aa);
+    }
+    else {
+        _mpd_qlog10(DO_FINALIZE, result, a, &workctx, status);
+        mpd_check_underflow(result, &workctx, status);
+    }
+}
+
+/*
+ * Maximum of the two operands. Attention: If one operand is a quiet NaN and the
+ * other is numeric, the numeric operand is returned. This may not be what one
+ * expects.
+ */
+void
+mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isqnan(a) && !mpd_isnan(b)) {
+        mpd_qcopy(result, b, status);
+    }
+    else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+        mpd_qcopy(result, a, status);
+    }
+    else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+        return;
+    }
+    else {
+        c = _mpd_cmp(a, b);
+        if (c == 0) {
+            c = _mpd_cmp_numequal(a, b);
+        }
+
+        if (c < 0) {
+            mpd_qcopy(result, b, status);
+        }
+        else {
+            mpd_qcopy(result, a, status);
+        }
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Maximum magnitude: Same as mpd_max(), but compares the operands with their
+ * sign ignored.
+ */
+void
+mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isqnan(a) && !mpd_isnan(b)) {
+        mpd_qcopy(result, b, status);
+    }
+    else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+        mpd_qcopy(result, a, status);
+    }
+    else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+        return;
+    }
+    else {
+        c = _mpd_cmp_abs(a, b);
+        if (c == 0) {
+            c = _mpd_cmp_numequal(a, b);
+        }
+
+        if (c < 0) {
+            mpd_qcopy(result, b, status);
+        }
+        else {
+            mpd_qcopy(result, a, status);
+        }
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Minimum of the two operands. Attention: If one operand is a quiet NaN and the
+ * other is numeric, the numeric operand is returned. This may not be what one
+ * expects.
+ */
+void
+mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isqnan(a) && !mpd_isnan(b)) {
+        mpd_qcopy(result, b, status);
+    }
+    else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+        mpd_qcopy(result, a, status);
+    }
+    else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+        return;
+    }
+    else {
+        c = _mpd_cmp(a, b);
+        if (c == 0) {
+            c = _mpd_cmp_numequal(a, b);
+        }
+
+        if (c < 0) {
+            mpd_qcopy(result, a, status);
+        }
+        else {
+            mpd_qcopy(result, b, status);
+        }
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Minimum magnitude: Same as mpd_min(), but compares the operands with their
+ * sign ignored.
+ */
+void
+mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_isqnan(a) && !mpd_isnan(b)) {
+        mpd_qcopy(result, b, status);
+    }
+    else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+        mpd_qcopy(result, a, status);
+    }
+    else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+        return;
+    }
+    else {
+        c = _mpd_cmp_abs(a, b);
+        if (c == 0) {
+            c = _mpd_cmp_numequal(a, b);
+        }
+
+        if (c < 0) {
+            mpd_qcopy(result, a, status);
+        }
+        else {
+            mpd_qcopy(result, b, status);
+        }
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* Minimum space needed for the result array in _karatsuba_rec(). */
+static inline mpd_size_t
+_kmul_resultsize(mpd_size_t la, mpd_size_t lb)
+{
+    mpd_size_t n, m;
+
+    n = add_size_t(la, lb);
+    n = add_size_t(n, 1);
+
+    m = (la+1)/2 + 1;
+    m = mul_size_t(m, 3);
+
+    return (m > n) ? m : n;
+}
+
+/* Work space needed in _karatsuba_rec(). lim >= 4 */
+static inline mpd_size_t
+_kmul_worksize(mpd_size_t n, mpd_size_t lim)
+{
+    mpd_size_t m;
+
+    if (n <= lim) {
+        return 0;
+    }
+
+    m = (n+1)/2 + 1;
+
+    return add_size_t(mul_size_t(m, 2), _kmul_worksize(m, lim));
+}
+
+
+#define MPD_KARATSUBA_BASECASE 16  /* must be >= 4 */
+
+/*
+ * Add the product of a and b to c.
+ * c must be _kmul_resultsize(la, lb) in size.
+ * w is used as a work array and must be _kmul_worksize(a, lim) in size.
+ * Roman E. Maeder, Storage Allocation for the Karatsuba Integer Multiplication
+ * Algorithm. In "Design and implementation of symbolic computation systems",
+ * Springer, 1993, ISBN 354057235X, 9783540572350.
+ */
+static void
+_karatsuba_rec(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b,
+               mpd_uint_t *w, mpd_size_t la, mpd_size_t lb)
+{
+    mpd_size_t m, lt;
+
+    assert(la >= lb && lb > 0);
+    assert(la <= MPD_KARATSUBA_BASECASE || w != NULL);
+
+    if (la <= MPD_KARATSUBA_BASECASE) {
+        _mpd_basemul(c, a, b, la, lb);
+        return;
+    }
+
+    m = (la+1)/2;  /* ceil(la/2) */
+
+    /* lb <= m < la */
+    if (lb <= m) {
+
+        /* lb can now be larger than la-m */
+        if (lb > la-m) {
+            lt = lb + lb + 1;       /* space needed for result array */
+            mpd_uint_zero(w, lt);   /* clear result array */
+            _karatsuba_rec(w, b, a+m, w+lt, lb, la-m); /* b*ah */
+        }
+        else {
+            lt = (la-m) + (la-m) + 1;  /* space needed for result array */
+            mpd_uint_zero(w, lt);      /* clear result array */
+            _karatsuba_rec(w, a+m, b, w+lt, la-m, lb); /* ah*b */
+        }
+        _mpd_baseaddto(c+m, w, (la-m)+lb);      /* add ah*b*B**m */
+
+        lt = m + m + 1;         /* space needed for the result array */
+        mpd_uint_zero(w, lt);   /* clear result array */
+        _karatsuba_rec(w, a, b, w+lt, m, lb);  /* al*b */
+        _mpd_baseaddto(c, w, m+lb);    /* add al*b */
+
+        return;
+    }
+
+    /* la >= lb > m */
+    memcpy(w, a, m * sizeof *w);
+    w[m] = 0;
+    _mpd_baseaddto(w, a+m, la-m);
+
+    memcpy(w+(m+1), b, m * sizeof *w);
+    w[m+1+m] = 0;
+    _mpd_baseaddto(w+(m+1), b+m, lb-m);
+
+    _karatsuba_rec(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1);
+
+    lt = (la-m) + (la-m) + 1;
+    mpd_uint_zero(w, lt);
+
+    _karatsuba_rec(w, a+m, b+m, w+lt, la-m, lb-m);
+
+    _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m));
+    _mpd_basesubfrom(c+m, w, (la-m) + (lb-m));
+
+    lt = m + m + 1;
+    mpd_uint_zero(w, lt);
+
+    _karatsuba_rec(w, a, b, w+lt, m, m);
+    _mpd_baseaddto(c, w, m+m);
+    _mpd_basesubfrom(c+m, w, m+m);
+
+    return;
+}
+
+/*
+ * Multiply u and v, using Karatsuba multiplication. Returns a pointer
+ * to the result or NULL in case of failure (malloc error).
+ * Conditions: ulen >= vlen, ulen >= 4
+ */
+static mpd_uint_t *
+_mpd_kmul(const mpd_uint_t *u, const mpd_uint_t *v,
+          mpd_size_t ulen, mpd_size_t vlen,
+          mpd_size_t *rsize)
+{
+    mpd_uint_t *result = NULL, *w = NULL;
+    mpd_size_t m;
+
+    assert(ulen >= 4);
+    assert(ulen >= vlen);
+
+    *rsize = _kmul_resultsize(ulen, vlen);
+    if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) {
+        return NULL;
+    }
+
+    m = _kmul_worksize(ulen, MPD_KARATSUBA_BASECASE);
+    if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) {
+        mpd_free(result);
+        return NULL;
+    }
+
+    _karatsuba_rec(result, u, v, w, ulen, vlen);
+
+
+    if (w) mpd_free(w);
+    return result;
+}
+
+
+/*
+ * Determine the minimum length for the number theoretic transform. Valid
+ * transform lengths are 2**n or 3*2**n, where 2**n <= MPD_MAXTRANSFORM_2N.
+ * The function finds the shortest length m such that rsize <= m.
+ */
+static inline mpd_size_t
+_mpd_get_transform_len(mpd_size_t rsize)
+{
+    mpd_size_t log2rsize;
+    mpd_size_t x, step;
+
+    assert(rsize >= 4);
+    log2rsize = mpd_bsr(rsize);
+
+    if (rsize <= 1024) {
+        /* 2**n is faster in this range. */
+        x = ((mpd_size_t)1)<<log2rsize;
+        return (rsize == x) ? x : x<<1;
+    }
+    else if (rsize <= MPD_MAXTRANSFORM_2N) {
+        x = ((mpd_size_t)1)<<log2rsize;
+        if (rsize == x) return x;
+        step = x>>1;
+        x += step;
+        return (rsize <= x) ? x : x + step;
+    }
+    else if (rsize <= MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2) {
+        return MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2;
+    }
+    else if (rsize <= 3*MPD_MAXTRANSFORM_2N) {
+        return 3*MPD_MAXTRANSFORM_2N;
+    }
+    else {
+        return MPD_SIZE_MAX;
+    }
+}
+
+#ifdef PPRO
+#ifndef _MSC_VER
+static inline unsigned short
+_mpd_get_control87(void)
+{
+    unsigned short cw;
+
+    __asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
+    return cw;
+}
+
+static inline void
+_mpd_set_control87(unsigned short cw)
+{
+    __asm__ __volatile__ ("fldcw %0" : : "m" (cw));
+}
+#endif
+
+static unsigned int
+mpd_set_fenv(void)
+{
+    unsigned int cw;
+#ifdef _MSC_VER
+    unsigned int flags =
+        _EM_INVALID|_EM_DENORMAL|_EM_ZERODIVIDE|_EM_OVERFLOW|
+        _EM_UNDERFLOW|_EM_INEXACT|_RC_CHOP|_PC_64;
+    unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC;
+    unsigned int dummy;
+
+    __control87_2(0, 0, &cw, NULL);
+    __control87_2(flags, mask, &dummy, NULL);
+#else
+    cw = _mpd_get_control87();
+    _mpd_set_control87(cw|0xF3F);
+#endif
+    return cw;
+}
+
+static void
+mpd_restore_fenv(unsigned int cw)
+{
+#ifdef _MSC_VER
+    unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC;
+    unsigned int dummy;
+
+    __control87_2(cw, mask, &dummy, NULL);
+#else
+    _mpd_set_control87((unsigned short)cw);
+#endif
+}
+#endif /* PPRO */
+
+/*
+ * Multiply u and v, using the fast number theoretic transform. Returns
+ * a pointer to the result or NULL in case of failure (malloc error).
+ */
+static mpd_uint_t *
+_mpd_fntmul(const mpd_uint_t *u, const mpd_uint_t *v,
+            mpd_size_t ulen, mpd_size_t vlen,
+            mpd_size_t *rsize)
+{
+    mpd_uint_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *vtmp = NULL;
+    mpd_size_t n;
+
+#ifdef PPRO
+    unsigned int cw;
+    cw = mpd_set_fenv();
+#endif
+
+    *rsize = add_size_t(ulen, vlen);
+    if ((n = _mpd_get_transform_len(*rsize)) == MPD_SIZE_MAX) {
+        goto malloc_error;
+    }
+
+    if ((c1 = mpd_calloc(n, sizeof *c1)) == NULL) {
+        goto malloc_error;
+    }
+    if ((c2 = mpd_calloc(n, sizeof *c2)) == NULL) {
+        goto malloc_error;
+    }
+    if ((c3 = mpd_calloc(n, sizeof *c3)) == NULL) {
+        goto malloc_error;
+    }
+
+    memcpy(c1, u, ulen * (sizeof *c1));
+    memcpy(c2, u, ulen * (sizeof *c2));
+    memcpy(c3, u, ulen * (sizeof *c3));
+
+    if (u == v) {
+        if (!fnt_autoconvolute(c1, n, P1) ||
+            !fnt_autoconvolute(c2, n, P2) ||
+            !fnt_autoconvolute(c3, n, P3)) {
+            goto malloc_error;
+        }
+    }
+    else {
+        if ((vtmp = mpd_calloc(n, sizeof *vtmp)) == NULL) {
+            goto malloc_error;
+        }
+
+        memcpy(vtmp, v, vlen * (sizeof *vtmp));
+        if (!fnt_convolute(c1, vtmp, n, P1)) {
+            mpd_free(vtmp);
+            goto malloc_error;
+        }
+
+        memcpy(vtmp, v, vlen * (sizeof *vtmp));
+        mpd_uint_zero(vtmp+vlen, n-vlen);
+        if (!fnt_convolute(c2, vtmp, n, P2)) {
+            mpd_free(vtmp);
+            goto malloc_error;
+        }
+
+        memcpy(vtmp, v, vlen * (sizeof *vtmp));
+        mpd_uint_zero(vtmp+vlen, n-vlen);
+        if (!fnt_convolute(c3, vtmp, n, P3)) {
+            mpd_free(vtmp);
+            goto malloc_error;
+        }
+
+        mpd_free(vtmp);
+    }
+
+    crt3(c1, c2, c3, *rsize);
+
+out:
+#ifdef PPRO
+    mpd_restore_fenv(cw);
+#endif
+    if (c2) mpd_free(c2);
+    if (c3) mpd_free(c3);
+    return c1;
+
+malloc_error:
+    if (c1) mpd_free(c1);
+    c1 = NULL;
+    goto out;
+}
+
+
+/*
+ * Karatsuba multiplication with FNT/basemul as the base case.
+ */
+static int
+_karatsuba_rec_fnt(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b,
+                   mpd_uint_t *w, mpd_size_t la, mpd_size_t lb)
+{
+    mpd_size_t m, lt;
+
+    assert(la >= lb && lb > 0);
+    assert(la <= 3*(MPD_MAXTRANSFORM_2N/2) || w != NULL);
+
+    if (la <= 3*(MPD_MAXTRANSFORM_2N/2)) {
+
+        if (lb <= 192) {
+            _mpd_basemul(c, b, a, lb, la);
+        }
+        else {
+            mpd_uint_t *result;
+            mpd_size_t dummy;
+
+            if ((result = _mpd_fntmul(a, b, la, lb, &dummy)) == NULL) {
+                return 0;
+            }
+            memcpy(c, result, (la+lb) * (sizeof *result));
+            mpd_free(result);
+        }
+        return 1;
+    }
+
+    m = (la+1)/2;  /* ceil(la/2) */
+
+    /* lb <= m < la */
+    if (lb <= m) {
+
+        /* lb can now be larger than la-m */
+        if (lb > la-m) {
+            lt = lb + lb + 1;       /* space needed for result array */
+            mpd_uint_zero(w, lt);   /* clear result array */
+            if (!_karatsuba_rec_fnt(w, b, a+m, w+lt, lb, la-m)) { /* b*ah */
+                return 0; /* GCOV_UNLIKELY */
+            }
+        }
+        else {
+            lt = (la-m) + (la-m) + 1;  /* space needed for result array */
+            mpd_uint_zero(w, lt);      /* clear result array */
+            if (!_karatsuba_rec_fnt(w, a+m, b, w+lt, la-m, lb)) { /* ah*b */
+                return 0; /* GCOV_UNLIKELY */
+            }
+        }
+        _mpd_baseaddto(c+m, w, (la-m)+lb); /* add ah*b*B**m */
+
+        lt = m + m + 1;         /* space needed for the result array */
+        mpd_uint_zero(w, lt);   /* clear result array */
+        if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, lb)) {  /* al*b */
+            return 0; /* GCOV_UNLIKELY */
+        }
+        _mpd_baseaddto(c, w, m+lb);       /* add al*b */
+
+        return 1;
+    }
+
+    /* la >= lb > m */
+    memcpy(w, a, m * sizeof *w);
+    w[m] = 0;
+    _mpd_baseaddto(w, a+m, la-m);
+
+    memcpy(w+(m+1), b, m * sizeof *w);
+    w[m+1+m] = 0;
+    _mpd_baseaddto(w+(m+1), b+m, lb-m);
+
+    if (!_karatsuba_rec_fnt(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1)) {
+        return 0; /* GCOV_UNLIKELY */
+    }
+
+    lt = (la-m) + (la-m) + 1;
+    mpd_uint_zero(w, lt);
+
+    if (!_karatsuba_rec_fnt(w, a+m, b+m, w+lt, la-m, lb-m)) {
+        return 0; /* GCOV_UNLIKELY */
+    }
+
+    _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m));
+    _mpd_basesubfrom(c+m, w, (la-m) + (lb-m));
+
+    lt = m + m + 1;
+    mpd_uint_zero(w, lt);
+
+    if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, m)) {
+        return 0; /* GCOV_UNLIKELY */
+    }
+    _mpd_baseaddto(c, w, m+m);
+    _mpd_basesubfrom(c+m, w, m+m);
+
+    return 1;
+}
+
+/*
+ * Multiply u and v, using Karatsuba multiplication with the FNT as the
+ * base case. Returns a pointer to the result or NULL in case of failure
+ * (malloc error). Conditions: ulen >= vlen, ulen >= 4.
+ */
+static mpd_uint_t *
+_mpd_kmul_fnt(const mpd_uint_t *u, const mpd_uint_t *v,
+              mpd_size_t ulen, mpd_size_t vlen,
+              mpd_size_t *rsize)
+{
+    mpd_uint_t *result = NULL, *w = NULL;
+    mpd_size_t m;
+
+    assert(ulen >= 4);
+    assert(ulen >= vlen);
+
+    *rsize = _kmul_resultsize(ulen, vlen);
+    if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) {
+        return NULL;
+    }
+
+    m = _kmul_worksize(ulen, 3*(MPD_MAXTRANSFORM_2N/2));
+    if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) {
+        mpd_free(result); /* GCOV_UNLIKELY */
+        return NULL; /* GCOV_UNLIKELY */
+    }
+
+    if (!_karatsuba_rec_fnt(result, u, v, w, ulen, vlen)) {
+        mpd_free(result);
+        result = NULL;
+    }
+
+
+    if (w) mpd_free(w);
+    return result;
+}
+
+
+/* Deal with the special cases of multiplying infinities. */
+static void
+_mpd_qmul_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+    if (mpd_isinfinite(a)) {
+        if (mpd_iszero(b)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+        }
+        else {
+            mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+        }
+        return;
+    }
+    assert(mpd_isinfinite(b));
+    if (mpd_iszero(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+    }
+    else {
+        mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+    }
+}
+
+/*
+ * Internal function: Multiply a and b. _mpd_qmul deals with specials but
+ * does NOT finalize the result. This is for use in mpd_fma().
+ */
+static inline void
+_mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+          const mpd_context_t *ctx, uint32_t *status)
+{
+    const mpd_t *big = a, *small = b;
+    mpd_uint_t *rdata = NULL;
+    mpd_uint_t rbuf[MPD_MINALLOC_MAX];
+    mpd_size_t rsize, i;
+
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+        _mpd_qmul_inf(result, a, b, status);
+        return;
+    }
+
+    if (small->len > big->len) {
+        _mpd_ptrswap(&big, &small);
+    }
+
+    rsize = big->len + small->len;
+
+    if (big->len == 1) {
+        _mpd_singlemul(result->data, big->data[0], small->data[0]);
+        goto finish;
+    }
+    if (rsize <= (mpd_size_t)MPD_MINALLOC_MAX) {
+        if (big->len == 2) {
+            _mpd_mul_2_le2(rbuf, big->data, small->data, small->len);
+        }
+        else {
+            mpd_uint_zero(rbuf, rsize);
+            if (small->len == 1) {
+                _mpd_shortmul(rbuf, big->data, big->len, small->data[0]);
+            }
+            else {
+                _mpd_basemul(rbuf, small->data, big->data, small->len, big->len);
+            }
+        }
+        if (!mpd_qresize(result, rsize, status)) {
+            return;
+        }
+        for(i = 0; i < rsize; i++) {
+            result->data[i] = rbuf[i];
+        }
+        goto finish;
+    }
+
+
+    if (small->len <= 256) {
+        rdata = mpd_calloc(rsize, sizeof *rdata);
+        if (rdata != NULL) {
+            if (small->len == 1) {
+                _mpd_shortmul(rdata, big->data, big->len, small->data[0]);
+            }
+            else {
+                _mpd_basemul(rdata, small->data, big->data, small->len, big->len);
+            }
+        }
+    }
+    else if (rsize <= 1024) {
+        rdata = _mpd_kmul(big->data, small->data, big->len, small->len, &rsize);
+    }
+    else if (rsize <= 3*MPD_MAXTRANSFORM_2N) {
+        rdata = _mpd_fntmul(big->data, small->data, big->len, small->len, &rsize);
+    }
+    else {
+        rdata = _mpd_kmul_fnt(big->data, small->data, big->len, small->len, &rsize);
+    }
+
+    if (rdata == NULL) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        return;
+    }
+
+    if (mpd_isdynamic_data(result)) {
+        mpd_free(result->data);
+    }
+    result->data = rdata;
+    result->alloc = rsize;
+    mpd_set_dynamic_data(result);
+
+
+finish:
+    mpd_set_flags(result, mpd_sign(a)^mpd_sign(b));
+    result->exp = big->exp + small->exp;
+    result->len = _mpd_real_size(result->data, rsize);
+    /* resize to smaller cannot fail */
+    mpd_qresize(result, result->len, status);
+    mpd_setdigits(result);
+}
+
+/* Multiply a and b. */
+void
+mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    _mpd_qmul(result, a, b, ctx, status);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* Multiply a and b. Set NaN/Invalid_operation if the result is inexact. */
+static void
+_mpd_qmul_exact(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+
+    mpd_qmul(result, a, b, ctx, &workstatus);
+    *status |= workstatus;
+    if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+    }
+}
+
+/* Multiply decimal and mpd_ssize_t. */
+void
+mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+               const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_ssize(&bb, b, &maxcontext, status);
+    mpd_qmul(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Multiply decimal and mpd_uint_t. */
+void
+mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qsset_uint(&bb, b, &maxcontext, status);
+    mpd_qmul(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+void
+mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qmul_ssize(result, a, b, ctx, status);
+}
+
+void
+mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qmul_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+void
+mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qmul_ssize(result, a, b, ctx, status);
+}
+
+void
+mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_qmul_uint(result, a, b, ctx, status);
+}
+#elif !defined(LEGACY_COMPILER)
+/* Multiply decimal and int64_t. */
+void
+mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_i64(&bb, b, &maxcontext, status);
+    mpd_qmul(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+
+/* Multiply decimal and uint64_t. */
+void
+mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(bb,0,0,0,0);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_qset_u64(&bb, b, &maxcontext, status);
+    mpd_qmul(result, a, &bb, ctx, status);
+    mpd_del(&bb);
+}
+#endif
+
+/* Like the minus operator. */
+void
+mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+    }
+
+    if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) {
+        mpd_qcopy_abs(result, a, status);
+    }
+    else {
+        mpd_qcopy_negate(result, a, status);
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* Like the plus operator. */
+void
+mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+          uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+    }
+
+    if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) {
+        mpd_qcopy_abs(result, a, status);
+    }
+    else {
+        mpd_qcopy(result, a, status);
+    }
+
+    mpd_qfinalize(result, ctx, status);
+}
+
+/* The largest representable number that is smaller than the operand. */
+void
+mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+                uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1);
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+
+        assert(mpd_isinfinite(a));
+        if (mpd_isnegative(a)) {
+            mpd_qcopy(result, a, status);
+            return;
+        }
+        else {
+            mpd_clear_flags(result);
+            mpd_qmaxcoeff(result, ctx, status);
+            if (mpd_isnan(result)) {
+                return;
+            }
+            result->exp = mpd_etop(ctx);
+            return;
+        }
+    }
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.round = MPD_ROUND_FLOOR;
+
+    if (!mpd_qcopy(result, a, status)) {
+        return;
+    }
+
+    mpd_qfinalize(result, &workctx, &workctx.status);
+    if (workctx.status&(MPD_Inexact|MPD_Errors)) {
+        *status |= (workctx.status&MPD_Errors);
+        return;
+    }
+
+    workctx.status = 0;
+    mpd_qsub(result, a, &tiny, &workctx, &workctx.status);
+    *status |= (workctx.status&MPD_Errors);
+}
+
+/* The smallest representable number that is larger than the operand. */
+void
+mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+               uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1);
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+
+        assert(mpd_isinfinite(a));
+        if (mpd_ispositive(a)) {
+            mpd_qcopy(result, a, status);
+        }
+        else {
+            mpd_clear_flags(result);
+            mpd_qmaxcoeff(result, ctx, status);
+            if (mpd_isnan(result)) {
+                return;
+            }
+            mpd_set_flags(result, MPD_NEG);
+            result->exp = mpd_etop(ctx);
+        }
+        return;
+    }
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.round = MPD_ROUND_CEILING;
+
+    if (!mpd_qcopy(result, a, status)) {
+        return;
+    }
+
+    mpd_qfinalize(result, &workctx, &workctx.status);
+    if (workctx.status & (MPD_Inexact|MPD_Errors)) {
+        *status |= (workctx.status&MPD_Errors);
+        return;
+    }
+
+    workctx.status = 0;
+    mpd_qadd(result, a, &tiny, &workctx, &workctx.status);
+    *status |= (workctx.status&MPD_Errors);
+}
+
+/*
+ * The number closest to the first operand that is in the direction towards
+ * the second operand.
+ */
+void
+mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b,
+                 const mpd_context_t *ctx, uint32_t *status)
+{
+    int c;
+
+    if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+        return;
+    }
+
+    c = _mpd_cmp(a, b);
+    if (c == 0) {
+        mpd_qcopy_sign(result, a, b, status);
+        return;
+    }
+
+    if (c < 0) {
+        mpd_qnext_plus(result, a, ctx, status);
+    }
+    else {
+        mpd_qnext_minus(result, a, ctx, status);
+    }
+
+    if (mpd_isinfinite(result)) {
+        *status |= (MPD_Overflow|MPD_Rounded|MPD_Inexact);
+    }
+    else if (mpd_adjexp(result) < ctx->emin) {
+        *status |= (MPD_Underflow|MPD_Subnormal|MPD_Rounded|MPD_Inexact);
+        if (mpd_iszero(result)) {
+            *status |= MPD_Clamped;
+        }
+    }
+}
+
+/*
+ * Internal function: Integer power with mpd_uint_t exponent. The function
+ * can fail with MPD_Malloc_error.
+ *
+ * The error is equal to the error incurred in k-1 multiplications. Assuming
+ * the upper bound for the relative error in each operation:
+ *
+ *   abs(err) = 5 * 10**-prec
+ *   result = x**k * (1 + err)**(k-1)
+ */
+static inline void
+_mpd_qpow_uint(mpd_t *result, const mpd_t *base, mpd_uint_t exp,
+               uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_uint_t n;
+
+    if (exp == 0) {
+        _settriple(result, resultsign, 1, 0); /* GCOV_NOT_REACHED */
+        return; /* GCOV_NOT_REACHED */
+    }
+
+    if (!mpd_qcopy(result, base, status)) {
+        return;
+    }
+
+    n = mpd_bits[mpd_bsr(exp)];
+    while (n >>= 1) {
+        mpd_qmul(result, result, result, ctx, &workstatus);
+        if (exp & n) {
+            mpd_qmul(result, result, base, ctx, &workstatus);
+        }
+        if (mpd_isspecial(result) ||
+            (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) {
+            break;
+        }
+    }
+
+    *status |= workstatus;
+    mpd_set_sign(result, resultsign);
+}
+
+/*
+ * Internal function: Integer power with mpd_t exponent, tbase and texp
+ * are modified!! Function can fail with MPD_Malloc_error.
+ *
+ * The error is equal to the error incurred in k multiplications. Assuming
+ * the upper bound for the relative error in each operation:
+ *
+ *   abs(err) = 5 * 10**-prec
+ *   result = x**k * (1 + err)**k
+ */
+static inline void
+_mpd_qpow_mpd(mpd_t *result, mpd_t *tbase, mpd_t *texp, uint8_t resultsign,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_context_t maxctx;
+    MPD_NEW_CONST(two,0,0,1,1,1,2);
+
+
+    mpd_maxcontext(&maxctx);
+
+    /* resize to smaller cannot fail */
+    mpd_qcopy(result, &one, status);
+
+    while (!mpd_iszero(texp)) {
+        if (mpd_isodd(texp)) {
+            mpd_qmul(result, result, tbase, ctx, &workstatus);
+            *status |= workstatus;
+            if (mpd_isspecial(result) ||
+                (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) {
+                break;
+            }
+        }
+        mpd_qmul(tbase, tbase, tbase, ctx, &workstatus);
+        mpd_qdivint(texp, texp, &two, &maxctx, &workstatus);
+        if (mpd_isnan(tbase) || mpd_isnan(texp)) {
+            mpd_seterror(result, workstatus&MPD_Errors, status);
+            return;
+        }
+    }
+    mpd_set_sign(result, resultsign);
+}
+
+/*
+ * The power function for integer exponents. Relative error _before_ the
+ * final rounding to prec:
+ *   abs(result - base**exp) < 0.1 * 10**-prec * abs(base**exp)
+ */
+static void
+_mpd_qpow_int(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+              uint8_t resultsign,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_STATIC(tbase,0,0,0,0);
+    MPD_NEW_STATIC(texp,0,0,0,0);
+    mpd_uint_t n;
+
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.prec += (exp->digits + exp->exp + 2);
+    workctx.round = MPD_ROUND_HALF_EVEN;
+    workctx.clamp = 0;
+    if (mpd_isnegative(exp)) {
+        uint32_t workstatus = 0;
+        workctx.prec += 1;
+        mpd_qdiv(&tbase, &one, base, &workctx, &workstatus);
+        *status |= workstatus;
+        if (workstatus&MPD_Errors) {
+            mpd_setspecial(result, MPD_POS, MPD_NAN);
+            goto finish;
+        }
+    }
+    else {
+        if (!mpd_qcopy(&tbase, base, status)) {
+            mpd_setspecial(result, MPD_POS, MPD_NAN);
+            goto finish;
+        }
+    }
+
+    n = mpd_qabs_uint(exp, &workctx.status);
+    if (workctx.status&MPD_Invalid_operation) {
+        if (!mpd_qcopy(&texp, exp, status)) {
+            mpd_setspecial(result, MPD_POS, MPD_NAN); /* GCOV_UNLIKELY */
+            goto finish; /* GCOV_UNLIKELY */
+        }
+        _mpd_qpow_mpd(result, &tbase, &texp, resultsign, &workctx, status);
+    }
+    else {
+        _mpd_qpow_uint(result, &tbase, n, resultsign, &workctx, status);
+    }
+
+    if (mpd_isinfinite(result)) {
+        /* for ROUND_DOWN, ROUND_FLOOR, etc. */
+        _settriple(result, resultsign, 1, MPD_EXP_INF);
+    }
+
+finish:
+    mpd_del(&tbase);
+    mpd_del(&texp);
+    mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * If the exponent is infinite and base equals one, the result is one
+ * with a coefficient of length prec. Otherwise, result is undefined.
+ * Return the value of the comparison against one.
+ */
+static int
+_qcheck_pow_one_inf(mpd_t *result, const mpd_t *base, uint8_t resultsign,
+                    const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t shift;
+    int cmp;
+
+    if ((cmp = _mpd_cmp(base, &one)) == 0) {
+        shift = ctx->prec-1;
+        mpd_qshiftl(result, &one, shift, status);
+        result->exp = -shift;
+        mpd_set_flags(result, resultsign);
+        *status |= (MPD_Inexact|MPD_Rounded);
+    }
+
+    return cmp;
+}
+
+/*
+ * If abs(base) equals one, calculate the correct power of one result.
+ * Otherwise, result is undefined. Return the value of the comparison
+ * against 1.
+ *
+ * This is an internal function that does not check for specials.
+ */
+static int
+_qcheck_pow_one(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+                uint8_t resultsign,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_ssize_t shift;
+    int cmp;
+
+    if ((cmp = _mpd_cmp_abs(base, &one)) == 0) {
+        if (_mpd_isint(exp)) {
+            if (mpd_isnegative(exp)) {
+                _settriple(result, resultsign, 1, 0);
+                return 0;
+            }
+            /* 1.000**3 = 1.000000000 */
+            mpd_qmul_ssize(result, exp, -base->exp, ctx, &workstatus);
+            if (workstatus&MPD_Errors) {
+                *status |= (workstatus&MPD_Errors);
+                return 0;
+            }
+            /* digits-1 after exponentiation */
+            shift = mpd_qget_ssize(result, &workstatus);
+            /* shift is MPD_SSIZE_MAX if result is too large */
+            if (shift > ctx->prec-1) {
+                shift = ctx->prec-1;
+                *status |= MPD_Rounded;
+            }
+        }
+        else if (mpd_ispositive(base)) {
+            shift = ctx->prec-1;
+            *status |= (MPD_Inexact|MPD_Rounded);
+        }
+        else {
+            return -2; /* GCOV_NOT_REACHED */
+        }
+        if (!mpd_qshiftl(result, &one, shift, status)) {
+            return 0;
+        }
+        result->exp = -shift;
+        mpd_set_flags(result, resultsign);
+    }
+
+    return cmp;
+}
+
+/*
+ * Detect certain over/underflow of x**y.
+ * ACL2 proof: pow-bounds.lisp.
+ *
+ *   Symbols:
+ *
+ *     e: EXP_INF or EXP_CLAMP
+ *     x: base
+ *     y: exponent
+ *
+ *     omega(e) = log10(abs(e))
+ *     zeta(x)  = log10(abs(log10(x)))
+ *     theta(y) = log10(abs(y))
+ *
+ *   Upper and lower bounds:
+ *
+ *     ub_omega(e) = ceil(log10(abs(e)))
+ *     lb_theta(y) = floor(log10(abs(y)))
+ *
+ *                  | floor(log10(floor(abs(log10(x))))) if x < 1/10 or x >= 10
+ *     lb_zeta(x) = | floor(log10(abs(x-1)/10)) if 1/10 <= x < 1
+ *                  | floor(log10(abs((x-1)/100))) if 1 < x < 10
+ *
+ *   ub_omega(e) and lb_theta(y) are obviously upper and lower bounds
+ *   for omega(e) and theta(y).
+ *
+ *   lb_zeta is a lower bound for zeta(x):
+ *
+ *     x < 1/10 or x >= 10:
+ *
+ *       abs(log10(x)) >= 1, so the outer log10 is well defined. Since log10
+ *       is strictly increasing, the end result is a lower bound.
+ *
+ *     1/10 <= x < 1:
+ *
+ *       We use: log10(x) <= (x-1)/log(10)
+ *               abs(log10(x)) >= abs(x-1)/log(10)
+ *               abs(log10(x)) >= abs(x-1)/10
+ *
+ *     1 < x < 10:
+ *
+ *       We use: (x-1)/(x*log(10)) < log10(x)
+ *               abs((x-1)/100) < abs(log10(x))
+ *
+ *       XXX: abs((x-1)/10) would work, need ACL2 proof.
+ *
+ *
+ *   Let (0 < x < 1 and y < 0) or (x > 1 and y > 0).                  (H1)
+ *   Let ub_omega(exp_inf) < lb_zeta(x) + lb_theta(y)                 (H2)
+ *
+ *   Then:
+ *       log10(abs(exp_inf)) < log10(abs(log10(x))) + log10(abs(y)).   (1)
+ *                   exp_inf < log10(x) * y                            (2)
+ *               10**exp_inf < x**y                                    (3)
+ *
+ *   Let (0 < x < 1 and y > 0) or (x > 1 and y < 0).                  (H3)
+ *   Let ub_omega(exp_clamp) < lb_zeta(x) + lb_theta(y)               (H4)
+ *
+ *   Then:
+ *     log10(abs(exp_clamp)) < log10(abs(log10(x))) + log10(abs(y)).   (4)
+ *              log10(x) * y < exp_clamp                               (5)
+ *                      x**y < 10**exp_clamp                           (6)
+ *
+ */
+static mpd_ssize_t
+_lower_bound_zeta(const mpd_t *x, uint32_t *status)
+{
+    mpd_context_t maxctx;
+    MPD_NEW_STATIC(scratch,0,0,0,0);
+    mpd_ssize_t t, u;
+
+    t = mpd_adjexp(x);
+    if (t > 0) {
+        /* x >= 10 -> floor(log10(floor(abs(log10(x))))) */
+        return mpd_exp_digits(t) - 1;
+    }
+    else if (t < -1) {
+        /* x < 1/10 -> floor(log10(floor(abs(log10(x))))) */
+        return mpd_exp_digits(t+1) - 1;
+    }
+    else {
+        mpd_maxcontext(&maxctx);
+        mpd_qsub(&scratch, x, &one, &maxctx, status);
+        if (mpd_isspecial(&scratch)) {
+            mpd_del(&scratch);
+            return MPD_SSIZE_MAX;
+        }
+        u = mpd_adjexp(&scratch);
+        mpd_del(&scratch);
+
+        /* t == -1, 1/10 <= x < 1 -> floor(log10(abs(x-1)/10))
+         * t == 0,  1 < x < 10    -> floor(log10(abs(x-1)/100)) */
+        return (t == 0) ? u-2 : u-1;
+    }
+}
+
+/*
+ * Detect cases of certain overflow/underflow in the power function.
+ * Assumptions: x != 1, y != 0. The proof above is for positive x.
+ * If x is negative and y is an odd integer, x**y == -(abs(x)**y),
+ * so the analysis does not change.
+ */
+static int
+_qcheck_pow_bounds(mpd_t *result, const mpd_t *x, const mpd_t *y,
+                   uint8_t resultsign,
+                   const mpd_context_t *ctx, uint32_t *status)
+{
+    MPD_NEW_SHARED(abs_x, x);
+    mpd_ssize_t ub_omega, lb_zeta, lb_theta;
+    uint8_t sign;
+
+    mpd_set_positive(&abs_x);
+
+    lb_theta = mpd_adjexp(y);
+    lb_zeta = _lower_bound_zeta(&abs_x, status);
+    if (lb_zeta == MPD_SSIZE_MAX) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        return 1;
+    }
+
+    sign = (mpd_adjexp(&abs_x) < 0) ^ mpd_sign(y);
+    if (sign == 0) {
+        /* (0 < |x| < 1 and y < 0) or (|x| > 1 and y > 0) */
+        ub_omega = mpd_exp_digits(ctx->emax);
+        if (ub_omega < lb_zeta + lb_theta) {
+            _settriple(result, resultsign, 1, MPD_EXP_INF);
+            mpd_qfinalize(result, ctx, status);
+            return 1;
+        }
+    }
+    else {
+        /* (0 < |x| < 1 and y > 0) or (|x| > 1 and y < 0). */
+        ub_omega = mpd_exp_digits(mpd_etiny(ctx));
+        if (ub_omega < lb_zeta + lb_theta) {
+            _settriple(result, resultsign, 1, mpd_etiny(ctx)-1);
+            mpd_qfinalize(result, ctx, status);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * TODO: Implement algorithm for computing exact powers from decimal.py.
+ * In order to prevent infinite loops, this has to be called before
+ * using Ziv's strategy for correct rounding.
+ */
+/*
+static int
+_mpd_qpow_exact(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    return 0;
+}
+*/
+
+/*
+ * The power function for real exponents.
+ *   Relative error: abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1)
+ */
+static void
+_mpd_qpow_real(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+               const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_STATIC(texp,0,0,0,0);
+
+    if (!mpd_qcopy(&texp, exp, status)) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        return;
+    }
+
+    mpd_maxcontext(&workctx);
+    workctx.prec = (base->digits > ctx->prec) ? base->digits : ctx->prec;
+    workctx.prec += (4 + MPD_EXPDIGITS);
+    workctx.round = MPD_ROUND_HALF_EVEN;
+    workctx.allcr = ctx->allcr;
+
+    /*
+     * extra := MPD_EXPDIGITS = MPD_EXP_MAX_T
+     * wp := prec + 4 + extra
+     * abs(err) < 5 * 10**-wp
+     * y := log(base) * exp
+     * Calculate:
+     *   1)   e**(y * (1 + err)**2) * (1 + err)
+     *      = e**y * e**(y * (2*err + err**2)) * (1 + err)
+     *        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     * Relative error of the underlined term:
+     *   2) abs(e**(y * (2*err + err**2)) - 1)
+     * Case abs(y) >= 10**extra:
+     *   3) adjexp(y)+1 > log10(abs(y)) >= extra
+     *   This triggers the Overflow/Underflow shortcut in _mpd_qexp(),
+     *   so no further analysis is necessary.
+     * Case abs(y) < 10**extra:
+     *   4) abs(y * (2*err + err**2)) < 1/5 * 10**(-prec - 2)
+     *   Use (see _mpd_qexp):
+     *     5) abs(x) <= 9/10 * 10**-p ==> abs(e**x - 1) < 10**-p
+     *   With 2), 4) and 5):
+     *     6) abs(e**(y * (2*err + err**2)) - 1) < 10**(-prec - 2)
+     *   The complete relative error of 1) is:
+     *     7) abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1)
+     */
+    mpd_qln(result, base, &workctx, &workctx.status);
+    mpd_qmul(result, result, &texp, &workctx, &workctx.status);
+    mpd_qexp(result, result, &workctx, status);
+
+    mpd_del(&texp);
+    *status |= (workctx.status&MPD_Errors);
+    *status |= (MPD_Inexact|MPD_Rounded);
+}
+
+/* The power function: base**exp */
+void
+mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+         const mpd_context_t *ctx, uint32_t *status)
+{
+    uint8_t resultsign = 0;
+    int intexp = 0;
+    int cmp;
+
+    if (mpd_isspecial(base) || mpd_isspecial(exp)) {
+        if (mpd_qcheck_nans(result, base, exp, ctx, status)) {
+            return;
+        }
+    }
+    if (mpd_isinteger(exp)) {
+        intexp = 1;
+        resultsign = mpd_isnegative(base) && mpd_isodd(exp);
+    }
+
+    if (mpd_iszero(base)) {
+        if (mpd_iszero(exp)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+        }
+        else if (mpd_isnegative(exp)) {
+            mpd_setspecial(result, resultsign, MPD_INF);
+        }
+        else {
+            _settriple(result, resultsign, 0, 0);
+        }
+        return;
+    }
+    if (mpd_isnegative(base)) {
+        if (!intexp || mpd_isinfinite(exp)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+    }
+    if (mpd_isinfinite(exp)) {
+        /* power of one */
+        cmp = _qcheck_pow_one_inf(result, base, resultsign, ctx, status);
+        if (cmp == 0) {
+            return;
+        }
+        else {
+            cmp *= mpd_arith_sign(exp);
+            if (cmp < 0) {
+                _settriple(result, resultsign, 0, 0);
+            }
+            else {
+                mpd_setspecial(result, resultsign, MPD_INF);
+            }
+        }
+        return;
+    }
+    if (mpd_isinfinite(base)) {
+        if (mpd_iszero(exp)) {
+            _settriple(result, resultsign, 1, 0);
+        }
+        else if (mpd_isnegative(exp)) {
+            _settriple(result, resultsign, 0, 0);
+        }
+        else {
+            mpd_setspecial(result, resultsign, MPD_INF);
+        }
+        return;
+    }
+    if (mpd_iszero(exp)) {
+        _settriple(result, resultsign, 1, 0);
+        return;
+    }
+    if (_qcheck_pow_one(result, base, exp, resultsign, ctx, status) == 0) {
+        return;
+    }
+    if (_qcheck_pow_bounds(result, base, exp, resultsign, ctx, status)) {
+        return;
+    }
+
+    if (intexp) {
+        _mpd_qpow_int(result, base, exp, resultsign, ctx, status);
+    }
+    else {
+        _mpd_qpow_real(result, base, exp, ctx, status);
+        if (!mpd_isspecial(result) && _mpd_cmp(result, &one) == 0) {
+            mpd_ssize_t shift = ctx->prec-1;
+            mpd_qshiftl(result, &one, shift, status);
+            result->exp = -shift;
+        }
+        if (mpd_isinfinite(result)) {
+            /* for ROUND_DOWN, ROUND_FLOOR, etc. */
+            _settriple(result, MPD_POS, 1, MPD_EXP_INF);
+        }
+        mpd_qfinalize(result, ctx, status);
+    }
+}
+
+/*
+ * Internal function: Integer powmod with mpd_uint_t exponent, base is modified!
+ * Function can fail with MPD_Malloc_error.
+ */
+static inline void
+_mpd_qpowmod_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp,
+                  const mpd_t *mod, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+
+    mpd_maxcontext(&maxcontext);
+
+    /* resize to smaller cannot fail */
+    mpd_qcopy(result, &one, status);
+
+    while (exp > 0) {
+        if (exp & 1) {
+            _mpd_qmul_exact(result, result, base, &maxcontext, status);
+            mpd_qrem(result, result, mod, &maxcontext, status);
+        }
+        _mpd_qmul_exact(base, base, base, &maxcontext, status);
+        mpd_qrem(base, base, mod, &maxcontext, status);
+        exp >>= 1;
+    }
+}
+
+/* The powmod function: (base**exp) % mod */
+void
+mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+            const mpd_t *mod,
+            const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(tbase,0,0,0,0);
+    MPD_NEW_STATIC(texp,0,0,0,0);
+    MPD_NEW_STATIC(tmod,0,0,0,0);
+    MPD_NEW_STATIC(tmp,0,0,0,0);
+    MPD_NEW_CONST(two,0,0,1,1,1,2);
+    mpd_ssize_t tbase_exp, texp_exp;
+    mpd_ssize_t i;
+    mpd_t t;
+    mpd_uint_t r;
+    uint8_t sign;
+
+
+    if (mpd_isspecial(base) || mpd_isspecial(exp) || mpd_isspecial(mod)) {
+        if (mpd_qcheck_3nans(result, base, exp, mod, ctx, status)) {
+            return;
+        }
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+
+    if (!_mpd_isint(base) || !_mpd_isint(exp) || !_mpd_isint(mod)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_iszerocoeff(mod)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mod->digits+mod->exp > ctx->prec) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    sign = (mpd_isnegative(base)) && (mpd_isodd(exp));
+    if (mpd_iszerocoeff(exp)) {
+        if (mpd_iszerocoeff(base)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        r = (_mpd_cmp_abs(mod, &one)==0) ? 0 : 1;
+        _settriple(result, sign, r, 0);
+        return;
+    }
+    if (mpd_isnegative(exp)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+    if (mpd_iszerocoeff(base)) {
+        _settriple(result, sign, 0, 0);
+        return;
+    }
+
+    mpd_maxcontext(&maxcontext);
+
+    mpd_qrescale(&tmod, mod, 0, &maxcontext, &maxcontext.status);
+    if (maxcontext.status&MPD_Errors) {
+        mpd_seterror(result, maxcontext.status&MPD_Errors, status);
+        goto out;
+    }
+    maxcontext.status = 0;
+    mpd_set_positive(&tmod);
+
+    mpd_qround_to_int(&tbase, base, &maxcontext, status);
+    mpd_set_positive(&tbase);
+    tbase_exp = tbase.exp;
+    tbase.exp = 0;
+
+    mpd_qround_to_int(&texp, exp, &maxcontext, status);
+    texp_exp = texp.exp;
+    texp.exp = 0;
+
+    /* base = (base.int % modulo * pow(10, base.exp, modulo)) % modulo */
+    mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+    mpd_qshiftl(result, &one, tbase_exp, status);
+    mpd_qrem(result, result, &tmod, &maxcontext, status);
+    _mpd_qmul_exact(&tbase, &tbase, result, &maxcontext, status);
+    mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+    if (mpd_isspecial(&tbase) ||
+        mpd_isspecial(&texp) ||
+        mpd_isspecial(&tmod)) {
+        goto mpd_errors;
+    }
+
+    for (i = 0; i < texp_exp; i++) {
+        _mpd_qpowmod_uint(&tmp, &tbase, 10, &tmod, status);
+        t = tmp;
+        tmp = tbase;
+        tbase = t;
+    }
+    if (mpd_isspecial(&tbase)) {
+        goto mpd_errors; /* GCOV_UNLIKELY */
+    }
+
+    /* resize to smaller cannot fail */
+    mpd_qcopy(result, &one, status);
+    while (mpd_isfinite(&texp) && !mpd_iszero(&texp)) {
+        if (mpd_isodd(&texp)) {
+            _mpd_qmul_exact(result, result, &tbase, &maxcontext, status);
+            mpd_qrem(result, result, &tmod, &maxcontext, status);
+        }
+        _mpd_qmul_exact(&tbase, &tbase, &tbase, &maxcontext, status);
+        mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+        mpd_qdivint(&texp, &texp, &two, &maxcontext, status);
+    }
+    if (mpd_isspecial(&texp) || mpd_isspecial(&tbase) ||
+        mpd_isspecial(&tmod) || mpd_isspecial(result)) {
+        /* MPD_Malloc_error */
+        goto mpd_errors;
+    }
+    else {
+        mpd_set_sign(result, sign);
+    }
+
+out:
+    mpd_del(&tbase);
+    mpd_del(&texp);
+    mpd_del(&tmod);
+    mpd_del(&tmp);
+    return;
+
+mpd_errors:
+    mpd_setspecial(result, MPD_POS, MPD_NAN);
+    goto out;
+}
+
+void
+mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_ssize_t b_exp = b->exp;
+    mpd_ssize_t expdiff, shift;
+    mpd_uint_t rnd;
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+            return;
+        }
+        if (mpd_isinfinite(a) && mpd_isinfinite(b)) {
+            mpd_qcopy(result, a, status);
+            return;
+        }
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    if (b->exp > ctx->emax || b->exp < mpd_etiny(ctx)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    if (mpd_iszero(a)) {
+        _settriple(result, mpd_sign(a), 0, b->exp);
+        mpd_qfinalize(result, ctx, status);
+        return;
+    }
+
+
+    expdiff = a->exp - b->exp;
+    if (a->digits + expdiff > ctx->prec) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    if (expdiff >= 0) {
+        shift = expdiff;
+        if (!mpd_qshiftl(result, a, shift, status)) {
+            return;
+        }
+        result->exp = b_exp;
+    }
+    else {
+        /* At this point expdiff < 0 and a->digits+expdiff <= prec,
+         * so the shift before an increment will fit in prec. */
+        shift = -expdiff;
+        rnd = mpd_qshiftr(result, a, shift, status);
+        if (rnd == MPD_UINT_MAX) {
+            return;
+        }
+        result->exp = b_exp;
+        if (!_mpd_apply_round_fit(result, rnd, ctx, status)) {
+            return;
+        }
+        workstatus |= MPD_Rounded;
+        if (rnd) {
+            workstatus |= MPD_Inexact;
+        }
+    }
+
+    if (mpd_adjexp(result) > ctx->emax ||
+        mpd_adjexp(result) < mpd_etiny(ctx)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    *status |= workstatus;
+    mpd_qfinalize(result, ctx, status);
+}
+
+void
+mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+            uint32_t *status)
+{
+    mpd_ssize_t shift, maxexp, maxshift;
+    uint8_t sign_a = mpd_sign(a);
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+    if (!mpd_qcopy(result, a, status)) {
+        return;
+    }
+    mpd_qfinalize(result, ctx, status);
+    if (mpd_isspecial(result)) {
+        return;
+    }
+    if (mpd_iszero(result)) {
+        _settriple(result, sign_a, 0, 0);
+        return;
+    }
+
+    shift = mpd_trail_zeros(result);
+    maxexp = (ctx->clamp) ? mpd_etop(ctx) : ctx->emax;
+    /* After the finalizing above result->exp <= maxexp. */
+    maxshift = maxexp - result->exp;
+    shift = (shift > maxshift) ? maxshift : shift;
+
+    mpd_qshiftr_inplace(result, shift);
+    result->exp += shift;
+}
+
+void
+mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx,
+         uint32_t *status)
+{
+    MPD_NEW_STATIC(q,0,0,0,0);
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(r, a, b, ctx, status)) {
+            return;
+        }
+        if (mpd_isinfinite(a)) {
+            mpd_seterror(r, MPD_Invalid_operation, status);
+            return;
+        }
+        if (mpd_isinfinite(b)) {
+            mpd_qcopy(r, a, status);
+            mpd_qfinalize(r, ctx, status);
+            return;
+        }
+        /* debug */
+        abort(); /* GCOV_NOT_REACHED */
+    }
+    if (mpd_iszerocoeff(b)) {
+        if (mpd_iszerocoeff(a)) {
+            mpd_seterror(r, MPD_Division_undefined, status);
+        }
+        else {
+            mpd_seterror(r, MPD_Invalid_operation, status);
+        }
+        return;
+    }
+
+    _mpd_qdivmod(&q, r, a, b, ctx, status);
+    mpd_del(&q);
+    mpd_qfinalize(r, ctx, status);
+}
+
+void
+mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_context_t workctx;
+    MPD_NEW_STATIC(btmp,0,0,0,0);
+    MPD_NEW_STATIC(q,0,0,0,0);
+    mpd_ssize_t expdiff, qdigits;
+    int cmp, isodd, allnine;
+
+    assert(r != NULL); /* annotation for scan-build */
+
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        if (mpd_qcheck_nans(r, a, b, ctx, status)) {
+            return;
+        }
+        if (mpd_isinfinite(a)) {
+            mpd_seterror(r, MPD_Invalid_operation, status);
+            return;
+        }
+        if (mpd_isinfinite(b)) {
+            mpd_qcopy(r, a, status);
+            mpd_qfinalize(r, ctx, status);
+            return;
+        }
+        /* debug */
+        abort(); /* GCOV_NOT_REACHED */
+    }
+    if (mpd_iszerocoeff(b)) {
+        if (mpd_iszerocoeff(a)) {
+            mpd_seterror(r,  MPD_Division_undefined, status);
+        }
+        else {
+            mpd_seterror(r,  MPD_Invalid_operation, status);
+        }
+        return;
+    }
+
+    if (r == b) {
+        if (!mpd_qcopy(&btmp, b, status)) {
+            mpd_seterror(r, MPD_Malloc_error, status);
+            return;
+        }
+        b = &btmp;
+    }
+
+    _mpd_qdivmod(&q, r, a, b, ctx, status);
+    if (mpd_isnan(&q) || mpd_isnan(r)) {
+        goto finish;
+    }
+    if (mpd_iszerocoeff(r)) {
+        goto finish;
+    }
+
+    expdiff = mpd_adjexp(b) - mpd_adjexp(r);
+    if (-1 <= expdiff && expdiff <= 1) {
+
+        allnine = mpd_coeff_isallnine(&q);
+        qdigits = q.digits;
+        isodd = mpd_isodd(&q);
+
+        mpd_maxcontext(&workctx);
+        if (mpd_sign(a) == mpd_sign(b)) {
+            /* sign(r) == sign(b) */
+            _mpd_qsub(&q, r, b, &workctx, &workctx.status);
+        }
+        else {
+            /* sign(r) != sign(b) */
+            _mpd_qadd(&q, r, b, &workctx, &workctx.status);
+        }
+
+        if (workctx.status&MPD_Errors) {
+            mpd_seterror(r, workctx.status&MPD_Errors, status);
+            goto finish;
+        }
+
+        cmp = _mpd_cmp_abs(&q, r);
+        if (cmp < 0 || (cmp == 0 && isodd)) {
+            /* abs(r) > abs(b)/2 or abs(r) == abs(b)/2 and isodd(quotient) */
+            if (allnine && qdigits == ctx->prec) {
+                /* abs(quotient) + 1 == 10**prec */
+                mpd_seterror(r, MPD_Division_impossible, status);
+                goto finish;
+            }
+            mpd_qcopy(r, &q, status);
+        }
+    }
+
+
+finish:
+    mpd_del(&btmp);
+    mpd_del(&q);
+    mpd_qfinalize(r, ctx, status);
+}
+
+static void
+_mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+              const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t expdiff, shift;
+    mpd_uint_t rnd;
+
+    if (mpd_isspecial(a)) {
+        mpd_qcopy(result, a, status);
+        return;
+    }
+
+    if (mpd_iszero(a)) {
+        _settriple(result, mpd_sign(a), 0, exp);
+        return;
+    }
+
+    expdiff = a->exp - exp;
+    if (expdiff >= 0) {
+        shift = expdiff;
+        if (a->digits + shift > MPD_MAX_PREC+1) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        if (!mpd_qshiftl(result, a, shift, status)) {
+            return;
+        }
+        result->exp = exp;
+    }
+    else {
+        shift = -expdiff;
+        rnd = mpd_qshiftr(result, a, shift, status);
+        if (rnd == MPD_UINT_MAX) {
+            return;
+        }
+        result->exp = exp;
+        _mpd_apply_round_excess(result, rnd, ctx, status);
+        *status |= MPD_Rounded;
+        if (rnd) {
+            *status |= MPD_Inexact;
+        }
+    }
+
+    if (mpd_issubnormal(result, ctx)) {
+        *status |= MPD_Subnormal;
+    }
+}
+
+/*
+ * Rescale a number so that it has exponent 'exp'. Does not regard context
+ * precision, emax, emin, but uses the rounding mode. Special numbers are
+ * quietly copied. Restrictions:
+ *
+ *     MPD_MIN_ETINY <= exp <= MPD_MAX_EMAX+1
+ *     result->digits <= MPD_MAX_PREC+1
+ */
+void
+mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+             const mpd_context_t *ctx, uint32_t *status)
+{
+    if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    _mpd_qrescale(result, a, exp, ctx, status);
+}
+
+/*
+ * Same as mpd_qrescale, but with relaxed restrictions. The result of this
+ * function should only be used for formatting a number and never as input
+ * for other operations.
+ *
+ *     MPD_MIN_ETINY-MPD_MAX_PREC <= exp <= MPD_MAX_EMAX+1
+ *     result->digits <= MPD_MAX_PREC+1
+ */
+void
+mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+                 const mpd_context_t *ctx, uint32_t *status)
+{
+    if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY-MPD_MAX_PREC) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    _mpd_qrescale(result, a, exp, ctx, status);
+}
+
+/* Round to an integer according to 'action' and ctx->round. */
+enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC};
+static void
+_mpd_qround_to_integral(int action, mpd_t *result, const mpd_t *a,
+                        const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_uint_t rnd;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        mpd_qcopy(result, a, status);
+        return;
+    }
+    if (a->exp >= 0) {
+        mpd_qcopy(result, a, status);
+        return;
+    }
+    if (mpd_iszerocoeff(a)) {
+        _settriple(result, mpd_sign(a), 0, 0);
+        return;
+    }
+
+    rnd = mpd_qshiftr(result, a, -a->exp, status);
+    if (rnd == MPD_UINT_MAX) {
+        return;
+    }
+    result->exp = 0;
+
+    if (action == TO_INT_EXACT || action == TO_INT_SILENT) {
+        _mpd_apply_round_excess(result, rnd, ctx, status);
+        if (action == TO_INT_EXACT) {
+            *status |= MPD_Rounded;
+            if (rnd) {
+                *status |= MPD_Inexact;
+            }
+        }
+    }
+}
+
+void
+mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+                   uint32_t *status)
+{
+    (void)_mpd_qround_to_integral(TO_INT_EXACT, result, a, ctx, status);
+}
+
+void
+mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+                  uint32_t *status)
+{
+    (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, ctx, status);
+}
+
+void
+mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    if (mpd_isspecial(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    (void)_mpd_qround_to_integral(TO_INT_TRUNC, result, a, ctx, status);
+}
+
+void
+mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    mpd_context_t workctx;
+    mpd_workcontext(&workctx, ctx);
+
+    if (mpd_isspecial(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    workctx.round = MPD_ROUND_FLOOR;
+    (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a,
+                                  &workctx, status);
+}
+
+void
+mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+          uint32_t *status)
+{
+    mpd_context_t workctx;
+    mpd_workcontext(&workctx, ctx);
+
+    if (mpd_isspecial(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    workctx.round = MPD_ROUND_CEILING;
+    (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a,
+                                  &workctx, status);
+}
+
+int
+mpd_same_quantum(const mpd_t *a, const mpd_t *b)
+{
+    if (mpd_isspecial(a) || mpd_isspecial(b)) {
+        return ((mpd_isnan(a) && mpd_isnan(b)) ||
+                (mpd_isinfinite(a) && mpd_isinfinite(b)));
+    }
+
+    return a->exp == b->exp;
+}
+
+/* Schedule the increase in precision for the Newton iteration. */
+static inline int
+recpr_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2],
+                    mpd_ssize_t maxprec, mpd_ssize_t initprec)
+{
+    mpd_ssize_t k;
+    int i;
+
+    assert(maxprec > 0 && initprec > 0);
+    if (maxprec <= initprec) return -1;
+
+    i = 0; k = maxprec;
+    do {
+        k = (k+1) / 2;
+        klist[i++] = k;
+    } while (k > initprec);
+
+    return i-1;
+}
+
+/*
+ * Initial approximation for the reciprocal:
+ *    k_0 := MPD_RDIGITS-2
+ *    z_0 := 10**(-k_0) * floor(10**(2*k_0 + 2) / floor(v * 10**(k_0 + 2)))
+ * Absolute error:
+ *    |1/v - z_0| < 10**(-k_0)
+ * ACL2 proof: maxerror-inverse-approx
+ */
+static void
+_mpd_qreciprocal_approx(mpd_t *z, const mpd_t *v, uint32_t *status)
+{
+    mpd_uint_t p10data[2] = {0, mpd_pow10[MPD_RDIGITS-2]};
+    mpd_uint_t dummy, word;
+    int n;
+
+    assert(v->exp == -v->digits);
+
+    _mpd_get_msdigits(&dummy, &word, v, MPD_RDIGITS);
+    n = mpd_word_digits(word);
+    word *= mpd_pow10[MPD_RDIGITS-n];
+
+    mpd_qresize(z, 2, status);
+    (void)_mpd_shortdiv(z->data, p10data, 2, word);
+
+    mpd_clear_flags(z);
+    z->exp = -(MPD_RDIGITS-2);
+    z->len = (z->data[1] == 0) ? 1 : 2;
+    mpd_setdigits(z);
+}
+
+/*
+ * Reciprocal, calculated with Newton's Method. Assumption: result != a.
+ * NOTE: The comments in the function show that certain operations are
+ * exact. The proof for the maximum error is too long to fit in here.
+ * ACL2 proof: maxerror-inverse-complete
+ */
+static void
+_mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+                 uint32_t *status)
+{
+    mpd_context_t varcontext, maxcontext;
+    mpd_t *z = result;         /* current approximation */
+    mpd_t *v;                  /* a, normalized to a number between 0.1 and 1 */
+    MPD_NEW_SHARED(vtmp, a);   /* v shares data with a */
+    MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */
+    MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */
+    MPD_NEW_CONST(two,0,0,1,1,1,2); /* const 2 */
+    mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+    mpd_ssize_t adj, maxprec, initprec;
+    uint8_t sign = mpd_sign(a);
+    int i;
+
+    assert(result != a);
+
+    v = &vtmp;
+    mpd_clear_flags(v);
+    adj = v->digits + v->exp;
+    v->exp = -v->digits;
+
+    /* Initial approximation */
+    _mpd_qreciprocal_approx(z, v, status);
+
+    mpd_maxcontext(&varcontext);
+    mpd_maxcontext(&maxcontext);
+    varcontext.round = maxcontext.round = MPD_ROUND_TRUNC;
+    varcontext.emax = maxcontext.emax = MPD_MAX_EMAX + 100;
+    varcontext.emin = maxcontext.emin = MPD_MIN_EMIN - 100;
+    maxcontext.prec = MPD_MAX_PREC + 100;
+
+    maxprec = ctx->prec;
+    maxprec += 2;
+    initprec = MPD_RDIGITS-3;
+
+    i = recpr_schedule_prec(klist, maxprec, initprec);
+    for (; i >= 0; i--) {
+         /* Loop invariant: z->digits <= klist[i]+7 */
+         /* Let s := z**2, exact result */
+        _mpd_qmul_exact(&s, z, z, &maxcontext, status);
+        varcontext.prec = 2*klist[i] + 5;
+        if (v->digits > varcontext.prec) {
+            /* Let t := v, truncated to n >= 2*k+5 fraction digits */
+            mpd_qshiftr(&t, v, v->digits-varcontext.prec, status);
+            t.exp = -varcontext.prec;
+            /* Let t := trunc(v)*s, truncated to n >= 2*k+1 fraction digits */
+            mpd_qmul(&t, &t, &s, &varcontext, status);
+        }
+        else { /* v->digits <= 2*k+5 */
+            /* Let t := v*s, truncated to n >= 2*k+1 fraction digits */
+            mpd_qmul(&t, v, &s, &varcontext, status);
+        }
+        /* Let s := 2*z, exact result */
+        _mpd_qmul_exact(&s, z, &two, &maxcontext, status);
+        /* s.digits < t.digits <= 2*k+5, |adjexp(s)-adjexp(t)| <= 1,
+         * so the subtraction generates at most 2*k+6 <= klist[i+1]+7
+         * digits. The loop invariant is preserved. */
+        _mpd_qsub_exact(z, &s, &t, &maxcontext, status);
+    }
+
+    if (!mpd_isspecial(z)) {
+        z->exp -= adj;
+        mpd_set_flags(z, sign);
+    }
+
+    mpd_del(&s);
+    mpd_del(&t);
+    mpd_qfinalize(z, ctx, status);
+}
+
+/*
+ * Internal function for large numbers:
+ *
+ *     q, r = divmod(coeff(a), coeff(b))
+ *
+ * Strategy: Multiply the dividend by the reciprocal of the divisor. The
+ * inexact result is fixed by a small loop, using at most one iteration.
+ *
+ * ACL2 proofs:
+ * ------------
+ *    1) q is a natural number.  (ndivmod-quotient-natp)
+ *    2) r is a natural number.  (ndivmod-remainder-natp)
+ *    3) a = q * b + r           (ndivmod-q*b+r==a)
+ *    4) r < b                   (ndivmod-remainder-<-b)
+ */
+static void
+_mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+                  uint32_t *status)
+{
+    mpd_context_t workctx;
+    mpd_t *qq = q, *rr = r;
+    mpd_t aa, bb;
+    int k;
+
+    _mpd_copy_shared(&aa, a);
+    _mpd_copy_shared(&bb, b);
+
+    mpd_set_positive(&aa);
+    mpd_set_positive(&bb);
+    aa.exp = 0;
+    bb.exp = 0;
+
+    if (q == a || q == b) {
+        if ((qq = mpd_qnew()) == NULL) {
+            *status |= MPD_Malloc_error;
+            goto nanresult;
+        }
+    }
+    if (r == a || r == b) {
+        if ((rr = mpd_qnew()) == NULL) {
+            *status |= MPD_Malloc_error;
+            goto nanresult;
+        }
+    }
+
+    mpd_maxcontext(&workctx);
+
+    /* Let prec := adigits - bdigits + 4 */
+    workctx.prec = a->digits - b->digits + 1 + 3;
+    if (a->digits > MPD_MAX_PREC || workctx.prec > MPD_MAX_PREC) {
+        *status |= MPD_Division_impossible;
+        goto nanresult;
+    }
+
+    /* Let x := _mpd_qreciprocal(b, prec)
+     * Then x is bounded by:
+     *    1) 1/b - 10**(-prec - bdigits) < x < 1/b + 10**(-prec - bdigits)
+     *    2) 1/b - 10**(-adigits - 4) < x < 1/b + 10**(-adigits - 4)
+     */
+    _mpd_qreciprocal(rr, &bb, &workctx, &workctx.status);
+
+    /* Get an estimate for the quotient. Let q := a * x
+     * Then q is bounded by:
+     *    3) a/b - 10**-4 < q < a/b + 10**-4
+     */
+    _mpd_qmul(qq, &aa, rr, &workctx, &workctx.status);
+    /* Truncate q to an integer:
+     *    4) a/b - 2 < trunc(q) < a/b + 1
+     */
+    mpd_qtrunc(qq, qq, &workctx, &workctx.status);
+
+    workctx.prec = aa.digits + 3;
+    workctx.emax = MPD_MAX_EMAX + 3;
+    workctx.emin = MPD_MIN_EMIN - 3;
+    /* Multiply the estimate for q by b:
+     *    5) a - 2 * b < trunc(q) * b < a + b
+     */
+    _mpd_qmul(rr, &bb, qq, &workctx, &workctx.status);
+    /* Get the estimate for r such that a = q * b + r. */
+    _mpd_qsub_exact(rr, &aa, rr, &workctx, &workctx.status);
+
+    /* Fix the result. At this point -b < r < 2*b, so the correction loop
+       takes at most one iteration. */
+    for (k = 0;; k++) {
+        if (mpd_isspecial(qq) || mpd_isspecial(rr)) {
+            *status |= (workctx.status&MPD_Errors);
+            goto nanresult;
+        }
+        if (k > 2) { /* Allow two iterations despite the proof. */
+            mpd_err_warn("libmpdec: internal error in "       /* GCOV_NOT_REACHED */
+                         "_mpd_base_ndivmod: please report"); /* GCOV_NOT_REACHED */
+            *status |= MPD_Invalid_operation;                 /* GCOV_NOT_REACHED */
+            goto nanresult;                                   /* GCOV_NOT_REACHED */
+        }
+        /* r < 0 */
+        else if (_mpd_cmp(&zero, rr) == 1) {
+            _mpd_qadd_exact(rr, rr, &bb, &workctx, &workctx.status);
+            _mpd_qadd_exact(qq, qq, &minus_one, &workctx, &workctx.status);
+        }
+        /* 0 <= r < b */
+        else if (_mpd_cmp(rr, &bb) == -1) {
+            break;
+        }
+        /* r >= b */
+        else {
+            _mpd_qsub_exact(rr, rr, &bb, &workctx, &workctx.status);
+            _mpd_qadd_exact(qq, qq, &one, &workctx, &workctx.status);
+        }
+    }
+
+    if (qq != q) {
+        if (!mpd_qcopy(q, qq, status)) {
+            goto nanresult; /* GCOV_UNLIKELY */
+        }
+        mpd_del(qq);
+    }
+    if (rr != r) {
+        if (!mpd_qcopy(r, rr, status)) {
+            goto nanresult; /* GCOV_UNLIKELY */
+        }
+        mpd_del(rr);
+    }
+
+    *status |= (workctx.status&MPD_Errors);
+    return;
+
+
+nanresult:
+    if (qq && qq != q) mpd_del(qq);
+    if (rr && rr != r) mpd_del(rr);
+    mpd_setspecial(q, MPD_POS, MPD_NAN);
+    mpd_setspecial(r, MPD_POS, MPD_NAN);
+}
+
+/* LIBMPDEC_ONLY */
+/*
+ * Schedule the optimal precision increase for the Newton iteration.
+ *   v := input operand
+ *   z_0 := initial approximation
+ *   initprec := natural number such that abs(sqrt(v) - z_0) < 10**-initprec
+ *   maxprec := target precision
+ *
+ * For convenience the output klist contains the elements in reverse order:
+ *   klist := [k_n-1, ..., k_0], where
+ *     1) k_0 <= initprec and
+ *     2) abs(sqrt(v) - result) < 10**(-2*k_n-1 + 2) <= 10**-maxprec.
+ */
+static inline int
+invroot_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2],
+                      mpd_ssize_t maxprec, mpd_ssize_t initprec)
+{
+    mpd_ssize_t k;
+    int i;
+
+    assert(maxprec >= 3 && initprec >= 3);
+    if (maxprec <= initprec) return -1;
+
+    i = 0; k = maxprec;
+    do {
+        k = (k+3) / 2;
+        klist[i++] = k;
+    } while (k > initprec);
+
+    return i-1;
+}
+
+/*
+ * Initial approximation for the inverse square root function.
+ *   Input:
+ *     v := rational number, with 1 <= v < 100
+ *     vhat := floor(v * 10**6)
+ *   Output:
+ *     z := approximation to 1/sqrt(v), such that abs(z - 1/sqrt(v)) < 10**-3.
+ */
+static inline void
+_invroot_init_approx(mpd_t *z, mpd_uint_t vhat)
+{
+    mpd_uint_t lo = 1000;
+    mpd_uint_t hi = 10000;
+    mpd_uint_t a, sq;
+
+    assert(lo*lo <= vhat && vhat < (hi+1)*(hi+1));
+
+    for(;;) {
+        a = (lo + hi) / 2;
+        sq = a * a;
+        if (vhat >= sq) {
+            if (vhat < sq + 2*a + 1) {
+                break;
+            }
+            lo = a + 1;
+        }
+        else {
+            hi = a - 1;
+        }
+    }
+
+    /*
+     * After the binary search we have:
+     *  1) a**2 <= floor(v * 10**6) < (a + 1)**2
+     * This implies:
+     *  2) a**2 <= v * 10**6 < (a + 1)**2
+     *  3) a <= sqrt(v) * 10**3 < a + 1
+     * Since 10**3 <= a:
+     *  4) 0 <= 10**prec/a - 1/sqrt(v) < 10**-prec
+     * We have:
+     *  5) 10**3/a - 10**-3 < floor(10**9/a) * 10**-6 <= 10**3/a
+     * Merging 4) and 5):
+     *  6) abs(floor(10**9/a) * 10**-6 - 1/sqrt(v)) < 10**-3
+     */
+    mpd_minalloc(z);
+    mpd_clear_flags(z);
+    z->data[0] = 1000000000UL / a;
+    z->len = 1;
+    z->exp = -6;
+    mpd_setdigits(z);
+}
+
+/*
+ * Set 'result' to 1/sqrt(a).
+ *   Relative error: abs(result - 1/sqrt(a)) < 10**-prec * 1/sqrt(a)
+ */
+static void
+_mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+              uint32_t *status)
+{
+    uint32_t workstatus = 0;
+    mpd_context_t varcontext, maxcontext;
+    mpd_t *z = result;         /* current approximation */
+    mpd_t *v;                  /* a, normalized to a number between 1 and 100 */
+    MPD_NEW_SHARED(vtmp, a);   /* by default v will share data with a */
+    MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */
+    MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */
+    MPD_NEW_CONST(one_half,0,-1,1,1,1,5);
+    MPD_NEW_CONST(three,0,0,1,1,1,3);
+    mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+    mpd_ssize_t ideal_exp, shift;
+    mpd_ssize_t adj, tz;
+    mpd_ssize_t maxprec, fracdigits;
+    mpd_uint_t vhat, dummy;
+    int i, n;
+
+
+    ideal_exp = -(a->exp - (a->exp & 1)) / 2;
+
+    v = &vtmp;
+    if (result == a) {
+        if ((v = mpd_qncopy(a)) == NULL) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            return;
+        }
+    }
+
+    /* normalize a to 1 <= v < 100 */
+    if ((v->digits+v->exp) & 1) {
+        fracdigits = v->digits - 1;
+        v->exp = -fracdigits;
+        n = (v->digits > 7) ? 7 : (int)v->digits;
+        /* Let vhat := floor(v * 10**(2*initprec)) */
+        _mpd_get_msdigits(&dummy, &vhat, v, n);
+        if (n < 7) {
+            vhat *= mpd_pow10[7-n];
+        }
+    }
+    else {
+        fracdigits = v->digits - 2;
+        v->exp = -fracdigits;
+        n = (v->digits > 8) ? 8 : (int)v->digits;
+        /* Let vhat := floor(v * 10**(2*initprec)) */
+        _mpd_get_msdigits(&dummy, &vhat, v, n);
+        if (n < 8) {
+            vhat *= mpd_pow10[8-n];
+        }
+    }
+    adj = (a->exp-v->exp) / 2;
+
+    /* initial approximation */
+    _invroot_init_approx(z, vhat);
+
+    mpd_maxcontext(&maxcontext);
+    mpd_maxcontext(&varcontext);
+    varcontext.round = MPD_ROUND_TRUNC;
+    maxprec = ctx->prec + 1;
+
+    /* initprec == 3 */
+    i = invroot_schedule_prec(klist, maxprec, 3);
+    for (; i >= 0; i--) {
+        varcontext.prec = 2*klist[i]+2;
+        mpd_qmul(&s, z, z, &maxcontext, &workstatus);
+        if (v->digits > varcontext.prec) {
+            shift = v->digits - varcontext.prec;
+            mpd_qshiftr(&t, v, shift, &workstatus);
+            t.exp += shift;
+            mpd_qmul(&t, &t, &s, &varcontext, &workstatus);
+        }
+        else {
+            mpd_qmul(&t, v, &s, &varcontext, &workstatus);
+        }
+        mpd_qsub(&t, &three, &t, &maxcontext, &workstatus);
+        mpd_qmul(z, z, &t, &varcontext, &workstatus);
+        mpd_qmul(z, z, &one_half, &maxcontext, &workstatus);
+    }
+
+    z->exp -= adj;
+
+    tz = mpd_trail_zeros(result);
+    shift = ideal_exp - result->exp;
+    shift = (tz > shift) ? shift : tz;
+    if (shift > 0) {
+        mpd_qshiftr_inplace(result, shift);
+        result->exp += shift;
+    }
+
+
+    mpd_del(&s);
+    mpd_del(&t);
+    if (v != &vtmp) mpd_del(v);
+    *status |= (workstatus&MPD_Errors);
+    *status |= (MPD_Rounded|MPD_Inexact);
+}
+
+void
+mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+             uint32_t *status)
+{
+    mpd_context_t workctx;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        if (mpd_isnegative(a)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        /* positive infinity */
+        _settriple(result, MPD_POS, 0, mpd_etiny(ctx));
+        *status |= MPD_Clamped;
+        return;
+    }
+    if (mpd_iszero(a)) {
+        mpd_setspecial(result, mpd_sign(a), MPD_INF);
+        *status |= MPD_Division_by_zero;
+        return;
+    }
+    if (mpd_isnegative(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    mpd_workcontext(&workctx, ctx);
+    workctx.prec += 2;
+    workctx.round = MPD_ROUND_HALF_EVEN;
+    _mpd_qinvroot(result, a, &workctx, status);
+    mpd_qfinalize(result, ctx, status);
+}
+/* END LIBMPDEC_ONLY */
+
+/* Algorithm from decimal.py */
+static void
+_mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+           uint32_t *status)
+{
+    mpd_context_t maxcontext;
+    MPD_NEW_STATIC(c,0,0,0,0);
+    MPD_NEW_STATIC(q,0,0,0,0);
+    MPD_NEW_STATIC(r,0,0,0,0);
+    MPD_NEW_CONST(two,0,0,1,1,1,2);
+    mpd_ssize_t prec, ideal_exp;
+    mpd_ssize_t l, shift;
+    int exact = 0;
+
+
+    ideal_exp = (a->exp - (a->exp & 1)) / 2;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_qcheck_nan(result, a, ctx, status)) {
+            return;
+        }
+        if (mpd_isnegative(a)) {
+            mpd_seterror(result, MPD_Invalid_operation, status);
+            return;
+        }
+        mpd_setspecial(result, MPD_POS, MPD_INF);
+        return;
+    }
+    if (mpd_iszero(a)) {
+        _settriple(result, mpd_sign(a), 0, ideal_exp);
+        mpd_qfinalize(result, ctx, status);
+        return;
+    }
+    if (mpd_isnegative(a)) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    mpd_maxcontext(&maxcontext);
+    prec = ctx->prec + 1;
+
+    if (!mpd_qcopy(&c, a, status)) {
+        goto malloc_error;
+    }
+    c.exp = 0;
+
+    if (a->exp & 1) {
+        if (!mpd_qshiftl(&c, &c, 1, status)) {
+            goto malloc_error;
+        }
+        l = (a->digits >> 1) + 1;
+    }
+    else {
+        l = (a->digits + 1) >> 1;
+    }
+
+    shift = prec - l;
+    if (shift >= 0) {
+        if (!mpd_qshiftl(&c, &c, 2*shift, status)) {
+            goto malloc_error;
+        }
+        exact = 1;
+    }
+    else {
+        exact = !mpd_qshiftr_inplace(&c, -2*shift);
+    }
+
+    ideal_exp -= shift;
+
+    /* find result = floor(sqrt(c)) using Newton's method */
+    if (!mpd_qshiftl(result, &one, prec, status)) {
+        goto malloc_error;
+    }
+
+    while (1) {
+        _mpd_qdivmod(&q, &r, &c, result, &maxcontext, &maxcontext.status);
+        if (mpd_isspecial(result) || mpd_isspecial(&q)) {
+            mpd_seterror(result, maxcontext.status&MPD_Errors, status);
+            goto out;
+        }
+        if (_mpd_cmp(result, &q) <= 0) {
+            break;
+        }
+        _mpd_qadd_exact(result, result, &q, &maxcontext, &maxcontext.status);
+        if (mpd_isspecial(result)) {
+            mpd_seterror(result, maxcontext.status&MPD_Errors, status);
+            goto out;
+        }
+        _mpd_qdivmod(result, &r, result, &two, &maxcontext, &maxcontext.status);
+    }
+
+    if (exact) {
+        _mpd_qmul_exact(&r, result, result, &maxcontext, &maxcontext.status);
+        if (mpd_isspecial(&r)) {
+            mpd_seterror(result, maxcontext.status&MPD_Errors, status);
+            goto out;
+        }
+        exact = (_mpd_cmp(&r, &c) == 0);
+    }
+
+    if (exact) {
+        if (shift >= 0) {
+            mpd_qshiftr_inplace(result, shift);
+        }
+        else {
+            if (!mpd_qshiftl(result, result, -shift, status)) {
+                goto malloc_error;
+            }
+        }
+        ideal_exp += shift;
+    }
+    else {
+        int lsd = (int)mpd_lsd(result->data[0]);
+        if (lsd == 0 || lsd == 5) {
+            result->data[0] += 1;
+        }
+    }
+
+    result->exp = ideal_exp;
+
+
+out:
+    mpd_del(&c);
+    mpd_del(&q);
+    mpd_del(&r);
+    mpd_workcontext(&maxcontext, ctx);
+    maxcontext.round = MPD_ROUND_HALF_EVEN;
+    mpd_qfinalize(result, &maxcontext, status);
+    return;
+
+malloc_error:
+    mpd_seterror(result, MPD_Malloc_error, status);
+    goto out;
+}
+
+void
+mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+          uint32_t *status)
+{
+    MPD_NEW_STATIC(aa,0,0,0,0);
+    uint32_t xstatus = 0;
+
+    if (result == a) {
+        if (!mpd_qcopy(&aa, a, status)) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            goto out;
+        }
+        a = &aa;
+    }
+
+    _mpd_qsqrt(result, a, ctx, &xstatus);
+
+    if (xstatus & (MPD_Malloc_error|MPD_Division_impossible)) {
+        /* The above conditions can occur at very high context precisions
+         * if intermediate values get too large. Retry the operation with
+         * a lower context precision in case the result is exact.
+         *
+         * If the result is exact, an upper bound for the number of digits
+         * is the number of digits in the input.
+         *
+         * NOTE: sqrt(40e9) = 2.0e+5 /\ digits(40e9) = digits(2.0e+5) = 2
+         */
+        uint32_t ystatus = 0;
+        mpd_context_t workctx;
+        mpd_workcontext(&workctx, ctx);
+
+        workctx.prec = a->digits;
+        if (workctx.prec >= ctx->prec) {
+            *status |= (xstatus|MPD_Errors);
+            goto out; /* No point in repeating this, keep the original error. */
+        }
+
+        _mpd_qsqrt(result, a, &workctx, &ystatus);
+        if (ystatus != 0) {
+            ystatus = *status | ((xstatus|ystatus)&MPD_Errors);
+            mpd_seterror(result, ystatus, status);
+        }
+    }
+    else {
+        *status |= xstatus;
+    }
+
+out:
+    mpd_del(&aa);
+}
+
+
+/******************************************************************************/
+/*                              Base conversions                              */
+/******************************************************************************/
+
+/* Space needed to represent an integer mpd_t in base 'base'. */
+size_t
+mpd_sizeinbase(const mpd_t *a, uint32_t base)
+{
+    double x;
+    size_t digits;
+    double upper_bound;
+
+    assert(mpd_isinteger(a));
+    assert(base >= 2);
+
+    if (mpd_iszero(a)) {
+        return 1;
+    }
+
+    digits = a->digits+a->exp;
+
+#ifdef CONFIG_64
+    /* ceil(2711437152599294 / log10(2)) + 4 == 2**53 */
+    if (digits > 2711437152599294ULL) {
+        return SIZE_MAX;
+    }
+
+    upper_bound = (double)((1ULL<<53)-1);
+#else
+    upper_bound = (double)(SIZE_MAX-1);
+#endif
+
+    x = (double)digits / log10(base);
+    return (x > upper_bound) ? SIZE_MAX : (size_t)x + 1;
+}
+
+/* Space needed to import a base 'base' integer of length 'srclen'. */
+static mpd_ssize_t
+_mpd_importsize(size_t srclen, uint32_t base)
+{
+    double x;
+    double upper_bound;
+
+    assert(srclen > 0);
+    assert(base >= 2);
+
+#if SIZE_MAX == UINT64_MAX
+    if (srclen > (1ULL<<53)) {
+        return MPD_SSIZE_MAX;
+    }
+
+    assert((1ULL<<53) <= MPD_MAXIMPORT);
+    upper_bound = (double)((1ULL<<53)-1);
+#else
+    upper_bound = MPD_MAXIMPORT-1;
+#endif
+
+    x = (double)srclen * (log10(base)/MPD_RDIGITS);
+    return (x > upper_bound) ? MPD_SSIZE_MAX : (mpd_ssize_t)x + 1;
+}
+
+static uint8_t
+mpd_resize_u16(uint16_t **w, size_t nmemb)
+{
+    uint8_t err = 0;
+    *w = mpd_realloc(*w, nmemb, sizeof **w, &err);
+    return !err;
+}
+
+static uint8_t
+mpd_resize_u32(uint32_t **w, size_t nmemb)
+{
+    uint8_t err = 0;
+    *w = mpd_realloc(*w, nmemb, sizeof **w, &err);
+    return !err;
+}
+
+static size_t
+_baseconv_to_u16(uint16_t **w, size_t wlen, mpd_uint_t wbase,
+                 mpd_uint_t *u, mpd_ssize_t ulen)
+{
+    size_t n = 0;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(wbase <= (1U<<16));
+
+    do {
+        if (n >= wlen) {
+            if (!mpd_resize_u16(w, n+1)) {
+                return SIZE_MAX;
+            }
+            wlen = n+1;
+        }
+        (*w)[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase);
+        /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */
+        ulen = _mpd_real_size(u, ulen);
+
+    } while (u[ulen-1] != 0);
+
+    return n;
+}
+
+static size_t
+_coeff_from_u16(mpd_t *w, mpd_ssize_t wlen,
+                const mpd_uint_t *u, size_t ulen, uint32_t ubase,
+                uint32_t *status)
+{
+    mpd_ssize_t n = 0;
+    mpd_uint_t carry;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(ubase <= (1U<<16));
+
+    w->data[n++] = u[--ulen];
+    while (--ulen != SIZE_MAX) {
+        carry = _mpd_shortmul_c(w->data, w->data, n, ubase);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_qresize(w, n+1, status)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            w->data[n++] = carry;
+        }
+        carry = _mpd_shortadd(w->data, n, u[ulen]);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_qresize(w, n+1, status)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            w->data[n++] = carry;
+        }
+    }
+
+    return n;
+}
+
+/* target base wbase < source base ubase */
+static size_t
+_baseconv_to_smaller(uint32_t **w, size_t wlen, uint32_t wbase,
+                     mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase)
+{
+    size_t n = 0;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(wbase < ubase);
+
+    do {
+        if (n >= wlen) {
+            if (!mpd_resize_u32(w, n+1)) {
+                return SIZE_MAX;
+            }
+            wlen = n+1;
+        }
+        (*w)[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase);
+        /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */
+        ulen = _mpd_real_size(u, ulen);
+
+    } while (u[ulen-1] != 0);
+
+    return n;
+}
+
+#ifdef CONFIG_32
+/* target base 'wbase' == source base 'ubase' */
+static size_t
+_copy_equal_base(uint32_t **w, size_t wlen,
+                 const uint32_t *u, size_t ulen)
+{
+    if (wlen < ulen) {
+        if (!mpd_resize_u32(w, ulen)) {
+            return SIZE_MAX;
+        }
+    }
+
+    memcpy(*w, u, ulen * (sizeof **w));
+    return ulen;
+}
+
+/* target base 'wbase' > source base 'ubase' */
+static size_t
+_baseconv_to_larger(uint32_t **w, size_t wlen, mpd_uint_t wbase,
+                    const mpd_uint_t *u, size_t ulen, mpd_uint_t ubase)
+{
+    size_t n = 0;
+    mpd_uint_t carry;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(ubase < wbase);
+
+    (*w)[n++] = u[--ulen];
+    while (--ulen != SIZE_MAX) {
+        carry = _mpd_shortmul_b(*w, *w, n, ubase, wbase);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_resize_u32(w, n+1)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            (*w)[n++] = carry;
+        }
+        carry = _mpd_shortadd_b(*w, n, u[ulen], wbase);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_resize_u32(w, n+1)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            (*w)[n++] = carry;
+        }
+    }
+
+    return n;
+}
+
+/* target base wbase < source base ubase */
+static size_t
+_coeff_from_larger_base(mpd_t *w, size_t wlen, mpd_uint_t wbase,
+                        mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase,
+                        uint32_t *status)
+{
+    size_t n = 0;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(wbase < ubase);
+
+    do {
+        if (n >= wlen) {
+            if (!mpd_qresize(w, n+1, status)) {
+                return SIZE_MAX;
+            }
+            wlen = n+1;
+        }
+        w->data[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase);
+        /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */
+        ulen = _mpd_real_size(u, ulen);
+
+    } while (u[ulen-1] != 0);
+
+    return n;
+}
+#endif
+
+/* target base 'wbase' > source base 'ubase' */
+static size_t
+_coeff_from_smaller_base(mpd_t *w, mpd_ssize_t wlen, mpd_uint_t wbase,
+                         const uint32_t *u, size_t ulen, mpd_uint_t ubase,
+                         uint32_t *status)
+{
+    mpd_ssize_t n = 0;
+    mpd_uint_t carry;
+
+    assert(wlen > 0 && ulen > 0);
+    assert(wbase > ubase);
+
+    w->data[n++] = u[--ulen];
+    while (--ulen != SIZE_MAX) {
+        carry = _mpd_shortmul_b(w->data, w->data, n, ubase, wbase);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_qresize(w, n+1, status)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            w->data[n++] = carry;
+        }
+        carry = _mpd_shortadd_b(w->data, n, u[ulen], wbase);
+        if (carry) {
+            if (n >= wlen) {
+                if (!mpd_qresize(w, n+1, status)) {
+                    return SIZE_MAX;
+                }
+                wlen = n+1;
+            }
+            w->data[n++] = carry;
+        }
+    }
+
+    return n;
+}
+
+/*
+ * Convert an integer mpd_t to a multiprecision integer with base <= 2**16.
+ * The least significant word of the result is (*rdata)[0].
+ *
+ * If rdata is NULL, space is allocated by the function and rlen is irrelevant.
+ * In case of an error any allocated storage is freed and rdata is set back to
+ * NULL.
+ *
+ * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation
+ * functions and rlen MUST be correct. If necessary, the function will resize
+ * rdata. In case of an error the caller must free rdata.
+ *
+ * Return value: In case of success, the exact length of rdata, SIZE_MAX
+ * otherwise.
+ */
+size_t
+mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t rbase,
+                const mpd_t *src, uint32_t *status)
+{
+    MPD_NEW_STATIC(tsrc,0,0,0,0);
+    int alloc = 0; /* rdata == NULL */
+    size_t n;
+
+    assert(rbase <= (1U<<16));
+
+    if (mpd_isspecial(src) || !_mpd_isint(src)) {
+        *status |= MPD_Invalid_operation;
+        return SIZE_MAX;
+    }
+
+    if (*rdata == NULL) {
+        rlen = mpd_sizeinbase(src, rbase);
+        if (rlen == SIZE_MAX) {
+            *status |= MPD_Invalid_operation;
+            return SIZE_MAX;
+        }
+        *rdata = mpd_alloc(rlen, sizeof **rdata);
+        if (*rdata == NULL) {
+            goto malloc_error;
+        }
+        alloc = 1;
+    }
+
+    if (mpd_iszero(src)) {
+        **rdata = 0;
+        return 1;
+    }
+
+    if (src->exp >= 0) {
+        if (!mpd_qshiftl(&tsrc, src, src->exp, status)) {
+            goto malloc_error;
+        }
+    }
+    else {
+        if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) {
+            goto malloc_error;
+        }
+    }
+
+    n = _baseconv_to_u16(rdata, rlen, rbase, tsrc.data, tsrc.len);
+    if (n == SIZE_MAX) {
+        goto malloc_error;
+    }
+
+
+out:
+    mpd_del(&tsrc);
+    return n;
+
+malloc_error:
+    if (alloc) {
+        mpd_free(*rdata);
+        *rdata = NULL;
+    }
+    n = SIZE_MAX;
+    *status |= MPD_Malloc_error;
+    goto out;
+}
+
+/*
+ * Convert an integer mpd_t to a multiprecision integer with base<=UINT32_MAX.
+ * The least significant word of the result is (*rdata)[0].
+ *
+ * If rdata is NULL, space is allocated by the function and rlen is irrelevant.
+ * In case of an error any allocated storage is freed and rdata is set back to
+ * NULL.
+ *
+ * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation
+ * functions and rlen MUST be correct. If necessary, the function will resize
+ * rdata. In case of an error the caller must free rdata.
+ *
+ * Return value: In case of success, the exact length of rdata, SIZE_MAX
+ * otherwise.
+ */
+size_t
+mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t rbase,
+                const mpd_t *src, uint32_t *status)
+{
+    MPD_NEW_STATIC(tsrc,0,0,0,0);
+    int alloc = 0; /* rdata == NULL */
+    size_t n;
+
+    if (mpd_isspecial(src) || !_mpd_isint(src)) {
+        *status |= MPD_Invalid_operation;
+        return SIZE_MAX;
+    }
+
+    if (*rdata == NULL) {
+        rlen = mpd_sizeinbase(src, rbase);
+        if (rlen == SIZE_MAX) {
+            *status |= MPD_Invalid_operation;
+            return SIZE_MAX;
+        }
+        *rdata = mpd_alloc(rlen, sizeof **rdata);
+        if (*rdata == NULL) {
+            goto malloc_error;
+        }
+        alloc = 1;
+    }
+
+    if (mpd_iszero(src)) {
+        **rdata = 0;
+        return 1;
+    }
+
+    if (src->exp >= 0) {
+        if (!mpd_qshiftl(&tsrc, src, src->exp, status)) {
+            goto malloc_error;
+        }
+    }
+    else {
+        if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) {
+            goto malloc_error;
+        }
+    }
+
+#ifdef CONFIG_64
+    n = _baseconv_to_smaller(rdata, rlen, rbase,
+                             tsrc.data, tsrc.len, MPD_RADIX);
+#else
+    if (rbase == MPD_RADIX) {
+        n = _copy_equal_base(rdata, rlen, tsrc.data, tsrc.len);
+    }
+    else if (rbase < MPD_RADIX) {
+        n = _baseconv_to_smaller(rdata, rlen, rbase,
+                                 tsrc.data, tsrc.len, MPD_RADIX);
+    }
+    else {
+        n = _baseconv_to_larger(rdata, rlen, rbase,
+                                tsrc.data, tsrc.len, MPD_RADIX);
+    }
+#endif
+
+    if (n == SIZE_MAX) {
+        goto malloc_error;
+    }
+
+
+out:
+    mpd_del(&tsrc);
+    return n;
+
+malloc_error:
+    if (alloc) {
+        mpd_free(*rdata);
+        *rdata = NULL;
+    }
+    n = SIZE_MAX;
+    *status |= MPD_Malloc_error;
+    goto out;
+}
+
+
+/*
+ * Converts a multiprecision integer with base <= UINT16_MAX+1 to an mpd_t.
+ * The least significant word of the source is srcdata[0].
+ */
+void
+mpd_qimport_u16(mpd_t *result,
+                const uint16_t *srcdata, size_t srclen,
+                uint8_t srcsign, uint32_t srcbase,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_uint_t *usrc; /* uint16_t src copied to an mpd_uint_t array */
+    mpd_ssize_t rlen; /* length of the result */
+    size_t n;
+
+    assert(srclen > 0);
+    assert(srcbase <= (1U<<16));
+
+    rlen = _mpd_importsize(srclen, srcbase);
+    if (rlen == MPD_SSIZE_MAX) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc);
+    if (usrc == NULL) {
+        mpd_seterror(result, MPD_Malloc_error, status);
+        return;
+    }
+    for (n = 0; n < srclen; n++) {
+        usrc[n] = srcdata[n];
+    }
+
+    if (!mpd_qresize(result, rlen, status)) {
+        goto finish;
+    }
+
+    n = _coeff_from_u16(result, rlen, usrc, srclen, srcbase, status);
+    if (n == SIZE_MAX) {
+        goto finish;
+    }
+
+    mpd_set_flags(result, srcsign);
+    result->exp = 0;
+    result->len = n;
+    mpd_setdigits(result);
+
+    mpd_qresize(result, result->len, status);
+    mpd_qfinalize(result, ctx, status);
+
+
+finish:
+    mpd_free(usrc);
+}
+
+/*
+ * Converts a multiprecision integer with base <= UINT32_MAX to an mpd_t.
+ * The least significant word of the source is srcdata[0].
+ */
+void
+mpd_qimport_u32(mpd_t *result,
+                const uint32_t *srcdata, size_t srclen,
+                uint8_t srcsign, uint32_t srcbase,
+                const mpd_context_t *ctx, uint32_t *status)
+{
+    mpd_ssize_t rlen; /* length of the result */
+    size_t n;
+
+    assert(srclen > 0);
+
+    rlen = _mpd_importsize(srclen, srcbase);
+    if (rlen == MPD_SSIZE_MAX) {
+        mpd_seterror(result, MPD_Invalid_operation, status);
+        return;
+    }
+
+    if (!mpd_qresize(result, rlen, status)) {
+        return;
+    }
+
+#ifdef CONFIG_64
+    n = _coeff_from_smaller_base(result, rlen, MPD_RADIX,
+                                 srcdata, srclen, srcbase,
+                                 status);
+#else
+    if (srcbase == MPD_RADIX) {
+        if (!mpd_qresize(result, srclen, status)) {
+            return;
+        }
+        memcpy(result->data, srcdata, srclen * (sizeof *srcdata));
+        n = srclen;
+    }
+    else if (srcbase < MPD_RADIX) {
+        n = _coeff_from_smaller_base(result, rlen, MPD_RADIX,
+                                     srcdata, srclen, srcbase,
+                                     status);
+    }
+    else {
+        mpd_uint_t *usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc);
+        if (usrc == NULL) {
+            mpd_seterror(result, MPD_Malloc_error, status);
+            return;
+        }
+        for (n = 0; n < srclen; n++) {
+            usrc[n] = srcdata[n];
+        }
+
+        n = _coeff_from_larger_base(result, rlen, MPD_RADIX,
+                                    usrc, (mpd_ssize_t)srclen, srcbase,
+                                    status);
+        mpd_free(usrc);
+    }
+#endif
+
+    if (n == SIZE_MAX) {
+        return;
+    }
+
+    mpd_set_flags(result, srcsign);
+    result->exp = 0;
+    result->len = n;
+    mpd_setdigits(result);
+
+    mpd_qresize(result, result->len, status);
+    mpd_qfinalize(result, ctx, status);
+}
+
+
+/******************************************************************************/
+/*                                From triple                                 */
+/******************************************************************************/
+
+#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
+static mpd_ssize_t
+_set_coeff(uint64_t data[3], uint64_t hi, uint64_t lo)
+{
+    __uint128_t d = ((__uint128_t)hi << 64) + lo;
+    __uint128_t q, r;
+
+    q = d / MPD_RADIX;
+    r = d % MPD_RADIX;
+    data[0] = (uint64_t)r;
+    d = q;
+
+    q = d / MPD_RADIX;
+    r = d % MPD_RADIX;
+    data[1] = (uint64_t)r;
+    d = q;
+
+    q = d / MPD_RADIX;
+    r = d % MPD_RADIX;
+    data[2] = (uint64_t)r;
+
+    if (q != 0) {
+        abort(); /* GCOV_NOT_REACHED */
+    }
+
+    return data[2] != 0 ? 3 : (data[1] != 0 ? 2 : 1);
+}
+#else
+static size_t
+_uint_from_u16(mpd_uint_t *w, mpd_ssize_t wlen, const uint16_t *u, size_t ulen)
+{
+    const mpd_uint_t ubase = 1U<<16;
+    mpd_ssize_t n = 0;
+    mpd_uint_t carry;
+
+    assert(wlen > 0 && ulen > 0);
+
+    w[n++] = u[--ulen];
+    while (--ulen != SIZE_MAX) {
+        carry = _mpd_shortmul_c(w, w, n, ubase);
+        if (carry) {
+            if (n >= wlen) {
+                abort();  /* GCOV_NOT_REACHED */
+            }
+            w[n++] = carry;
+        }
+        carry = _mpd_shortadd(w, n, u[ulen]);
+        if (carry) {
+            if (n >= wlen) {
+                abort();  /* GCOV_NOT_REACHED */
+            }
+            w[n++] = carry;
+        }
+    }
+
+    return n;
+}
+
+static mpd_ssize_t
+_set_coeff(mpd_uint_t *data, mpd_ssize_t len, uint64_t hi, uint64_t lo)
+{
+    uint16_t u16[8] = {0};
+
+    u16[7] = (uint16_t)((hi & 0xFFFF000000000000ULL) >> 48);
+    u16[6] = (uint16_t)((hi & 0x0000FFFF00000000ULL) >> 32);
+    u16[5] = (uint16_t)((hi & 0x00000000FFFF0000ULL) >> 16);
+    u16[4] = (uint16_t) (hi & 0x000000000000FFFFULL);
+
+    u16[3] = (uint16_t)((lo & 0xFFFF000000000000ULL) >> 48);
+    u16[2] = (uint16_t)((lo & 0x0000FFFF00000000ULL) >> 32);
+    u16[1] = (uint16_t)((lo & 0x00000000FFFF0000ULL) >> 16);
+    u16[0] = (uint16_t) (lo & 0x000000000000FFFFULL);
+
+    return (mpd_ssize_t)_uint_from_u16(data, len, u16, 8);
+}
+#endif
+
+static int
+_set_uint128_coeff_exp(mpd_t *result, uint64_t hi, uint64_t lo, mpd_ssize_t exp)
+{
+    mpd_uint_t data[5] = {0};
+    uint32_t status = 0;
+    mpd_ssize_t len;
+
+#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
+    len = _set_coeff(data, hi, lo);
+#else
+    len = _set_coeff(data, 5, hi, lo);
+#endif
+
+    if (!mpd_qresize(result, len, &status)) {
+        return -1;
+    }
+
+    for (mpd_ssize_t i = 0; i < len; i++) {
+        result->data[i] = data[i];
+    }
+
+    result->exp = exp;
+    result->len = len;
+    mpd_setdigits(result);
+
+    return 0;
+}
+
+int
+mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status)
+{
+    static const mpd_context_t maxcontext = {
+     .prec=MPD_MAX_PREC,
+     .emax=MPD_MAX_EMAX,
+     .emin=MPD_MIN_EMIN,
+     .round=MPD_ROUND_HALF_EVEN,
+     .traps=MPD_Traps,
+     .status=0,
+     .newtrap=0,
+     .clamp=0,
+     .allcr=1,
+    };
+    const enum mpd_triple_class tag = triple->tag;
+    const uint8_t sign = triple->sign;
+    const uint64_t hi = triple->hi;
+    const uint64_t lo = triple->lo;
+    mpd_ssize_t exp;
+
+#ifdef CONFIG_32
+    if (triple->exp < MPD_SSIZE_MIN || triple->exp > MPD_SSIZE_MAX) {
+        goto conversion_error;
+    }
+#endif
+    exp = (mpd_ssize_t)triple->exp;
+
+    switch (tag) {
+    case MPD_TRIPLE_QNAN: case MPD_TRIPLE_SNAN: {
+        if (sign > 1 || exp != 0) {
+            goto conversion_error;
+        }
+
+        const uint8_t flags = tag == MPD_TRIPLE_QNAN ? MPD_NAN : MPD_SNAN;
+        mpd_setspecial(result, sign, flags);
+
+        if (hi == 0 && lo == 0) {  /* no payload */
+            return 0;
+        }
+
+        if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) {
+            goto malloc_error;
+        }
+
+        return 0;
+    }
+
+    case MPD_TRIPLE_INF: {
+        if (sign > 1 || hi != 0 || lo != 0 || exp != 0) {
+            goto conversion_error;
+        }
+
+        mpd_setspecial(result, sign, MPD_INF);
+
+        return 0;
+    }
+
+    case MPD_TRIPLE_NORMAL: {
+        if (sign > 1) {
+            goto conversion_error;
+        }
+
+        const uint8_t flags = sign ? MPD_NEG : MPD_POS;
+        mpd_set_flags(result, flags);
+
+        if (exp > MPD_EXP_INF) {
+            exp = MPD_EXP_INF;
+        }
+        if (exp == MPD_SSIZE_MIN) {
+            exp = MPD_SSIZE_MIN+1;
+        }
+
+        if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) {
+            goto malloc_error;
+        }
+
+        uint32_t workstatus = 0;
+        mpd_qfinalize(result, &maxcontext, &workstatus);
+        if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
+            goto conversion_error;
+        }
+
+        return 0;
+    }
+
+    default:
+        goto conversion_error;
+    }
+
+conversion_error:
+    mpd_seterror(result, MPD_Conversion_syntax, status);
+    return -1;
+
+malloc_error:
+    mpd_seterror(result, MPD_Malloc_error, status);
+    return -1;
+}
+
+
+/******************************************************************************/
+/*                                  As triple                                 */
+/******************************************************************************/
+
+#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
+static void
+_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a)
+{
+    __uint128_t u128 = 0;
+
+    switch (a->len) {
+    case 3:
+        u128 = a->data[2]; /* fall through */
+    case 2:
+        u128 = u128 * MPD_RADIX + a->data[1]; /* fall through */
+    case 1:
+        u128 = u128 * MPD_RADIX + a->data[0];
+        break;
+    default:
+        abort(); /* GCOV_NOT_REACHED */
+    }
+
+    *hi = u128 >> 64;
+    *lo = (uint64_t)u128;
+}
+#else
+static size_t
+_uint_to_u16(uint16_t w[8], mpd_uint_t *u, mpd_ssize_t ulen)
+{
+    const mpd_uint_t wbase = 1U<<16;
+    size_t n = 0;
+
+    assert(ulen > 0);
+
+    do {
+        if (n >= 8) {
+            abort();  /* GCOV_NOT_REACHED */
+        }
+        w[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase);
+        /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */
+        ulen = _mpd_real_size(u, ulen);
+
+    } while (u[ulen-1] != 0);
+
+    return n;
+}
+
+static void
+_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a)
+{
+    uint16_t u16[8] = {0};
+    mpd_uint_t data[5] = {0};
+
+    switch (a->len) {
+    case 5:
+        data[4] = a->data[4]; /* fall through */
+    case 4:
+        data[3] = a->data[3]; /* fall through */
+    case 3:
+        data[2] = a->data[2]; /* fall through */
+    case 2:
+        data[1] = a->data[1]; /* fall through */
+    case 1:
+        data[0] = a->data[0];
+        break;
+    default:
+        abort();  /* GCOV_NOT_REACHED */
+    }
+
+    _uint_to_u16(u16, data, a->len);
+
+    *hi = (uint64_t)u16[7] << 48;
+    *hi |= (uint64_t)u16[6] << 32;
+    *hi |= (uint64_t)u16[5] << 16;
+    *hi |= (uint64_t)u16[4];
+
+    *lo = (uint64_t)u16[3] << 48;
+    *lo |= (uint64_t)u16[2] << 32;
+    *lo |= (uint64_t)u16[1] << 16;
+    *lo |= (uint64_t)u16[0];
+}
+#endif
+
+static enum mpd_triple_class
+_coeff_as_uint128(uint64_t *hi, uint64_t *lo, const mpd_t *a)
+{
+#ifdef CONFIG_64
+    static mpd_uint_t uint128_max_data[3] = { 3374607431768211455ULL, 4028236692093846346ULL, 3ULL };
+    static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 3, 3, uint128_max_data };
+#else
+    static mpd_uint_t uint128_max_data[5] = { 768211455U, 374607431U, 938463463U, 282366920U, 340U };
+    static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 5, 5, uint128_max_data };
+#endif
+    enum mpd_triple_class ret = MPD_TRIPLE_NORMAL;
+    uint32_t status = 0;
+    mpd_t coeff;
+
+    *hi = *lo = 0ULL;
+
+    if (mpd_isspecial(a)) {
+        if (mpd_isinfinite(a)) {
+            return MPD_TRIPLE_INF;
+        }
+
+        ret = mpd_isqnan(a) ? MPD_TRIPLE_QNAN : MPD_TRIPLE_SNAN;
+        if (a->len == 0) { /* no payload */
+            return ret;
+        }
+    }
+    else if (mpd_iszero(a)) {
+        return ret;
+    }
+
+    _mpd_copy_shared(&coeff, a);
+    mpd_set_flags(&coeff, 0);
+    coeff.exp = 0;
+
+    if (mpd_qcmp(&coeff, &uint128_max, &status) > 0) {
+        return MPD_TRIPLE_ERROR;
+    }
+
+    _get_coeff(hi, lo, &coeff);
+    return ret;
+}
+
+mpd_uint128_triple_t
+mpd_as_uint128_triple(const mpd_t *a)
+{
+    mpd_uint128_triple_t triple = { MPD_TRIPLE_ERROR, 0, 0, 0, 0 };
+
+    triple.tag = _coeff_as_uint128(&triple.hi, &triple.lo, a);
+    if (triple.tag == MPD_TRIPLE_ERROR) {
+        return triple;
+    }
+
+    triple.sign = !!mpd_isnegative(a);
+    if (triple.tag == MPD_TRIPLE_NORMAL) {
+        triple.exp = a->exp;
+    }
+
+    return triple;
+}
diff --git a/libmpdec/mpdecimal.h.in b/libmpdec/mpdecimal.h.in
new file mode 100644 (file)
index 0000000..9e7f84a
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_MPDECIMAL_H_
+#define LIBMPDEC_MPDECIMAL_H_
+
+
+#ifdef __cplusplus
+  #include <cinttypes>
+  #include <climits>
+  #include <cstdint>
+  #include <cstdio>
+  #include <cstdlib>
+  #define MPD_UINT8_C(x) (static_cast<uint8_t>(x))
+extern "C" {
+#else
+  #include <inttypes.h>
+  #include <limits.h>
+  #include <stdint.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #define MPD_UINT8_C(x) ((uint8_t)x)
+#endif
+
+
+#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \
+    defined(__GNUC__) && __GNUC__ >= 4 && !defined(__INTEL_COMPILER)
+  #define MPD_PRAGMA(x) _Pragma(x)
+  #define MPD_HIDE_SYMBOLS_START "GCC visibility push(hidden)"
+  #define MPD_HIDE_SYMBOLS_END "GCC visibility pop"
+#else
+  #define MPD_PRAGMA(x)
+  #define MPD_HIDE_SYMBOLS_START
+  #define MPD_HIDE_SYMBOLS_END
+#endif
+
+
+/******************************************************************************/
+/*                                  Version                                   */
+/******************************************************************************/
+
+#define MPD_MAJOR_VERSION 4
+#define MPD_MINOR_VERSION 0
+#define MPD_MICRO_VERSION 1
+
+#define MPD_VERSION "4.0.1"
+
+#define MPD_VERSION_HEX ((MPD_MAJOR_VERSION << 24) | \
+                         (MPD_MINOR_VERSION << 16) | \
+                         (MPD_MICRO_VERSION <<  8))
+
+const char *mpd_version(void);
+
+
+/******************************************************************************/
+/*                              Configuration                                 */
+/******************************************************************************/
+@MPD_HEADER_CONFIG@
+
+
+/* BEGIN MPD_CONFIG_64 */
+#if defined(MPD_CONFIG_64)
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT64_MAX
+#define MPD_BITS_PER_UINT 64
+typedef uint64_t mpd_uint_t;  /* unsigned mod type */
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for exp, digits, len, prec */
+#define MPD_SSIZE_MAX INT64_MAX
+#define MPD_SSIZE_MIN INT64_MIN
+typedef int64_t mpd_ssize_t;
+#define _mpd_strtossize strtoll
+
+/* decimal arithmetic */
+#define MPD_RADIX 10000000000000000000ULL  /* 10**19 */
+#define MPD_RDIGITS 19
+#define MPD_MAX_POW10 19
+#define MPD_EXPDIGITS 19  /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 4294967296ULL      /* 2**32 */
+#define MPD_MAX_PREC 999999999999999999LL
+#define MPD_MAX_PREC_LOG2 64
+#define MPD_ELIMIT  1000000000000000000LL
+#define MPD_MAX_EMAX   999999999999999999LL    /* ELIMIT-1 */
+#define MPD_MIN_EMIN  (-999999999999999999LL)  /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 2000000000000000001LL
+#define MPD_EXP_CLAMP (-4000000000000000001LL)
+#define MPD_MAXIMPORT 105263157894736842L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+#define MPD_IEEE_CONTEXT_MAX_BITS 512     /* 16*(log2(MPD_MAX_EMAX / 3)-3) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu64
+#define PRI_mpd_ssize_t PRIi64
+/* END MPD_CONFIG_64 */
+
+
+/* BEGIN MPD_CONFIG_32 */
+#elif defined(MPD_CONFIG_32)
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT32_MAX
+#define MPD_BITS_PER_UINT 32
+typedef uint32_t mpd_uint_t;  /* unsigned mod type */
+
+#ifndef MPD_LEGACY_COMPILER
+#define MPD_UUINT_MAX UINT64_MAX
+typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */
+#endif
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for dec->len, dec->exp, ctx->prec */
+#define MPD_SSIZE_MAX INT32_MAX
+#define MPD_SSIZE_MIN INT32_MIN
+typedef int32_t mpd_ssize_t;
+#define _mpd_strtossize strtol
+
+/* decimal arithmetic */
+#define MPD_RADIX 1000000000UL  /* 10**9 */
+#define MPD_RDIGITS 9
+#define MPD_MAX_POW10 9
+#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */
+#define MPD_MAX_PREC 425000000L
+#define MPD_MAX_PREC_LOG2 32
+#define MPD_ELIMIT 425000001L
+#define MPD_MAX_EMAX 425000000L        /* ELIMIT-1 */
+#define MPD_MIN_EMIN (-425000000L)     /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 1000000001L       /* allows for emax=999999999 in the tests */
+#define MPD_EXP_CLAMP (-2000000001L)  /* allows for emin=-999999999 in the tests */
+#define MPD_MAXIMPORT 94444445L       /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+#define MPD_IEEE_CONTEXT_MAX_BITS 256 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu32
+#define PRI_mpd_ssize_t PRIi32
+/* END MPD_CONFIG_32 */
+
+#else
+  #error "define MPD_CONFIG_64 or MPD_CONFIG_32"
+#endif
+/* END CONFIG */
+
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+  #error "unsupported platform: need mpd_size_t == mpd_uint_t"
+#endif
+
+
+/******************************************************************************/
+/*                                Context                                     */
+/******************************************************************************/
+
+enum {
+    MPD_ROUND_UP,          /* round away from 0               */
+    MPD_ROUND_DOWN,        /* round toward 0 (truncate)       */
+    MPD_ROUND_CEILING,     /* round toward +infinity          */
+    MPD_ROUND_FLOOR,       /* round toward -infinity          */
+    MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
+    MPD_ROUND_HALF_DOWN,   /* 0.5 is rounded down             */
+    MPD_ROUND_HALF_EVEN,   /* 0.5 is rounded to even          */
+    MPD_ROUND_05UP,        /* round zero or five away from 0  */
+    MPD_ROUND_TRUNC,       /* truncate, but set infinity      */
+    MPD_ROUND_GUARD
+};
+
+enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD };
+
+extern const char * const mpd_round_string[MPD_ROUND_GUARD];
+extern const char * const mpd_clamp_string[MPD_CLAMP_GUARD];
+
+
+typedef struct mpd_context_t {
+    mpd_ssize_t prec;   /* precision */
+    mpd_ssize_t emax;   /* max positive exp */
+    mpd_ssize_t emin;   /* min negative exp */
+    uint32_t traps;     /* status events that should be trapped */
+    uint32_t status;    /* status flags */
+    uint32_t newtrap;   /* set by mpd_addstatus_raise() */
+    int      round;     /* rounding mode */
+    int      clamp;     /* clamp mode */
+    int      allcr;     /* all functions correctly rounded */
+} mpd_context_t;
+
+
+/* Status flags */
+#define MPD_Clamped             0x00000001U
+#define MPD_Conversion_syntax   0x00000002U
+#define MPD_Division_by_zero    0x00000004U
+#define MPD_Division_impossible 0x00000008U
+#define MPD_Division_undefined  0x00000010U
+#define MPD_Fpu_error           0x00000020U
+#define MPD_Inexact             0x00000040U
+#define MPD_Invalid_context     0x00000080U
+#define MPD_Invalid_operation   0x00000100U
+#define MPD_Malloc_error        0x00000200U
+#define MPD_Not_implemented     0x00000400U
+#define MPD_Overflow            0x00000800U
+#define MPD_Rounded             0x00001000U
+#define MPD_Subnormal           0x00002000U
+#define MPD_Underflow           0x00004000U
+#define MPD_Max_status         (0x00008000U-1U)
+
+/* Conditions that result in an IEEE 754 exception */
+#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax |   \
+                                    MPD_Division_impossible | \
+                                    MPD_Division_undefined |  \
+                                    MPD_Fpu_error |           \
+                                    MPD_Invalid_context |     \
+                                    MPD_Invalid_operation |   \
+                                    MPD_Malloc_error)         \
+
+/* Errors that require the result of an operation to be set to NaN */
+#define MPD_Errors (MPD_IEEE_Invalid_operation | \
+                    MPD_Division_by_zero)
+
+/* Default traps */
+#define MPD_Traps (MPD_IEEE_Invalid_operation | \
+                   MPD_Division_by_zero |       \
+                   MPD_Overflow |               \
+                   MPD_Underflow)
+
+/* Official name */
+#define MPD_Insufficient_storage MPD_Malloc_error
+
+/* IEEE 754 interchange format contexts */
+#define MPD_DECIMAL32 32
+#define MPD_DECIMAL64 64
+#define MPD_DECIMAL128 128
+
+
+#define MPD_MINALLOC_MIN 2
+#define MPD_MINALLOC_MAX 64
+extern mpd_ssize_t MPD_MINALLOC;
+extern void (* mpd_traphandler)(mpd_context_t *);
+void mpd_dflt_traphandler(mpd_context_t *);
+
+void mpd_setminalloc(mpd_ssize_t n);
+void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec);
+
+void mpd_maxcontext(mpd_context_t *ctx);
+void mpd_defaultcontext(mpd_context_t *ctx);
+void mpd_basiccontext(mpd_context_t *ctx);
+int mpd_ieee_context(mpd_context_t *ctx, int bits);
+
+mpd_ssize_t mpd_getprec(const mpd_context_t *ctx);
+mpd_ssize_t mpd_getemax(const mpd_context_t *ctx);
+mpd_ssize_t mpd_getemin(const mpd_context_t *ctx);
+int mpd_getround(const mpd_context_t *ctx);
+uint32_t mpd_gettraps(const mpd_context_t *ctx);
+uint32_t mpd_getstatus(const mpd_context_t *ctx);
+int mpd_getclamp(const mpd_context_t *ctx);
+int mpd_getcr(const mpd_context_t *ctx);
+
+int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
+int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
+int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
+int mpd_qsetround(mpd_context_t *ctx, int newround);
+int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetclamp(mpd_context_t *ctx, int c);
+int mpd_qsetcr(mpd_context_t *ctx, int c);
+void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags);
+
+
+/******************************************************************************/
+/*                           Decimal Arithmetic                               */
+/******************************************************************************/
+
+/* mpd_t flags */
+#define MPD_POS                 MPD_UINT8_C(0)
+#define MPD_NEG                 MPD_UINT8_C(1)
+#define MPD_INF                 MPD_UINT8_C(2)
+#define MPD_NAN                 MPD_UINT8_C(4)
+#define MPD_SNAN                MPD_UINT8_C(8)
+#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN)
+#define MPD_STATIC              MPD_UINT8_C(16)
+#define MPD_STATIC_DATA         MPD_UINT8_C(32)
+#define MPD_SHARED_DATA         MPD_UINT8_C(64)
+#define MPD_CONST_DATA          MPD_UINT8_C(128)
+#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA)
+
+/* mpd_t */
+typedef struct mpd_t {
+    uint8_t flags;
+    mpd_ssize_t exp;
+    mpd_ssize_t digits;
+    mpd_ssize_t len;
+    mpd_ssize_t alloc;
+    mpd_uint_t *data;
+} mpd_t;
+
+
+/******************************************************************************/
+/*                                    Triple                                  */
+/******************************************************************************/
+
+/* status cases for getting a triple */
+enum mpd_triple_class {
+  MPD_TRIPLE_NORMAL,
+  MPD_TRIPLE_INF,
+  MPD_TRIPLE_QNAN,
+  MPD_TRIPLE_SNAN,
+  MPD_TRIPLE_ERROR,
+};
+
+typedef struct {
+  enum mpd_triple_class tag;
+  uint8_t sign;
+  uint64_t hi;
+  uint64_t lo;
+  int64_t exp;
+} mpd_uint128_triple_t;
+
+int mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status);
+mpd_uint128_triple_t mpd_as_uint128_triple(const mpd_t *a);
+
+
+/******************************************************************************/
+/*                       Quiet, thread-safe functions                         */
+/******************************************************************************/
+
+/* format specification */
+typedef struct mpd_spec_t {
+    mpd_ssize_t min_width; /* minimum field width */
+    mpd_ssize_t prec;      /* fraction digits or significant digits */
+    char type;             /* conversion specifier */
+    char align;            /* alignment */
+    char sign;             /* sign printing/alignment */
+    char sign_coerce;      /* coerce to positive zero */
+    char fill[5];          /* fill character */
+    const char *dot;       /* decimal point */
+    const char *sep;       /* thousands separator */
+    const char *grouping;  /* grouping of digits */
+} mpd_spec_t;
+
+/* output to a string */
+char *mpd_to_sci(const mpd_t *dec, int fmt);
+char *mpd_to_eng(const mpd_t *dec, int fmt);
+mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt);
+mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt);
+int mpd_validate_lconv(mpd_spec_t *spec);
+int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps);
+char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status);
+char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status);
+
+#define MPD_NUM_FLAGS 15
+#define MPD_MAX_FLAG_STRING 208
+#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18)
+#define MPD_MAX_SIGNAL_LIST 121
+int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags);
+int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]);
+int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]);
+
+/* output to a file */
+void mpd_fprint(FILE *file, const mpd_t *dec);
+void mpd_print(const mpd_t *dec);
+
+/* assignment from a string */
+void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status);
+
+/* set to NaN with error flags */
+void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status);
+/* set a special with sign and type */
+void mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type);
+/* set coefficient to zero or all nines */
+void mpd_zerocoeff(mpd_t *result);
+void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly assign a C integer type to an mpd_t */
+void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status);
+void mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status);
+#endif
+
+/* quietly assign a C integer type to an mpd_t with a static coefficient */
+void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly get a C integer type from an mpd_t */
+mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status);
+mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status);
+mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status);
+
+int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status);
+uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status);
+uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status);
+#endif
+
+/* quiet functions */
+int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx);
+
+int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_cxx(mpd_t *result, const mpd_t *a);
+mpd_t *mpd_qncopy(const mpd_t *a);
+int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status);
+
+void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_same_quantum(const mpd_t *a, const mpd_t *b);
+
+void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n);
+void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status);
+
+int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status);
+int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_cmp_total(const mpd_t *a, const mpd_t *b);
+int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b);
+int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b);
+int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b);
+
+void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status);
+void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+#endif
+
+
+size_t mpd_sizeinbase(const mpd_t *a, uint32_t base);
+void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+                     uint8_t srcsign, uint32_t srcbase,
+                     const mpd_context_t *ctx, uint32_t *status);
+void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen,
+                     uint8_t srcsign, uint32_t srcbase,
+                     const mpd_context_t *ctx, uint32_t *status);
+size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base,
+                       const mpd_t *src, uint32_t *status);
+size_t mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t base,
+                       const mpd_t *src, uint32_t *status);
+
+
+/******************************************************************************/
+/*                           Signalling functions                             */
+/******************************************************************************/
+
+char *mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx);
+void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+size_t mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+size_t mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+void mpd_finalize(mpd_t *result, mpd_context_t *ctx);
+int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx);
+void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx);
+void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+#endif
+mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx);
+mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx);
+mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx);
+int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx);
+uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx);
+uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx);
+#endif
+void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx);
+void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx);
+void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx);
+void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx);
+void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+#endif
+
+
+/******************************************************************************/
+/*                          Configuration specific                            */
+/******************************************************************************/
+
+#ifdef MPD_CONFIG_64
+void mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+void mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+#endif
+
+
+/******************************************************************************/
+/*                       Get attributes of a decimal                          */
+/******************************************************************************/
+
+mpd_ssize_t mpd_adjexp(const mpd_t *dec);
+mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);
+mpd_ssize_t mpd_etop(const mpd_context_t *ctx);
+mpd_uint_t mpd_msword(const mpd_t *dec);
+int mpd_word_digits(mpd_uint_t word);
+/* most significant digit of a word */
+mpd_uint_t mpd_msd(mpd_uint_t word);
+/* least significant digit of a word */
+mpd_uint_t mpd_lsd(mpd_uint_t word);
+/* coefficient size needed to store 'digits' */
+mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits);
+/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */
+int mpd_exp_digits(mpd_ssize_t exp);
+int mpd_iscanonical(const mpd_t *dec);
+int mpd_isfinite(const mpd_t *dec);
+int mpd_isinfinite(const mpd_t *dec);
+int mpd_isinteger(const mpd_t *dec);
+int mpd_isnan(const mpd_t *dec);
+int mpd_isnegative(const mpd_t *dec);
+int mpd_ispositive(const mpd_t *dec);
+int mpd_isqnan(const mpd_t *dec);
+int mpd_issigned(const mpd_t *dec);
+int mpd_issnan(const mpd_t *dec);
+int mpd_isspecial(const mpd_t *dec);
+int mpd_iszero(const mpd_t *dec);
+/* undefined for special numbers */
+int mpd_iszerocoeff(const mpd_t *dec);
+int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx);
+int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx);
+/* odd word */
+int mpd_isoddword(mpd_uint_t word);
+/* odd coefficient */
+int mpd_isoddcoeff(const mpd_t *dec);
+/* odd decimal, only defined for integers */
+int mpd_isodd(const mpd_t *dec);
+/* even decimal, only defined for integers */
+int mpd_iseven(const mpd_t *dec);
+/* 0 if dec is positive, 1 if dec is negative */
+uint8_t mpd_sign(const mpd_t *dec);
+/* 1 if dec is positive, -1 if dec is negative */
+int mpd_arith_sign(const mpd_t *dec);
+long mpd_radix(void);
+int mpd_isdynamic(const mpd_t *dec);
+int mpd_isstatic(const mpd_t *dec);
+int mpd_isdynamic_data(const mpd_t *dec);
+int mpd_isstatic_data(const mpd_t *dec);
+int mpd_isshared_data(const mpd_t *dec);
+int mpd_isconst_data(const mpd_t *dec);
+mpd_ssize_t mpd_trail_zeros(const mpd_t *dec);
+
+
+/******************************************************************************/
+/*                       Set attributes of a decimal                          */
+/******************************************************************************/
+
+/* set number of decimal digits in the coefficient */
+void mpd_setdigits(mpd_t *result);
+void mpd_set_sign(mpd_t *result, uint8_t sign);
+/* copy sign from another decimal */
+void mpd_signcpy(mpd_t *result, const mpd_t *a);
+void mpd_set_infinity(mpd_t *result);
+void mpd_set_qnan(mpd_t *result);
+void mpd_set_snan(mpd_t *result);
+void mpd_set_negative(mpd_t *result);
+void mpd_set_positive(mpd_t *result);
+void mpd_set_dynamic(mpd_t *result);
+void mpd_set_static(mpd_t *result);
+void mpd_set_dynamic_data(mpd_t *result);
+void mpd_set_static_data(mpd_t *result);
+void mpd_set_shared_data(mpd_t *result);
+void mpd_set_const_data(mpd_t *result);
+void mpd_clear_flags(mpd_t *result);
+void mpd_set_flags(mpd_t *result, uint8_t flags);
+void mpd_copy_flags(mpd_t *result, const mpd_t *a);
+
+
+/******************************************************************************/
+/*                              Error Macros                                  */
+/******************************************************************************/
+
+#define mpd_err_fatal(...) \
+    do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__);  fputc('\n', stderr);    \
+        abort();                                               \
+    } while (0)
+#define mpd_err_warn(...) \
+    do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__); fputc('\n', stderr);       \
+    } while (0)
+
+
+/******************************************************************************/
+/*                            Memory handling                                 */
+/******************************************************************************/
+
+extern void *(* mpd_mallocfunc)(size_t size);
+extern void *(* mpd_callocfunc)(size_t nmemb, size_t size);
+extern void *(* mpd_reallocfunc)(void *ptr, size_t size);
+extern void (* mpd_free)(void *ptr);
+
+void *mpd_callocfunc_em(size_t nmemb, size_t size);
+
+void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size);
+void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size);
+void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err);
+void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size);
+
+mpd_t *mpd_qnew(void);
+mpd_t *mpd_new(mpd_context_t *ctx);
+mpd_t *mpd_qnew_size(mpd_ssize_t nwords);
+void mpd_del(mpd_t *dec);
+
+void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
+int mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+int mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+void mpd_minalloc(mpd_t *result);
+
+int mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+int mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+
+
+#ifdef __cplusplus
+} /* END extern "C" */
+#endif
+
+
+#endif /* LIBMPDEC_MPDECIMAL_H_ */
diff --git a/libmpdec/mpdecimal32vc.h b/libmpdec/mpdecimal32vc.h
new file mode 100644 (file)
index 0000000..d5cb6ac
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_MPDECIMAL_H_
+#define LIBMPDEC_MPDECIMAL_H_
+
+
+#ifdef __cplusplus
+  #include <cinttypes>
+  #include <climits>
+  #include <cstdint>
+  #include <cstdio>
+  #include <cstdlib>
+  #define MPD_UINT8_C(x) (static_cast<uint8_t>(x))
+extern "C" {
+#else
+  #include <inttypes.h>
+  #include <limits.h>
+  #include <stdint.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #define MPD_UINT8_C(x) ((uint8_t)x)
+  #undef inline
+  #define inline __inline
+#endif
+
+
+#define MPD_PRAGMA(x)
+#define MPD_HIDE_SYMBOLS_START
+#define MPD_HIDE_SYMBOLS_END
+#define EXTINLINE extern inline
+
+#define IMPORTEXPORT
+
+#if defined (BUILD_LIBMPDEC)
+  #undef IMPORTEXPORT
+  #define IMPORTEXPORT __declspec(dllexport)
+#elif defined(_DLL)
+  #undef IMPORTEXPORT
+  #define IMPORTEXPORT __declspec(dllimport)
+#endif
+
+
+/******************************************************************************/
+/*                                  Version                                   */
+/******************************************************************************/
+
+#define MPD_MAJOR_VERSION 4
+#define MPD_MINOR_VERSION 0
+#define MPD_MICRO_VERSION 1
+
+#define MPD_VERSION "4.0.1"
+
+#define MPD_VERSION_HEX ((MPD_MAJOR_VERSION << 24) | \
+                         (MPD_MINOR_VERSION << 16) | \
+                         (MPD_MICRO_VERSION <<  8))
+
+IMPORTEXPORT const char *mpd_version(void);
+
+
+/******************************************************************************/
+/*                      Types for 32 bit architectures                        */
+/******************************************************************************/
+
+/* ABI: 32-bit */
+#define MPD_CONFIG_32 1
+
+#ifdef MPD_CONFIG_64
+  #error "cannot use MPD_CONFIG_64 with 32-bit header."
+#endif
+
+#ifdef CONFIG_64
+  #error "cannot use CONFIG_64 with 32-bit header."
+#endif
+
+
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT32_MAX
+#define MPD_BITS_PER_UINT 32
+typedef uint32_t mpd_uint_t;  /* unsigned mod type */
+
+#ifndef MPD_LEGACY_COMPILER
+#define MPD_UUINT_MAX UINT64_MAX
+typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */
+#endif
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for dec->len, dec->exp, ctx->prec */
+#define MPD_SSIZE_MAX INT32_MAX
+#define MPD_SSIZE_MIN INT32_MIN
+typedef int32_t mpd_ssize_t;
+#define _mpd_strtossize strtol
+
+/* decimal arithmetic */
+#define MPD_RADIX 1000000000UL  /* 10**9 */
+#define MPD_RDIGITS 9
+#define MPD_MAX_POW10 9
+#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */
+#define MPD_MAX_PREC 425000000L
+#define MPD_MAX_PREC_LOG2 32
+#define MPD_ELIMIT 425000001L
+#define MPD_MAX_EMAX 425000000L        /* ELIMIT-1 */
+#define MPD_MIN_EMIN (-425000000L)     /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 1000000001L       /* allows for emax=999999999 in the tests */
+#define MPD_EXP_CLAMP (-2000000001L)  /* allows for emin=-999999999 in the tests */
+#define MPD_MAXIMPORT 94444445L       /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+#define MPD_IEEE_CONTEXT_MAX_BITS 256 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu32
+#define PRI_mpd_ssize_t PRIi32
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+  #error "unsupported platform: need mpd_size_t == mpd_uint_t"
+#endif
+
+
+/******************************************************************************/
+/*                                Context                                     */
+/******************************************************************************/
+
+enum {
+    MPD_ROUND_UP,          /* round away from 0               */
+    MPD_ROUND_DOWN,        /* round toward 0 (truncate)       */
+    MPD_ROUND_CEILING,     /* round toward +infinity          */
+    MPD_ROUND_FLOOR,       /* round toward -infinity          */
+    MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
+    MPD_ROUND_HALF_DOWN,   /* 0.5 is rounded down             */
+    MPD_ROUND_HALF_EVEN,   /* 0.5 is rounded to even          */
+    MPD_ROUND_05UP,        /* round zero or five away from 0  */
+    MPD_ROUND_TRUNC,       /* truncate, but set infinity      */
+    MPD_ROUND_GUARD
+};
+
+enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD };
+
+IMPORTEXPORT extern const char * const mpd_round_string[MPD_ROUND_GUARD];
+IMPORTEXPORT extern const char * const mpd_clamp_string[MPD_CLAMP_GUARD];
+
+
+typedef struct mpd_context_t {
+    mpd_ssize_t prec;   /* precision */
+    mpd_ssize_t emax;   /* max positive exp */
+    mpd_ssize_t emin;   /* min negative exp */
+    uint32_t traps;     /* status events that should be trapped */
+    uint32_t status;    /* status flags */
+    uint32_t newtrap;   /* set by mpd_addstatus_raise() */
+    int      round;     /* rounding mode */
+    int      clamp;     /* clamp mode */
+    int      allcr;     /* all functions correctly rounded */
+} mpd_context_t;
+
+
+/* Status flags */
+#define MPD_Clamped             0x00000001U
+#define MPD_Conversion_syntax   0x00000002U
+#define MPD_Division_by_zero    0x00000004U
+#define MPD_Division_impossible 0x00000008U
+#define MPD_Division_undefined  0x00000010U
+#define MPD_Fpu_error           0x00000020U
+#define MPD_Inexact             0x00000040U
+#define MPD_Invalid_context     0x00000080U
+#define MPD_Invalid_operation   0x00000100U
+#define MPD_Malloc_error        0x00000200U
+#define MPD_Not_implemented     0x00000400U
+#define MPD_Overflow            0x00000800U
+#define MPD_Rounded             0x00001000U
+#define MPD_Subnormal           0x00002000U
+#define MPD_Underflow           0x00004000U
+#define MPD_Max_status         (0x00008000U-1U)
+
+/* Conditions that result in an IEEE 754 exception */
+#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax |   \
+                                    MPD_Division_impossible | \
+                                    MPD_Division_undefined |  \
+                                    MPD_Fpu_error |           \
+                                    MPD_Invalid_context |     \
+                                    MPD_Invalid_operation |   \
+                                    MPD_Malloc_error)         \
+
+/* Errors that require the result of an operation to be set to NaN */
+#define MPD_Errors (MPD_IEEE_Invalid_operation | \
+                    MPD_Division_by_zero)
+
+/* Default traps */
+#define MPD_Traps (MPD_IEEE_Invalid_operation | \
+                   MPD_Division_by_zero |       \
+                   MPD_Overflow |               \
+                   MPD_Underflow)
+
+/* Official name */
+#define MPD_Insufficient_storage MPD_Malloc_error
+
+/* IEEE 754 interchange format contexts */
+#define MPD_DECIMAL32 32
+#define MPD_DECIMAL64 64
+#define MPD_DECIMAL128 128
+
+
+#define MPD_MINALLOC_MIN 2
+#define MPD_MINALLOC_MAX 64
+IMPORTEXPORT extern mpd_ssize_t MPD_MINALLOC;
+IMPORTEXPORT extern void (* mpd_traphandler)(mpd_context_t *);
+IMPORTEXPORT void mpd_dflt_traphandler(mpd_context_t *);
+
+IMPORTEXPORT void mpd_setminalloc(mpd_ssize_t n);
+IMPORTEXPORT void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec);
+
+IMPORTEXPORT void mpd_maxcontext(mpd_context_t *ctx);
+IMPORTEXPORT void mpd_defaultcontext(mpd_context_t *ctx);
+IMPORTEXPORT void mpd_basiccontext(mpd_context_t *ctx);
+IMPORTEXPORT int mpd_ieee_context(mpd_context_t *ctx, int bits);
+
+IMPORTEXPORT mpd_ssize_t mpd_getprec(const mpd_context_t *ctx);
+IMPORTEXPORT mpd_ssize_t mpd_getemax(const mpd_context_t *ctx);
+IMPORTEXPORT mpd_ssize_t mpd_getemin(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getround(const mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_gettraps(const mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_getstatus(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getclamp(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getcr(const mpd_context_t *ctx);
+
+IMPORTEXPORT int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
+IMPORTEXPORT int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
+IMPORTEXPORT int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
+IMPORTEXPORT int mpd_qsetround(mpd_context_t *ctx, int newround);
+IMPORTEXPORT int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
+IMPORTEXPORT int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
+IMPORTEXPORT int mpd_qsetclamp(mpd_context_t *ctx, int c);
+IMPORTEXPORT int mpd_qsetcr(mpd_context_t *ctx, int c);
+IMPORTEXPORT void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags);
+
+
+/******************************************************************************/
+/*                           Decimal Arithmetic                               */
+/******************************************************************************/
+
+/* mpd_t flags */
+#define MPD_POS                 MPD_UINT8_C(0)
+#define MPD_NEG                 MPD_UINT8_C(1)
+#define MPD_INF                 MPD_UINT8_C(2)
+#define MPD_NAN                 MPD_UINT8_C(4)
+#define MPD_SNAN                MPD_UINT8_C(8)
+#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN)
+#define MPD_STATIC              MPD_UINT8_C(16)
+#define MPD_STATIC_DATA         MPD_UINT8_C(32)
+#define MPD_SHARED_DATA         MPD_UINT8_C(64)
+#define MPD_CONST_DATA          MPD_UINT8_C(128)
+#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA)
+
+/* mpd_t */
+typedef struct mpd_t {
+    uint8_t flags;
+    mpd_ssize_t exp;
+    mpd_ssize_t digits;
+    mpd_ssize_t len;
+    mpd_ssize_t alloc;
+    mpd_uint_t *data;
+} mpd_t;
+
+
+/******************************************************************************/
+/*                                    Triple                                  */
+/******************************************************************************/
+
+/* status cases for getting a triple */
+enum mpd_triple_class {
+  MPD_TRIPLE_NORMAL,
+  MPD_TRIPLE_INF,
+  MPD_TRIPLE_QNAN,
+  MPD_TRIPLE_SNAN,
+  MPD_TRIPLE_ERROR,
+};
+
+typedef struct {
+  enum mpd_triple_class tag;
+  uint8_t sign;
+  uint64_t hi;
+  uint64_t lo;
+  int64_t exp;
+} mpd_uint128_triple_t;
+
+IMPORTEXPORT int mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status);
+IMPORTEXPORT mpd_uint128_triple_t mpd_as_uint128_triple(const mpd_t *a);
+
+
+/******************************************************************************/
+/*                       Quiet, thread-safe functions                         */
+/******************************************************************************/
+
+/* format specification */
+typedef struct mpd_spec_t {
+    mpd_ssize_t min_width; /* minimum field width */
+    mpd_ssize_t prec;      /* fraction digits or significant digits */
+    char type;             /* conversion specifier */
+    char align;            /* alignment */
+    char sign;             /* sign printing/alignment */
+    char sign_coerce;      /* coerce to positive zero */
+    char fill[5];          /* fill character */
+    const char *dot;       /* decimal point */
+    const char *sep;       /* thousands separator */
+    const char *grouping;  /* grouping of digits */
+} mpd_spec_t;
+
+/* output to a string */
+IMPORTEXPORT char *mpd_to_sci(const mpd_t *dec, int fmt);
+IMPORTEXPORT char *mpd_to_eng(const mpd_t *dec, int fmt);
+IMPORTEXPORT mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt);
+IMPORTEXPORT mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt);
+IMPORTEXPORT int mpd_validate_lconv(mpd_spec_t *spec);
+IMPORTEXPORT int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps);
+IMPORTEXPORT char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status);
+
+#define MPD_NUM_FLAGS 15
+#define MPD_MAX_FLAG_STRING 208
+#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18)
+#define MPD_MAX_SIGNAL_LIST 121
+IMPORTEXPORT int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags);
+IMPORTEXPORT int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]);
+IMPORTEXPORT int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]);
+
+/* output to a file */
+IMPORTEXPORT void mpd_fprint(FILE *file, const mpd_t *dec);
+IMPORTEXPORT void mpd_print(const mpd_t *dec);
+
+/* assignment from a string */
+IMPORTEXPORT void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status);
+
+/* set to NaN with error flags */
+IMPORTEXPORT void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status);
+/* set a special with sign and type */
+IMPORTEXPORT void mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type);
+/* set coefficient to zero or all nines */
+IMPORTEXPORT void mpd_zerocoeff(mpd_t *result);
+IMPORTEXPORT void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly assign a C integer type to an mpd_t */
+IMPORTEXPORT void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status);
+#endif
+
+/* quietly assign a C integer type to an mpd_t with a static coefficient */
+IMPORTEXPORT void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly get a C integer type from an mpd_t */
+IMPORTEXPORT mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status);
+
+IMPORTEXPORT int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status);
+#endif
+
+/* quiet functions */
+IMPORTEXPORT int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx);
+
+IMPORTEXPORT int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_cxx(mpd_t *result, const mpd_t *a);
+IMPORTEXPORT mpd_t *mpd_qncopy(const mpd_t *a);
+IMPORTEXPORT int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status);
+
+IMPORTEXPORT void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_same_quantum(const mpd_t *a, const mpd_t *b);
+
+IMPORTEXPORT void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n);
+IMPORTEXPORT void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status);
+IMPORTEXPORT int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_cmp_total(const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b);
+
+IMPORTEXPORT void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status);
+IMPORTEXPORT void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+#endif
+
+
+IMPORTEXPORT size_t mpd_sizeinbase(const mpd_t *a, uint32_t base);
+IMPORTEXPORT void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+                                  uint8_t srcsign, uint32_t srcbase,
+                                  const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen,
+                                  uint8_t srcsign, uint32_t srcbase,
+                                  const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base,
+                                    const mpd_t *src, uint32_t *status);
+IMPORTEXPORT size_t mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t base,
+                                    const mpd_t *src, uint32_t *status);
+
+
+/******************************************************************************/
+/*                           Signalling functions                             */
+/******************************************************************************/
+
+IMPORTEXPORT char *mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+IMPORTEXPORT size_t mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+IMPORTEXPORT size_t mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_finalize(mpd_t *result, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+#endif
+IMPORTEXPORT mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx);
+#endif
+IMPORTEXPORT void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+#endif
+
+
+/******************************************************************************/
+/*                       Get attributes of a decimal                          */
+/******************************************************************************/
+
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_adjexp(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_etop(const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_msword(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_word_digits(mpd_uint_t word);
+/* most significant digit of a word */
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_msd(mpd_uint_t word);
+/* least significant digit of a word */
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_lsd(mpd_uint_t word);
+/* coefficient size needed to store 'digits' */
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits);
+/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */
+IMPORTEXPORT EXTINLINE int mpd_exp_digits(mpd_ssize_t exp);
+IMPORTEXPORT EXTINLINE int mpd_iscanonical(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isfinite(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isinfinite(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isinteger(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnegative(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_ispositive(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isqnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_issigned(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_issnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isspecial(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_iszero(const mpd_t *dec);
+/* undefined for special numbers */
+IMPORTEXPORT EXTINLINE int mpd_iszerocoeff(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx);
+/* odd word */
+IMPORTEXPORT EXTINLINE int mpd_isoddword(mpd_uint_t word);
+/* odd coefficient */
+IMPORTEXPORT EXTINLINE int mpd_isoddcoeff(const mpd_t *dec);
+/* odd decimal, only defined for integers */
+IMPORTEXPORT int mpd_isodd(const mpd_t *dec);
+/* even decimal, only defined for integers */
+IMPORTEXPORT int mpd_iseven(const mpd_t *dec);
+/* 0 if dec is positive, 1 if dec is negative */
+IMPORTEXPORT EXTINLINE uint8_t mpd_sign(const mpd_t *dec);
+/* 1 if dec is positive, -1 if dec is negative */
+IMPORTEXPORT EXTINLINE int mpd_arith_sign(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE long mpd_radix(void);
+IMPORTEXPORT EXTINLINE int mpd_isdynamic(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isstatic(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isdynamic_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isstatic_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isshared_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isconst_data(const mpd_t *dec);
+IMPORTEXPORT mpd_ssize_t mpd_trail_zeros(const mpd_t *dec);
+
+
+/******************************************************************************/
+/*                       Set attributes of a decimal                          */
+/******************************************************************************/
+
+/* set number of decimal digits in the coefficient */
+IMPORTEXPORT EXTINLINE void mpd_setdigits(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_sign(mpd_t *result, uint8_t sign);
+/* copy sign from another decimal */
+IMPORTEXPORT EXTINLINE void mpd_signcpy(mpd_t *result, const mpd_t *a);
+IMPORTEXPORT EXTINLINE void mpd_set_infinity(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_qnan(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_snan(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_negative(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_positive(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_dynamic(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_static(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_dynamic_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_static_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_shared_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_const_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_clear_flags(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_flags(mpd_t *result, uint8_t flags);
+IMPORTEXPORT EXTINLINE void mpd_copy_flags(mpd_t *result, const mpd_t *a);
+
+
+/******************************************************************************/
+/*                              Error Macros                                  */
+/******************************************************************************/
+
+#define mpd_err_fatal(...) \
+    do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__);  fputc('\n', stderr);    \
+        abort();                                               \
+    } while (0)
+#define mpd_err_warn(...) \
+    do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__); fputc('\n', stderr);       \
+    } while (0)
+
+
+/******************************************************************************/
+/*                            Memory handling                                 */
+/******************************************************************************/
+
+IMPORTEXPORT extern void *(* mpd_mallocfunc)(size_t size);
+IMPORTEXPORT extern void *(* mpd_callocfunc)(size_t nmemb, size_t size);
+IMPORTEXPORT extern void *(* mpd_reallocfunc)(void *ptr, size_t size);
+IMPORTEXPORT extern void (* mpd_free)(void *ptr);
+
+IMPORTEXPORT void *mpd_callocfunc_em(size_t nmemb, size_t size);
+
+IMPORTEXPORT void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size);
+IMPORTEXPORT void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size);
+IMPORTEXPORT void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err);
+IMPORTEXPORT void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size);
+
+IMPORTEXPORT mpd_t *mpd_qnew(void);
+IMPORTEXPORT mpd_t *mpd_new(mpd_context_t *ctx);
+IMPORTEXPORT mpd_t *mpd_qnew_size(mpd_ssize_t nwords);
+IMPORTEXPORT EXTINLINE void mpd_del(mpd_t *dec);
+
+IMPORTEXPORT EXTINLINE void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
+IMPORTEXPORT EXTINLINE int mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+IMPORTEXPORT EXTINLINE int mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+IMPORTEXPORT EXTINLINE void mpd_minalloc(mpd_t *result);
+
+IMPORTEXPORT int mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+
+#undef IMPORTEXPORT
+#undef EXTINLINE
+
+#ifdef __cplusplus
+} /* END extern "C" */
+#endif
+
+
+#endif /* LIBMPDEC_MPDECIMAL_H_ */
diff --git a/libmpdec/mpdecimal64vc.h b/libmpdec/mpdecimal64vc.h
new file mode 100644 (file)
index 0000000..dce6ebb
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_MPDECIMAL_H_
+#define LIBMPDEC_MPDECIMAL_H_
+
+
+#ifdef __cplusplus
+  #include <cinttypes>
+  #include <climits>
+  #include <cstdint>
+  #include <cstdio>
+  #include <cstdlib>
+  #define MPD_UINT8_C(x) (static_cast<uint8_t>(x))
+extern "C" {
+#else
+  #include <inttypes.h>
+  #include <limits.h>
+  #include <stdint.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #define MPD_UINT8_C(x) ((uint8_t)x)
+  #undef inline
+  #define inline __inline
+#endif
+
+
+#define MPD_PRAGMA(x)
+#define MPD_HIDE_SYMBOLS_START
+#define MPD_HIDE_SYMBOLS_END
+#define EXTINLINE extern inline
+
+#define IMPORTEXPORT
+
+#if defined (BUILD_LIBMPDEC)
+  #undef IMPORTEXPORT
+  #define IMPORTEXPORT __declspec(dllexport)
+#elif defined(_DLL)
+  #undef IMPORTEXPORT
+  #define IMPORTEXPORT __declspec(dllimport)
+#endif
+
+
+/******************************************************************************/
+/*                                  Version                                   */
+/******************************************************************************/
+
+#define MPD_MAJOR_VERSION 4
+#define MPD_MINOR_VERSION 0
+#define MPD_MICRO_VERSION 1
+
+#define MPD_VERSION "4.0.1"
+
+#define MPD_VERSION_HEX ((MPD_MAJOR_VERSION << 24) | \
+                         (MPD_MINOR_VERSION << 16) | \
+                         (MPD_MICRO_VERSION <<  8))
+
+IMPORTEXPORT const char *mpd_version(void);
+
+
+/******************************************************************************/
+/*                      Types for 64 bit architectures                        */
+/******************************************************************************/
+
+/* ABI: 64-bit */
+#define MPD_CONFIG_64 1
+
+#ifdef MPD_CONFIG_32
+  #error "cannot use MPD_CONFIG_32 with 64-bit header."
+#endif
+
+#ifdef CONFIG_32
+  #error "cannot use CONFIG_32 with 64-bit header."
+#endif
+
+
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT64_MAX
+#define MPD_BITS_PER_UINT 64
+typedef uint64_t mpd_uint_t;  /* unsigned mod type */
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for exp, digits, len, prec */
+#define MPD_SSIZE_MAX INT64_MAX
+#define MPD_SSIZE_MIN INT64_MIN
+typedef int64_t mpd_ssize_t;
+#define _mpd_strtossize strtoll
+
+/* decimal arithmetic */
+#define MPD_RADIX 10000000000000000000ULL  /* 10**19 */
+#define MPD_RDIGITS 19
+#define MPD_MAX_POW10 19
+#define MPD_EXPDIGITS 19  /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 4294967296ULL      /* 2**32 */
+#define MPD_MAX_PREC 999999999999999999LL
+#define MPD_MAX_PREC_LOG2 64
+#define MPD_ELIMIT  1000000000000000000LL
+#define MPD_MAX_EMAX   999999999999999999LL    /* ELIMIT-1 */
+#define MPD_MIN_EMIN  (-999999999999999999LL)  /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 2000000000000000001LL
+#define MPD_EXP_CLAMP (-4000000000000000001LL)
+#define MPD_MAXIMPORT 105263157894736842L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+#define MPD_IEEE_CONTEXT_MAX_BITS 512     /* 16*(log2(MPD_MAX_EMAX / 3)-3) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu64
+#define PRI_mpd_ssize_t PRIi64
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+  #error "unsupported platform: need mpd_size_t == mpd_uint_t"
+#endif
+
+
+/******************************************************************************/
+/*                                Context                                     */
+/******************************************************************************/
+
+enum {
+    MPD_ROUND_UP,          /* round away from 0               */
+    MPD_ROUND_DOWN,        /* round toward 0 (truncate)       */
+    MPD_ROUND_CEILING,     /* round toward +infinity          */
+    MPD_ROUND_FLOOR,       /* round toward -infinity          */
+    MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
+    MPD_ROUND_HALF_DOWN,   /* 0.5 is rounded down             */
+    MPD_ROUND_HALF_EVEN,   /* 0.5 is rounded to even          */
+    MPD_ROUND_05UP,        /* round zero or five away from 0  */
+    MPD_ROUND_TRUNC,       /* truncate, but set infinity      */
+    MPD_ROUND_GUARD
+};
+
+enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD };
+
+IMPORTEXPORT extern const char * const mpd_round_string[MPD_ROUND_GUARD];
+IMPORTEXPORT extern const char * const mpd_clamp_string[MPD_CLAMP_GUARD];
+
+
+typedef struct mpd_context_t {
+    mpd_ssize_t prec;   /* precision */
+    mpd_ssize_t emax;   /* max positive exp */
+    mpd_ssize_t emin;   /* min negative exp */
+    uint32_t traps;     /* status events that should be trapped */
+    uint32_t status;    /* status flags */
+    uint32_t newtrap;   /* set by mpd_addstatus_raise() */
+    int      round;     /* rounding mode */
+    int      clamp;     /* clamp mode */
+    int      allcr;     /* all functions correctly rounded */
+} mpd_context_t;
+
+
+/* Status flags */
+#define MPD_Clamped             0x00000001U
+#define MPD_Conversion_syntax   0x00000002U
+#define MPD_Division_by_zero    0x00000004U
+#define MPD_Division_impossible 0x00000008U
+#define MPD_Division_undefined  0x00000010U
+#define MPD_Fpu_error           0x00000020U
+#define MPD_Inexact             0x00000040U
+#define MPD_Invalid_context     0x00000080U
+#define MPD_Invalid_operation   0x00000100U
+#define MPD_Malloc_error        0x00000200U
+#define MPD_Not_implemented     0x00000400U
+#define MPD_Overflow            0x00000800U
+#define MPD_Rounded             0x00001000U
+#define MPD_Subnormal           0x00002000U
+#define MPD_Underflow           0x00004000U
+#define MPD_Max_status         (0x00008000U-1U)
+
+/* Conditions that result in an IEEE 754 exception */
+#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax |   \
+                                    MPD_Division_impossible | \
+                                    MPD_Division_undefined |  \
+                                    MPD_Fpu_error |           \
+                                    MPD_Invalid_context |     \
+                                    MPD_Invalid_operation |   \
+                                    MPD_Malloc_error)         \
+
+/* Errors that require the result of an operation to be set to NaN */
+#define MPD_Errors (MPD_IEEE_Invalid_operation | \
+                    MPD_Division_by_zero)
+
+/* Default traps */
+#define MPD_Traps (MPD_IEEE_Invalid_operation | \
+                   MPD_Division_by_zero |       \
+                   MPD_Overflow |               \
+                   MPD_Underflow)
+
+/* Official name */
+#define MPD_Insufficient_storage MPD_Malloc_error
+
+/* IEEE 754 interchange format contexts */
+#define MPD_DECIMAL32 32
+#define MPD_DECIMAL64 64
+#define MPD_DECIMAL128 128
+
+
+#define MPD_MINALLOC_MIN 2
+#define MPD_MINALLOC_MAX 64
+IMPORTEXPORT extern mpd_ssize_t MPD_MINALLOC;
+IMPORTEXPORT extern void (* mpd_traphandler)(mpd_context_t *);
+IMPORTEXPORT void mpd_dflt_traphandler(mpd_context_t *);
+
+IMPORTEXPORT void mpd_setminalloc(mpd_ssize_t n);
+IMPORTEXPORT void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec);
+
+IMPORTEXPORT void mpd_maxcontext(mpd_context_t *ctx);
+IMPORTEXPORT void mpd_defaultcontext(mpd_context_t *ctx);
+IMPORTEXPORT void mpd_basiccontext(mpd_context_t *ctx);
+IMPORTEXPORT int mpd_ieee_context(mpd_context_t *ctx, int bits);
+
+IMPORTEXPORT mpd_ssize_t mpd_getprec(const mpd_context_t *ctx);
+IMPORTEXPORT mpd_ssize_t mpd_getemax(const mpd_context_t *ctx);
+IMPORTEXPORT mpd_ssize_t mpd_getemin(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getround(const mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_gettraps(const mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_getstatus(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getclamp(const mpd_context_t *ctx);
+IMPORTEXPORT int mpd_getcr(const mpd_context_t *ctx);
+
+IMPORTEXPORT int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
+IMPORTEXPORT int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
+IMPORTEXPORT int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
+IMPORTEXPORT int mpd_qsetround(mpd_context_t *ctx, int newround);
+IMPORTEXPORT int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
+IMPORTEXPORT int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
+IMPORTEXPORT int mpd_qsetclamp(mpd_context_t *ctx, int c);
+IMPORTEXPORT int mpd_qsetcr(mpd_context_t *ctx, int c);
+IMPORTEXPORT void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags);
+
+
+/******************************************************************************/
+/*                           Decimal Arithmetic                               */
+/******************************************************************************/
+
+/* mpd_t flags */
+#define MPD_POS                 MPD_UINT8_C(0)
+#define MPD_NEG                 MPD_UINT8_C(1)
+#define MPD_INF                 MPD_UINT8_C(2)
+#define MPD_NAN                 MPD_UINT8_C(4)
+#define MPD_SNAN                MPD_UINT8_C(8)
+#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN)
+#define MPD_STATIC              MPD_UINT8_C(16)
+#define MPD_STATIC_DATA         MPD_UINT8_C(32)
+#define MPD_SHARED_DATA         MPD_UINT8_C(64)
+#define MPD_CONST_DATA          MPD_UINT8_C(128)
+#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA)
+
+/* mpd_t */
+typedef struct mpd_t {
+    uint8_t flags;
+    mpd_ssize_t exp;
+    mpd_ssize_t digits;
+    mpd_ssize_t len;
+    mpd_ssize_t alloc;
+    mpd_uint_t *data;
+} mpd_t;
+
+
+/******************************************************************************/
+/*                                    Triple                                  */
+/******************************************************************************/
+
+/* status cases for getting a triple */
+enum mpd_triple_class {
+  MPD_TRIPLE_NORMAL,
+  MPD_TRIPLE_INF,
+  MPD_TRIPLE_QNAN,
+  MPD_TRIPLE_SNAN,
+  MPD_TRIPLE_ERROR,
+};
+
+typedef struct {
+  enum mpd_triple_class tag;
+  uint8_t sign;
+  uint64_t hi;
+  uint64_t lo;
+  int64_t exp;
+} mpd_uint128_triple_t;
+
+IMPORTEXPORT int mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status);
+IMPORTEXPORT mpd_uint128_triple_t mpd_as_uint128_triple(const mpd_t *a);
+
+
+/******************************************************************************/
+/*                       Quiet, thread-safe functions                         */
+/******************************************************************************/
+
+/* format specification */
+typedef struct mpd_spec_t {
+    mpd_ssize_t min_width; /* minimum field width */
+    mpd_ssize_t prec;      /* fraction digits or significant digits */
+    char type;             /* conversion specifier */
+    char align;            /* alignment */
+    char sign;             /* sign printing/alignment */
+    char sign_coerce;      /* coerce to positive zero */
+    char fill[5];          /* fill character */
+    const char *dot;       /* decimal point */
+    const char *sep;       /* thousands separator */
+    const char *grouping;  /* grouping of digits */
+} mpd_spec_t;
+
+/* output to a string */
+IMPORTEXPORT char *mpd_to_sci(const mpd_t *dec, int fmt);
+IMPORTEXPORT char *mpd_to_eng(const mpd_t *dec, int fmt);
+IMPORTEXPORT mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt);
+IMPORTEXPORT mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt);
+IMPORTEXPORT int mpd_validate_lconv(mpd_spec_t *spec);
+IMPORTEXPORT int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps);
+IMPORTEXPORT char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status);
+
+#define MPD_NUM_FLAGS 15
+#define MPD_MAX_FLAG_STRING 208
+#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18)
+#define MPD_MAX_SIGNAL_LIST 121
+IMPORTEXPORT int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags);
+IMPORTEXPORT int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]);
+IMPORTEXPORT int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]);
+
+/* output to a file */
+IMPORTEXPORT void mpd_fprint(FILE *file, const mpd_t *dec);
+IMPORTEXPORT void mpd_print(const mpd_t *dec);
+
+/* assignment from a string */
+IMPORTEXPORT void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status);
+
+/* set to NaN with error flags */
+IMPORTEXPORT void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status);
+/* set a special with sign and type */
+IMPORTEXPORT void mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type);
+/* set coefficient to zero or all nines */
+IMPORTEXPORT void mpd_zerocoeff(mpd_t *result);
+IMPORTEXPORT void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly assign a C integer type to an mpd_t */
+IMPORTEXPORT void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status);
+IMPORTEXPORT void mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status);
+#endif
+
+/* quietly assign a C integer type to an mpd_t with a static coefficient */
+IMPORTEXPORT void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly get a C integer type from an mpd_t */
+IMPORTEXPORT mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status);
+
+IMPORTEXPORT int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status);
+IMPORTEXPORT uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status);
+#endif
+
+/* quiet functions */
+IMPORTEXPORT int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx);
+
+IMPORTEXPORT int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_cxx(mpd_t *result, const mpd_t *a);
+IMPORTEXPORT mpd_t *mpd_qncopy(const mpd_t *a);
+IMPORTEXPORT int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status);
+IMPORTEXPORT int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status);
+
+IMPORTEXPORT void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_same_quantum(const mpd_t *a, const mpd_t *b);
+
+IMPORTEXPORT void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+IMPORTEXPORT mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n);
+IMPORTEXPORT void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status);
+IMPORTEXPORT int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT int mpd_cmp_total(const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b);
+IMPORTEXPORT int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b);
+
+IMPORTEXPORT void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+IMPORTEXPORT void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status);
+IMPORTEXPORT void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+#endif
+
+
+IMPORTEXPORT size_t mpd_sizeinbase(const mpd_t *a, uint32_t base);
+IMPORTEXPORT void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+                                  uint8_t srcsign, uint32_t srcbase,
+                                  const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen,
+                                  uint8_t srcsign, uint32_t srcbase,
+                                  const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base,
+                                    const mpd_t *src, uint32_t *status);
+IMPORTEXPORT size_t mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t base,
+                                    const mpd_t *src, uint32_t *status);
+
+
+/******************************************************************************/
+/*                           Signalling functions                             */
+/******************************************************************************/
+
+IMPORTEXPORT char *mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+IMPORTEXPORT size_t mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+IMPORTEXPORT size_t mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_finalize(mpd_t *result, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+#endif
+IMPORTEXPORT mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx);
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx);
+#endif
+IMPORTEXPORT void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+
+#ifndef MPD_LEGACY_COMPILER
+IMPORTEXPORT void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+#endif
+
+
+/******************************************************************************/
+/*                          Configuration specific                            */
+/******************************************************************************/
+
+IMPORTEXPORT void mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+IMPORTEXPORT void mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+IMPORTEXPORT void mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+
+
+
+/******************************************************************************/
+/*                       Get attributes of a decimal                          */
+/******************************************************************************/
+
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_adjexp(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_etop(const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_msword(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_word_digits(mpd_uint_t word);
+/* most significant digit of a word */
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_msd(mpd_uint_t word);
+/* least significant digit of a word */
+IMPORTEXPORT EXTINLINE mpd_uint_t mpd_lsd(mpd_uint_t word);
+/* coefficient size needed to store 'digits' */
+IMPORTEXPORT EXTINLINE mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits);
+/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */
+IMPORTEXPORT EXTINLINE int mpd_exp_digits(mpd_ssize_t exp);
+IMPORTEXPORT EXTINLINE int mpd_iscanonical(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isfinite(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isinfinite(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isinteger(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnegative(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_ispositive(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isqnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_issigned(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_issnan(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isspecial(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_iszero(const mpd_t *dec);
+/* undefined for special numbers */
+IMPORTEXPORT EXTINLINE int mpd_iszerocoeff(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx);
+IMPORTEXPORT EXTINLINE int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx);
+/* odd word */
+IMPORTEXPORT EXTINLINE int mpd_isoddword(mpd_uint_t word);
+/* odd coefficient */
+IMPORTEXPORT EXTINLINE int mpd_isoddcoeff(const mpd_t *dec);
+/* odd decimal, only defined for integers */
+IMPORTEXPORT int mpd_isodd(const mpd_t *dec);
+/* even decimal, only defined for integers */
+IMPORTEXPORT int mpd_iseven(const mpd_t *dec);
+/* 0 if dec is positive, 1 if dec is negative */
+IMPORTEXPORT EXTINLINE uint8_t mpd_sign(const mpd_t *dec);
+/* 1 if dec is positive, -1 if dec is negative */
+IMPORTEXPORT EXTINLINE int mpd_arith_sign(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE long mpd_radix(void);
+IMPORTEXPORT EXTINLINE int mpd_isdynamic(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isstatic(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isdynamic_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isstatic_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isshared_data(const mpd_t *dec);
+IMPORTEXPORT EXTINLINE int mpd_isconst_data(const mpd_t *dec);
+IMPORTEXPORT mpd_ssize_t mpd_trail_zeros(const mpd_t *dec);
+
+
+/******************************************************************************/
+/*                       Set attributes of a decimal                          */
+/******************************************************************************/
+
+/* set number of decimal digits in the coefficient */
+IMPORTEXPORT EXTINLINE void mpd_setdigits(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_sign(mpd_t *result, uint8_t sign);
+/* copy sign from another decimal */
+IMPORTEXPORT EXTINLINE void mpd_signcpy(mpd_t *result, const mpd_t *a);
+IMPORTEXPORT EXTINLINE void mpd_set_infinity(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_qnan(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_snan(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_negative(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_positive(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_dynamic(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_static(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_dynamic_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_static_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_shared_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_const_data(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_clear_flags(mpd_t *result);
+IMPORTEXPORT EXTINLINE void mpd_set_flags(mpd_t *result, uint8_t flags);
+IMPORTEXPORT EXTINLINE void mpd_copy_flags(mpd_t *result, const mpd_t *a);
+
+
+/******************************************************************************/
+/*                              Error Macros                                  */
+/******************************************************************************/
+
+#define mpd_err_fatal(...) \
+    do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__);  fputc('\n', stderr);    \
+        abort();                                               \
+    } while (0)
+#define mpd_err_warn(...) \
+    do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \
+        fprintf(stderr, __VA_ARGS__); fputc('\n', stderr);       \
+    } while (0)
+
+
+/******************************************************************************/
+/*                            Memory handling                                 */
+/******************************************************************************/
+
+IMPORTEXPORT extern void *(* mpd_mallocfunc)(size_t size);
+IMPORTEXPORT extern void *(* mpd_callocfunc)(size_t nmemb, size_t size);
+IMPORTEXPORT extern void *(* mpd_reallocfunc)(void *ptr, size_t size);
+IMPORTEXPORT extern void (* mpd_free)(void *ptr);
+
+IMPORTEXPORT void *mpd_callocfunc_em(size_t nmemb, size_t size);
+
+IMPORTEXPORT void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size);
+IMPORTEXPORT void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size);
+IMPORTEXPORT void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err);
+IMPORTEXPORT void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size);
+
+IMPORTEXPORT mpd_t *mpd_qnew(void);
+IMPORTEXPORT mpd_t *mpd_new(mpd_context_t *ctx);
+IMPORTEXPORT mpd_t *mpd_qnew_size(mpd_ssize_t nwords);
+IMPORTEXPORT EXTINLINE void mpd_del(mpd_t *dec);
+
+IMPORTEXPORT EXTINLINE void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
+IMPORTEXPORT EXTINLINE int mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+IMPORTEXPORT EXTINLINE int mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status);
+IMPORTEXPORT EXTINLINE void mpd_minalloc(mpd_t *result);
+
+IMPORTEXPORT int mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+IMPORTEXPORT int mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx);
+
+#undef IMPORTEXPORT
+#undef EXTINLINE
+
+#ifdef __cplusplus
+} /* END extern "C" */
+#endif
+
+
+#endif /* LIBMPDEC_MPDECIMAL_H_ */
diff --git a/libmpdec/mpsignal.c b/libmpdec/mpsignal.c
new file mode 100644 (file)
index 0000000..85f3c0e
--- /dev/null
@@ -0,0 +1,966 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mpdecimal.h"
+
+
+/* Signaling wrappers for the quiet functions in mpdecimal.c. */
+
+
+char *
+mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx)
+{
+    char *ret;
+    uint32_t status = 0;
+    ret = mpd_qformat(dec, fmt, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+void
+mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+               uint8_t srcsign, uint32_t base, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qimport_u16(result, srcdata, srclen, srcsign, base, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen,
+               uint8_t srcsign, uint32_t base, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qimport_u32(result, srcdata, srclen, srcsign, base, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+size_t
+mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src,
+               mpd_context_t *ctx)
+{
+    size_t n;
+    uint32_t status = 0;
+    n = mpd_qexport_u16(rdata, rlen, base, src, &status);
+    mpd_addstatus_raise(ctx, status);
+    return n;
+}
+
+size_t
+mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src,
+               mpd_context_t *ctx)
+{
+    size_t n;
+    uint32_t status = 0;
+    n = mpd_qexport_u32(rdata, rlen, base, src, &status);
+    mpd_addstatus_raise(ctx, status);
+    return n;
+}
+
+void
+mpd_finalize(mpd_t *result, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qfinalize(result, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+int
+mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (mpd_qcheck_nan(result, a, ctx, &status)) {
+        mpd_addstatus_raise(ctx, status);
+        return 1;
+    }
+    return 0;
+}
+
+int
+mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (mpd_qcheck_nans(result, a, b, ctx, &status)) {
+        mpd_addstatus_raise(ctx, status);
+        return 1;
+    }
+    return 0;
+}
+
+void
+mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_string(result, s, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmaxcoeff(result, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+/* set static mpd from signed integer */
+void
+mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_ssize(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_i32(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifdef CONFIG_64
+void
+mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_i64(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+/* set static mpd from unsigned integer */
+void
+mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_uint(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_u32(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifdef CONFIG_64
+void
+mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsset_u64(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+/* set mpd from signed integer */
+void
+mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_ssize(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_i32(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_i64(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+/* set mpd from unsigned integer */
+void
+mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_uint(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_u32(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qset_u64(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+/* convert mpd to signed integer */
+mpd_ssize_t
+mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_ssize_t ret;
+
+    ret = mpd_qget_ssize(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+int32_t
+mpd_get_i32(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    int32_t ret;
+
+    ret = mpd_qget_i32(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+#ifndef LEGACY_COMPILER
+int64_t
+mpd_get_i64(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    int64_t ret;
+
+    ret = mpd_qget_i64(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+#endif
+
+mpd_uint_t
+mpd_get_uint(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_uint_t ret;
+
+    ret = mpd_qget_uint(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+mpd_uint_t
+mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_uint_t ret;
+
+    ret = mpd_qabs_uint(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+uint32_t
+mpd_get_u32(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    uint32_t ret;
+
+    ret = mpd_qget_u32(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+
+#ifndef LEGACY_COMPILER
+uint64_t
+mpd_get_u64(const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    uint64_t ret;
+
+    ret = mpd_qget_u64(a, &status);
+    mpd_addstatus_raise(ctx, status);
+    return ret;
+}
+#endif
+
+void
+mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qand(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qcopy(result, a, &status)) {
+        mpd_addstatus_raise(ctx, status);
+    }
+}
+
+void
+mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    mpd_copy(result, a, ctx);
+}
+
+void
+mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qcopy_abs(result, a, &status)) {
+        mpd_addstatus_raise(ctx, status);
+    }
+}
+
+void
+mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qcopy_negate(result, a, &status)) {
+        mpd_addstatus_raise(ctx, status);
+    }
+}
+
+void
+mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    if (!mpd_qcopy_sign(result, a, b, &status)) {
+        mpd_addstatus_raise(ctx, status);
+    }
+}
+
+void
+mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qinvert(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qlogb(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qor(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qrotate(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qscaleb(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qshiftl(result, a, n, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+mpd_uint_t
+mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_uint_t rnd;
+
+    rnd = mpd_qshiftr(result, a, n, &status);
+    mpd_addstatus_raise(ctx, status);
+    return rnd;
+}
+
+void
+mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qshiftn(result, a, n, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qshift(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qxor(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qabs(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+int
+mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    int c;
+    c = mpd_qcmp(a, b, &status);
+    mpd_addstatus_raise(ctx, status);
+    return c;
+}
+
+int
+mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    int c;
+    c = mpd_qcompare(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+    return c;
+}
+
+int
+mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    int c;
+    c = mpd_qcompare_signal(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+    return c;
+}
+
+void
+mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_ssize(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_i32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_i64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_uint(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_u32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qadd_u64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_ssize(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_i32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_i64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_uint(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_u32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsub_u64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv(q, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_ssize(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_i32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_i64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_uint(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_u32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdiv_u64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdivmod(q, r, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qdivint(q, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qexp(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
+        mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qfma(result, a, b, c, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qln(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qlog10(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmax(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmax_mag(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmin(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmin_mag(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qminus(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_ssize(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_i32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_i64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_uint(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_u32(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+#ifndef LEGACY_COMPILER
+void
+mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qmul_u64(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+#endif
+
+void
+mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qnext_minus(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qnext_plus(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qnext_toward(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qplus(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qpow(result, base, exp, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod,
+           mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qpowmod(result, base, exp, mod, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qquantize(result, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qrescale(result, a, exp, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qreduce(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qrem(r, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qrem_near(r, a, b, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qround_to_intx(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qround_to_int(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qtrunc(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qfloor(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qceil(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qsqrt(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
+
+void
+mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx)
+{
+    uint32_t status = 0;
+    mpd_qinvroot(result, a, ctx, &status);
+    mpd_addstatus_raise(ctx, status);
+}
diff --git a/libmpdec/numbertheory.c b/libmpdec/numbertheory.c
new file mode 100644 (file)
index 0000000..6490f97
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "bits.h"
+#include "numbertheory.h"
+#include "mpdecimal.h"
+#include "umodarith.h"
+
+
+/* Bignum: Initialize the Number Theoretic Transform. */
+
+/*
+ * Return the nth root of unity in F(p). This corresponds to e**((2*pi*i)/n)
+ * in the Fourier transform. We have w**n == 1 (mod p).
+ *    n := transform length.
+ *    sign := -1 for forward transform, 1 for backward transform.
+ *    modnum := one of {P1, P2, P3}.
+ */
+mpd_uint_t
+_mpd_getkernel(mpd_uint_t n, int sign, int modnum)
+{
+    mpd_uint_t umod, p, r, xi;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+
+    SETMODULUS(modnum);
+    r = mpd_roots[modnum]; /* primitive root of F(p) */
+    p = umod;
+    xi = (p-1) / n;
+
+    if (sign == -1)
+        return POWMOD(r, (p-1-xi));
+    else
+        return POWMOD(r, xi);
+}
+
+/*
+ * Initialize and return transform parameters.
+ *    n := transform length.
+ *    sign := -1 for forward transform, 1 for backward transform.
+ *    modnum := one of {P1, P2, P3}.
+ */
+struct fnt_params *
+_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum)
+{
+    struct fnt_params *tparams;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t kernel, w;
+    mpd_uint_t i;
+    mpd_size_t nhalf;
+
+    assert(ispower2(n));
+    assert(sign == -1 || sign == 1);
+    assert(P1 <= modnum && modnum <= P3);
+
+    nhalf = n/2;
+    tparams = mpd_sh_alloc(sizeof *tparams, nhalf, sizeof (mpd_uint_t));
+    if (tparams == NULL) {
+        return NULL;
+    }
+
+    SETMODULUS(modnum);
+    kernel = _mpd_getkernel(n, sign, modnum);
+
+    tparams->modnum = modnum;
+    tparams->modulus = umod;
+    tparams->kernel = kernel;
+
+    /* wtable[] := w**0, w**1, ..., w**(nhalf-1) */
+    w = 1;
+    for (i = 0; i < nhalf; i++) {
+        tparams->wtable[i] = w;
+        w = MULMOD(w, kernel);
+    }
+
+    return tparams;
+}
+
+/* Initialize wtable of size three. */
+void
+_mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum)
+{
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t kernel;
+
+    SETMODULUS(modnum);
+    kernel = _mpd_getkernel(3, sign, modnum);
+
+    w3table[0] = 1;
+    w3table[1] = kernel;
+    w3table[2] = POWMOD(kernel, 2);
+}
diff --git a/libmpdec/numbertheory.h b/libmpdec/numbertheory.h
new file mode 100644 (file)
index 0000000..fcaead6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_NUMBERTHEORY_H_
+#define LIBMPDEC_NUMBERTHEORY_H_
+
+
+#include "constants.h"
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+/* transform parameters */
+struct fnt_params {
+    int modnum;
+    mpd_uint_t modulus;
+    mpd_uint_t kernel;
+    mpd_uint_t wtable[];
+};
+
+
+mpd_uint_t _mpd_getkernel(mpd_uint_t n, int sign, int modnum);
+struct fnt_params *_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum);
+void _mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum);
+
+
+#ifdef PPRO
+static inline void
+ppro_setmodulus(int modnum, mpd_uint_t *umod, double *dmod, uint32_t dinvmod[3])
+{
+    *dmod = *umod =  mpd_moduli[modnum];
+    dinvmod[0] = mpd_invmoduli[modnum][0];
+    dinvmod[1] = mpd_invmoduli[modnum][1];
+    dinvmod[2] = mpd_invmoduli[modnum][2];
+}
+#else
+static inline void
+std_setmodulus(int modnum, mpd_uint_t *umod)
+{
+    *umod =  mpd_moduli[modnum];
+}
+#endif
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_NUMBERTHEORY_H_ */
diff --git a/libmpdec/sixstep.c b/libmpdec/sixstep.c
new file mode 100644 (file)
index 0000000..a777578
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "bits.h"
+#include "constants.h"
+#include "difradix2.h"
+#include "numbertheory.h"
+#include "mpdecimal.h"
+#include "sixstep.h"
+#include "transpose.h"
+#include "umodarith.h"
+
+
+/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the
+   form 2**n (See literature/six-step.txt). */
+
+
+/* forward transform with sign = -1 */
+int
+six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    struct fnt_params *tparams;
+    mpd_size_t log2n, C, R;
+    mpd_uint_t kernel;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t *x, w0, w1, wstep;
+    mpd_size_t i, k;
+
+
+    assert(ispower2(n));
+    assert(n >= 16);
+    assert(n <= MPD_MAXTRANSFORM_2N);
+
+    log2n = mpd_bsr(n);
+    C = ((mpd_size_t)1) << (log2n / 2);  /* number of columns */
+    R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */
+
+
+    /* Transpose the matrix. */
+    if (!transpose_pow2(a, R, C)) {
+        return 0;
+    }
+
+    /* Length R transform on the rows. */
+    if ((tparams = _mpd_init_fnt_params(R, -1, modnum)) == NULL) {
+        return 0;
+    }
+    for (x = a; x < a+n; x += R) {
+        fnt_dif2(x, R, tparams);
+    }
+
+    /* Transpose the matrix. */
+    if (!transpose_pow2(a, C, R)) {
+        mpd_free(tparams);
+        return 0;
+    }
+
+    /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+    SETMODULUS(modnum);
+    kernel = _mpd_getkernel(n, -1, modnum);
+    for (i = 1; i < R; i++) {
+        w0 = 1;                  /* r**(i*0): initial value for k=0 */
+        w1 = POWMOD(kernel, i);  /* r**(i*1): initial value for k=1 */
+        wstep = MULMOD(w1, w1);  /* r**(2*i) */
+        for (k = 0; k < C; k += 2) {
+            mpd_uint_t x0 = a[i*C+k];
+            mpd_uint_t x1 = a[i*C+k+1];
+            MULMOD2(&x0, w0, &x1, w1);
+            MULMOD2C(&w0, &w1, wstep);  /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */
+            a[i*C+k] = x0;
+            a[i*C+k+1] = x1;
+        }
+    }
+
+    /* Length C transform on the rows. */
+    if (C != R) {
+        mpd_free(tparams);
+        if ((tparams = _mpd_init_fnt_params(C, -1, modnum)) == NULL) {
+            return 0;
+        }
+    }
+    for (x = a; x < a+n; x += C) {
+        fnt_dif2(x, C, tparams);
+    }
+    mpd_free(tparams);
+
+#if 0
+    /* An unordered transform is sufficient for convolution. */
+    /* Transpose the matrix. */
+    if (!transpose_pow2(a, R, C)) {
+        return 0;
+    }
+#endif
+
+    return 1;
+}
+
+
+/* reverse transform, sign = 1 */
+int
+inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+    struct fnt_params *tparams;
+    mpd_size_t log2n, C, R;
+    mpd_uint_t kernel;
+    mpd_uint_t umod;
+#ifdef PPRO
+    double dmod;
+    uint32_t dinvmod[3];
+#endif
+    mpd_uint_t *x, w0, w1, wstep;
+    mpd_size_t i, k;
+
+
+    assert(ispower2(n));
+    assert(n >= 16);
+    assert(n <= MPD_MAXTRANSFORM_2N);
+
+    log2n = mpd_bsr(n);
+    C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */
+    R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */
+
+
+#if 0
+    /* An unordered transform is sufficient for convolution. */
+    /* Transpose the matrix, producing an R*C matrix. */
+    if (!transpose_pow2(a, C, R)) {
+        return 0;
+    }
+#endif
+
+    /* Length C transform on the rows. */
+    if ((tparams = _mpd_init_fnt_params(C, 1, modnum)) == NULL) {
+        return 0;
+    }
+    for (x = a; x < a+n; x += C) {
+        fnt_dif2(x, C, tparams);
+    }
+
+    /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+    SETMODULUS(modnum);
+    kernel = _mpd_getkernel(n, 1, modnum);
+    for (i = 1; i < R; i++) {
+        w0 = 1;
+        w1 = POWMOD(kernel, i);
+        wstep = MULMOD(w1, w1);
+        for (k = 0; k < C; k += 2) {
+            mpd_uint_t x0 = a[i*C+k];
+            mpd_uint_t x1 = a[i*C+k+1];
+            MULMOD2(&x0, w0, &x1, w1);
+            MULMOD2C(&w0, &w1, wstep);
+            a[i*C+k] = x0;
+            a[i*C+k+1] = x1;
+        }
+    }
+
+    /* Transpose the matrix. */
+    if (!transpose_pow2(a, R, C)) {
+        mpd_free(tparams);
+        return 0;
+    }
+
+    /* Length R transform on the rows. */
+    if (R != C) {
+        mpd_free(tparams);
+        if ((tparams = _mpd_init_fnt_params(R, 1, modnum)) == NULL) {
+            return 0;
+        }
+    }
+    for (x = a; x < a+n; x += R) {
+        fnt_dif2(x, R, tparams);
+    }
+    mpd_free(tparams);
+
+    /* Transpose the matrix. */
+    if (!transpose_pow2(a, C, R)) {
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/libmpdec/sixstep.h b/libmpdec/sixstep.h
new file mode 100644 (file)
index 0000000..1cf2ee1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_SIXSTEP_H_
+#define LIBMPDEC_SIXSTEP_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+int six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+int inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_SIXSTEP_H_ */
diff --git a/libmpdec/transpose.c b/libmpdec/transpose.c
new file mode 100644 (file)
index 0000000..8027f71
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "bits.h"
+#include "constants.h"
+#include "mpdecimal.h"
+#include "transpose.h"
+#include "typearith.h"
+
+
+#define BUFSIZE 4096
+#define SIDE 128
+
+
+/* Bignum: The transpose functions are used for very large transforms
+           in sixstep.c and fourstep.c. */
+
+
+/* Definition of the matrix transpose */
+void
+std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols)
+{
+    mpd_size_t idest, isrc;
+    mpd_size_t r, c;
+
+    for (r = 0; r < rows; r++) {
+        isrc = r * cols;
+        idest = r;
+        for (c = 0; c < cols; c++) {
+            dest[idest] = src[isrc];
+            isrc += 1;
+            idest += rows;
+        }
+    }
+}
+
+/*
+ * Swap half-rows of 2^n * (2*2^n) matrix.
+ * FORWARD_CYCLE: even/odd permutation of the halfrows.
+ * BACKWARD_CYCLE: reverse the even/odd permutation.
+ */
+static int
+swap_halfrows_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols, int dir)
+{
+    mpd_uint_t buf1[BUFSIZE];
+    mpd_uint_t buf2[BUFSIZE];
+    mpd_uint_t *readbuf, *writebuf, *hp;
+    mpd_size_t *done, dbits;
+    mpd_size_t b = BUFSIZE, stride;
+    mpd_size_t hn, hmax; /* halfrow number */
+    mpd_size_t m, r=0;
+    mpd_size_t offset;
+    mpd_size_t next;
+
+
+    assert(cols == mul_size_t(2, rows));
+
+    if (dir == FORWARD_CYCLE) {
+        r = rows;
+    }
+    else if (dir == BACKWARD_CYCLE) {
+        r = 2;
+    }
+    else {
+        abort(); /* GCOV_NOT_REACHED */
+    }
+
+    m = cols - 1;
+    hmax = rows; /* cycles start at odd halfrows */
+    dbits = 8 * sizeof *done;
+    if ((done = mpd_calloc(hmax/(sizeof *done) + 1, sizeof *done)) == NULL) {
+        return 0;
+    }
+
+    for (hn = 1; hn <= hmax; hn += 2) {
+
+        if (done[hn/dbits] & mpd_bits[hn%dbits]) {
+            continue;
+        }
+
+        readbuf = buf1; writebuf = buf2;
+
+        for (offset = 0; offset < cols/2; offset += b) {
+
+            stride = (offset + b < cols/2) ? b : cols/2-offset;
+
+            hp = matrix + hn*cols/2;
+            memcpy(readbuf, hp+offset, stride*(sizeof *readbuf));
+            pointerswap(&readbuf, &writebuf);
+
+            next = mulmod_size_t(hn, r, m);
+            hp = matrix + next*cols/2;
+
+            while (next != hn) {
+
+                memcpy(readbuf, hp+offset, stride*(sizeof *readbuf));
+                memcpy(hp+offset, writebuf, stride*(sizeof *writebuf));
+                pointerswap(&readbuf, &writebuf);
+
+                done[next/dbits] |= mpd_bits[next%dbits];
+
+                next = mulmod_size_t(next, r, m);
+                hp = matrix + next*cols/2;
+
+            }
+
+            memcpy(hp+offset, writebuf, stride*(sizeof *writebuf));
+
+            done[hn/dbits] |= mpd_bits[hn%dbits];
+        }
+    }
+
+    mpd_free(done);
+    return 1;
+}
+
+/* In-place transpose of a square matrix */
+static inline void
+squaretrans(mpd_uint_t *buf, mpd_size_t cols)
+{
+    mpd_uint_t tmp;
+    mpd_size_t idest, isrc;
+    mpd_size_t r, c;
+
+    for (r = 0; r < cols; r++) {
+        c = r+1;
+        isrc = r*cols + c;
+        idest = c*cols + r;
+        for (c = r+1; c < cols; c++) {
+            tmp = buf[isrc];
+            buf[isrc] = buf[idest];
+            buf[idest] = tmp;
+            isrc += 1;
+            idest += cols;
+        }
+    }
+}
+
+/*
+ * Transpose 2^n * 2^n matrix. For cache efficiency, the matrix is split into
+ * square blocks with side length 'SIDE'. First, the blocks are transposed,
+ * then a square transposition is done on each individual block.
+ */
+static void
+squaretrans_pow2(mpd_uint_t *matrix, mpd_size_t size)
+{
+    mpd_uint_t buf1[SIDE*SIDE];
+    mpd_uint_t buf2[SIDE*SIDE];
+    mpd_uint_t *to, *from;
+    mpd_size_t b = size;
+    mpd_size_t r, c;
+    mpd_size_t i;
+
+    while (b > SIDE) b >>= 1;
+
+    for (r = 0; r < size; r += b) {
+
+        for (c = r; c < size; c += b) {
+
+            from = matrix + r*size + c;
+            to = buf1;
+            for (i = 0; i < b; i++) {
+                memcpy(to, from, b*(sizeof *to));
+                from += size;
+                to += b;
+            }
+            squaretrans(buf1, b);
+
+            if (r == c) {
+                to = matrix + r*size + c;
+                from = buf1;
+                for (i = 0; i < b; i++) {
+                    memcpy(to, from, b*(sizeof *to));
+                    from += b;
+                    to += size;
+                }
+                continue;
+            }
+            else {
+                from = matrix + c*size + r;
+                to = buf2;
+                for (i = 0; i < b; i++) {
+                    memcpy(to, from, b*(sizeof *to));
+                    from += size;
+                    to += b;
+                }
+                squaretrans(buf2, b);
+
+                to = matrix + c*size + r;
+                from = buf1;
+                for (i = 0; i < b; i++) {
+                    memcpy(to, from, b*(sizeof *to));
+                    from += b;
+                    to += size;
+                }
+
+                to = matrix + r*size + c;
+                from = buf2;
+                for (i = 0; i < b; i++) {
+                    memcpy(to, from, b*(sizeof *to));
+                    from += b;
+                    to += size;
+                }
+            }
+        }
+    }
+
+}
+
+/*
+ * In-place transposition of a 2^n x 2^n or a 2^n x (2*2^n)
+ * or a (2*2^n) x 2^n matrix.
+ */
+int
+transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols)
+{
+    mpd_size_t size = mul_size_t(rows, cols);
+
+    assert(ispower2(rows));
+    assert(ispower2(cols));
+
+    if (cols == rows) {
+        squaretrans_pow2(matrix, rows);
+    }
+    else if (cols == mul_size_t(2, rows)) {
+        if (!swap_halfrows_pow2(matrix, rows, cols, FORWARD_CYCLE)) {
+            return 0;
+        }
+        squaretrans_pow2(matrix, rows);
+        squaretrans_pow2(matrix+(size/2), rows);
+    }
+    else if (rows == mul_size_t(2, cols)) {
+        squaretrans_pow2(matrix, cols);
+        squaretrans_pow2(matrix+(size/2), cols);
+        if (!swap_halfrows_pow2(matrix, cols, rows, BACKWARD_CYCLE)) {
+            return 0;
+        }
+    }
+    else {
+        abort(); /* GCOV_NOT_REACHED */
+    }
+
+    return 1;
+}
diff --git a/libmpdec/transpose.h b/libmpdec/transpose.h
new file mode 100644 (file)
index 0000000..6f5f3fd
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_TRANSPOSE_H_
+#define LIBMPDEC_TRANSPOSE_H_
+
+
+#include "mpdecimal.h"
+
+
+/* Internal header file: all symbols have local scope in the DSO */
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_START)
+
+
+enum {FORWARD_CYCLE, BACKWARD_CYCLE};
+
+
+void std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols);
+int transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols);
+void transpose_3xpow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols);
+
+
+static inline void pointerswap(mpd_uint_t **a, mpd_uint_t **b)
+{
+    mpd_uint_t *tmp;
+
+    tmp = *b;
+    *b = *a;
+    *a = tmp;
+}
+
+
+MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */
+
+
+#endif /* LIBMPDEC_TRANSPOSE_H_ */
diff --git a/libmpdec/typearith.h b/libmpdec/typearith.h
new file mode 100644 (file)
index 0000000..a1b16fa
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_TYPEARITH_H_
+#define LIBMPDEC_TYPEARITH_H_
+
+
+#include <assert.h>
+
+#include "mpdecimal.h"
+
+
+/*****************************************************************************/
+/*                 Low level native arithmetic on basic types                */
+/*****************************************************************************/
+
+/** ------------------------------------------------------------
+ **           Double width multiplication and division
+ ** ------------------------------------------------------------
+ */
+
+#if defined(CONFIG_64)
+#if defined(ANSI)
+#if defined(HAVE_UINT128_T)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    __uint128_t hl;
+
+    hl = (__uint128_t)a * b;
+
+    *hi = hl >> 64;
+    *lo = (mpd_uint_t)hl;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+               mpd_uint_t d)
+{
+    __uint128_t hl;
+
+    hl = ((__uint128_t)hi<<64) + lo;
+    *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */
+    *r = (mpd_uint_t)(hl - (__uint128_t)(*q) * d);
+}
+#else
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    uint32_t w[4], carry;
+    uint32_t ah, al, bh, bl;
+    uint64_t hl;
+
+    ah = (uint32_t)(a>>32); al = (uint32_t)a;
+    bh = (uint32_t)(b>>32); bl = (uint32_t)b;
+
+    hl = (uint64_t)al * bl;
+    w[0] = (uint32_t)hl;
+    carry = (uint32_t)(hl>>32);
+
+    hl = (uint64_t)ah * bl + carry;
+    w[1] = (uint32_t)hl;
+    w[2] = (uint32_t)(hl>>32);
+
+    hl = (uint64_t)al * bh + w[1];
+    w[1] = (uint32_t)hl;
+    carry = (uint32_t)(hl>>32);
+
+    hl = ((uint64_t)ah * bh + w[2]) + carry;
+    w[2] = (uint32_t)hl;
+    w[3] = (uint32_t)(hl>>32);
+
+    *hi = ((uint64_t)w[3]<<32) + w[2];
+    *lo = ((uint64_t)w[1]<<32) + w[0];
+}
+
+/*
+ * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt
+ * http://www.hackersdelight.org/permissions.htm:
+ * "You are free to use, copy, and distribute any of the code on this web
+ *  site, whether modified by you or not. You need not give attribution."
+ *
+ * Slightly modified, comments are mine.
+ */
+static inline int
+nlz(uint64_t x)
+{
+    int n;
+
+    if (x == 0) return(64);
+
+    n = 0;
+    if (x <= 0x00000000FFFFFFFF) {n = n +32; x = x <<32;}
+    if (x <= 0x0000FFFFFFFFFFFF) {n = n +16; x = x <<16;}
+    if (x <= 0x00FFFFFFFFFFFFFF) {n = n + 8; x = x << 8;}
+    if (x <= 0x0FFFFFFFFFFFFFFF) {n = n + 4; x = x << 4;}
+    if (x <= 0x3FFFFFFFFFFFFFFF) {n = n + 2; x = x << 2;}
+    if (x <= 0x7FFFFFFFFFFFFFFF) {n = n + 1;}
+
+    return n;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0,
+               mpd_uint_t v)
+{
+    const mpd_uint_t b = 4294967296;
+    mpd_uint_t un1, un0,
+               vn1, vn0,
+               q1, q0,
+               un32, un21, un10,
+               rhat, t;
+    int s;
+
+    assert(u1 < v);
+
+    s = nlz(v);
+    v = v << s;
+    vn1 = v >> 32;
+    vn0 = v & 0xFFFFFFFF;
+
+    t = (s == 0) ? 0 : u0 >> (64 - s);
+    un32 = (u1 << s) | t;
+    un10 = u0 << s;
+
+    un1 = un10 >> 32;
+    un0 = un10 & 0xFFFFFFFF;
+
+    q1 = un32 / vn1;
+    rhat = un32 - q1*vn1;
+again1:
+    if (q1 >= b || q1*vn0 > b*rhat + un1) {
+        q1 = q1 - 1;
+        rhat = rhat + vn1;
+        if (rhat < b) goto again1;
+    }
+
+    /*
+     *  Before again1 we had:
+     *      (1) q1*vn1   + rhat         = un32
+     *      (2) q1*vn1*b + rhat*b + un1 = un32*b + un1
+     *
+     *  The statements inside the if-clause do not change the value
+     *  of the left-hand side of (2), and the loop is only exited
+     *  if q1*vn0 <= rhat*b + un1, so:
+     *
+     *      (3) q1*vn1*b + q1*vn0 <= un32*b + un1
+     *      (4)              q1*v <= un32*b + un1
+     *      (5)                 0 <= un32*b + un1 - q1*v
+     *
+     *  By (5) we are certain that the possible add-back step from
+     *  Knuth's algorithm D is never required.
+     *
+     *  Since the final quotient is less than 2**64, the following
+     *  must be true:
+     *
+     *      (6) un32*b + un1 - q1*v <= UINT64_MAX
+     *
+     *  This means that in the following line, the high words
+     *  of un32*b and q1*v can be discarded without any effect
+     *  on the result.
+     */
+    un21 = un32*b + un1 - q1*v;
+
+    q0 = un21 / vn1;
+    rhat = un21 - q0*vn1;
+again2:
+    if (q0 >= b || q0*vn0 > b*rhat + un0) {
+        q0 = q0 - 1;
+        rhat = rhat + vn1;
+        if (rhat < b) goto again2;
+    }
+
+    *q = q1*b + q0;
+    *r = (un21*b + un0 - q0*v) >> s;
+}
+#endif
+
+/* END ANSI */
+#elif defined(ASM)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    mpd_uint_t h, l;
+
+    __asm__ ( "mulq %3\n\t"
+              : "=d" (h), "=a" (l)
+              : "%a" (a), "rm" (b)
+              : "cc"
+    );
+
+    *hi = h;
+    *lo = l;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+               mpd_uint_t d)
+{
+    mpd_uint_t qq, rr;
+
+    __asm__ ( "divq %4\n\t"
+              : "=a" (qq), "=d" (rr)
+              : "a" (lo), "d" (hi), "rm" (d)
+              : "cc"
+    );
+
+    *q = qq;
+    *r = rr;
+}
+/* END GCC ASM */
+#elif defined(MASM)
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    *lo = _umul128(a, b, hi);
+}
+
+void _mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+                    mpd_uint_t d);
+
+/* END MASM (_MSC_VER) */
+#else
+  #error "need platform specific 128 bit multiplication and division"
+#endif
+
+#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d
+static inline void
+_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp)
+{
+    assert(exp <= 19);
+
+    if (exp <= 9) {
+        if (exp <= 4) {
+            switch (exp) {
+            case 0: *q = v; *r = 0; break;
+            case 1: DIVMOD(q, r, v, 10UL); break;
+            case 2: DIVMOD(q, r, v, 100UL); break;
+            case 3: DIVMOD(q, r, v, 1000UL); break;
+            case 4: DIVMOD(q, r, v, 10000UL); break;
+            }
+        }
+        else {
+            switch (exp) {
+            case 5: DIVMOD(q, r, v, 100000UL); break;
+            case 6: DIVMOD(q, r, v, 1000000UL); break;
+            case 7: DIVMOD(q, r, v, 10000000UL); break;
+            case 8: DIVMOD(q, r, v, 100000000UL); break;
+            case 9: DIVMOD(q, r, v, 1000000000UL); break;
+            }
+        }
+    }
+    else {
+        if (exp <= 14) {
+            switch (exp) {
+            case 10: DIVMOD(q, r, v, 10000000000ULL); break;
+            case 11: DIVMOD(q, r, v, 100000000000ULL); break;
+            case 12: DIVMOD(q, r, v, 1000000000000ULL); break;
+            case 13: DIVMOD(q, r, v, 10000000000000ULL); break;
+            case 14: DIVMOD(q, r, v, 100000000000000ULL); break;
+            }
+        }
+        else {
+            switch (exp) {
+            case 15: DIVMOD(q, r, v, 1000000000000000ULL); break;
+            case 16: DIVMOD(q, r, v, 10000000000000000ULL); break;
+            case 17: DIVMOD(q, r, v, 100000000000000000ULL); break;
+            case 18: DIVMOD(q, r, v, 1000000000000000000ULL); break;
+            case 19: DIVMOD(q, r, v, 10000000000000000000ULL); break; /* GCOV_NOT_REACHED */
+            }
+        }
+    }
+}
+
+/* END CONFIG_64 */
+#elif defined(CONFIG_32)
+#if defined(ANSI)
+#if !defined(LEGACY_COMPILER)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    mpd_uuint_t hl;
+
+    hl = (mpd_uuint_t)a * b;
+
+    *hi = hl >> 32;
+    *lo = (mpd_uint_t)hl;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+               mpd_uint_t d)
+{
+    mpd_uuint_t hl;
+
+    hl = ((mpd_uuint_t)hi<<32) + lo;
+    *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */
+    *r = (mpd_uint_t)(hl - (mpd_uuint_t)(*q) * d);
+}
+/* END ANSI + uint64_t */
+#else
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    uint16_t w[4], carry;
+    uint16_t ah, al, bh, bl;
+    uint32_t hl;
+
+    ah = (uint16_t)(a>>16); al = (uint16_t)a;
+    bh = (uint16_t)(b>>16); bl = (uint16_t)b;
+
+    hl = (uint32_t)al * bl;
+    w[0] = (uint16_t)hl;
+    carry = (uint16_t)(hl>>16);
+
+    hl = (uint32_t)ah * bl + carry;
+    w[1] = (uint16_t)hl;
+    w[2] = (uint16_t)(hl>>16);
+
+    hl = (uint32_t)al * bh + w[1];
+    w[1] = (uint16_t)hl;
+    carry = (uint16_t)(hl>>16);
+
+    hl = ((uint32_t)ah * bh + w[2]) + carry;
+    w[2] = (uint16_t)hl;
+    w[3] = (uint16_t)(hl>>16);
+
+    *hi = ((uint32_t)w[3]<<16) + w[2];
+    *lo = ((uint32_t)w[1]<<16) + w[0];
+}
+
+/*
+ * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt
+ * http://www.hackersdelight.org/permissions.htm:
+ * "You are free to use, copy, and distribute any of the code on this web
+ *  site, whether modified by you or not. You need not give attribution."
+ *
+ * Slightly modified, comments are mine.
+ */
+static inline int
+nlz(uint32_t x)
+{
+    int n;
+
+    if (x == 0) return(32);
+
+    n = 0;
+    if (x <= 0x0000FFFF) {n = n +16; x = x <<16;}
+    if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;}
+    if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;}
+    if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;}
+    if (x <= 0x7FFFFFFF) {n = n + 1;}
+
+    return n;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0,
+               mpd_uint_t v)
+{
+    const mpd_uint_t b = 65536;
+    mpd_uint_t un1, un0,
+               vn1, vn0,
+               q1, q0,
+               un32, un21, un10,
+               rhat, t;
+    int s;
+
+    assert(u1 < v);
+
+    s = nlz(v);
+    v = v << s;
+    vn1 = v >> 16;
+    vn0 = v & 0xFFFF;
+
+    t = (s == 0) ? 0 : u0 >> (32 - s);
+    un32 = (u1 << s) | t;
+    un10 = u0 << s;
+
+    un1 = un10 >> 16;
+    un0 = un10 & 0xFFFF;
+
+    q1 = un32 / vn1;
+    rhat = un32 - q1*vn1;
+again1:
+    if (q1 >= b || q1*vn0 > b*rhat + un1) {
+        q1 = q1 - 1;
+        rhat = rhat + vn1;
+        if (rhat < b) goto again1;
+    }
+
+    /*
+     *  Before again1 we had:
+     *      (1) q1*vn1   + rhat         = un32
+     *      (2) q1*vn1*b + rhat*b + un1 = un32*b + un1
+     *
+     *  The statements inside the if-clause do not change the value
+     *  of the left-hand side of (2), and the loop is only exited
+     *  if q1*vn0 <= rhat*b + un1, so:
+     *
+     *      (3) q1*vn1*b + q1*vn0 <= un32*b + un1
+     *      (4)              q1*v <= un32*b + un1
+     *      (5)                 0 <= un32*b + un1 - q1*v
+     *
+     *  By (5) we are certain that the possible add-back step from
+     *  Knuth's algorithm D is never required.
+     *
+     *  Since the final quotient is less than 2**32, the following
+     *  must be true:
+     *
+     *      (6) un32*b + un1 - q1*v <= UINT32_MAX
+     *
+     *  This means that in the following line, the high words
+     *  of un32*b and q1*v can be discarded without any effect
+     *  on the result.
+     */
+    un21 = un32*b + un1 - q1*v;
+
+    q0 = un21 / vn1;
+    rhat = un21 - q0*vn1;
+again2:
+    if (q0 >= b || q0*vn0 > b*rhat + un0) {
+        q0 = q0 - 1;
+        rhat = rhat + vn1;
+        if (rhat < b) goto again2;
+    }
+
+    *q = q1*b + q0;
+    *r = (un21*b + un0 - q0*v) >> s;
+}
+#endif /* END ANSI + LEGACY_COMPILER */
+
+/* END ANSI */
+#elif defined(ASM)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    mpd_uint_t h, l;
+
+    __asm__ ( "mull %3\n\t"
+              : "=d" (h), "=a" (l)
+              : "%a" (a), "rm" (b)
+              : "cc"
+    );
+
+    *hi = h;
+    *lo = l;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+               mpd_uint_t d)
+{
+    mpd_uint_t qq, rr;
+
+    __asm__ ( "divl %4\n\t"
+              : "=a" (qq), "=d" (rr)
+              : "a" (lo), "d" (hi), "rm" (d)
+              : "cc"
+    );
+
+    *q = qq;
+    *r = rr;
+}
+/* END GCC ASM */
+#elif defined(MASM)
+static inline void __cdecl
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+    mpd_uint_t h, l;
+
+    __asm {
+        mov eax, a
+        mul b
+        mov h, edx
+        mov l, eax
+    }
+
+    *hi = h;
+    *lo = l;
+}
+
+static inline void __cdecl
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+               mpd_uint_t d)
+{
+    mpd_uint_t qq, rr;
+
+    __asm {
+        mov eax, lo
+        mov edx, hi
+        div d
+        mov qq, eax
+        mov rr, edx
+    }
+
+    *q = qq;
+    *r = rr;
+}
+/* END MASM (_MSC_VER) */
+#else
+  #error "need platform specific 64 bit multiplication and division"
+#endif
+
+#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d
+static inline void
+_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp)
+{
+    assert(exp <= 9);
+
+    if (exp <= 4) {
+        switch (exp) {
+        case 0: *q = v; *r = 0; break;
+        case 1: DIVMOD(q, r, v, 10UL); break;
+        case 2: DIVMOD(q, r, v, 100UL); break;
+        case 3: DIVMOD(q, r, v, 1000UL); break;
+        case 4: DIVMOD(q, r, v, 10000UL); break;
+        }
+    }
+    else {
+        switch (exp) {
+        case 5: DIVMOD(q, r, v, 100000UL); break;
+        case 6: DIVMOD(q, r, v, 1000000UL); break;
+        case 7: DIVMOD(q, r, v, 10000000UL); break;
+        case 8: DIVMOD(q, r, v, 100000000UL); break;
+        case 9: DIVMOD(q, r, v, 1000000000UL); break; /* GCOV_NOT_REACHED */
+        }
+    }
+}
+/* END CONFIG_32 */
+
+/* NO CONFIG */
+#else
+  #error "define CONFIG_64 or CONFIG_32"
+#endif /* CONFIG */
+static inline void
+_mpd_div_word(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t d)
+{
+    *q = v / d;
+    *r = v - *q * d;
+}
+
+static inline void
+_mpd_idiv_word(mpd_ssize_t *q, mpd_ssize_t *r, mpd_ssize_t v, mpd_ssize_t d)
+{
+    *q = v / d;
+    *r = v - *q * d;
+}
+
+/** ------------------------------------------------------------
+ **              Arithmetic with overflow checking
+ ** ------------------------------------------------------------
+ */
+
+/* The following macros do call exit() in case of an overflow.
+   If the library is used correctly (i.e. with valid context
+   parameters), such overflows cannot occur. The macros are used
+   as sanity checks in a couple of strategic places and should
+   be viewed as a handwritten version of gcc's -ftrapv option. */
+
+static inline mpd_size_t
+add_size_t(mpd_size_t a, mpd_size_t b)
+{
+    if (a > MPD_SIZE_MAX - b) {
+        mpd_err_fatal("add_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+    }
+    return a + b;
+}
+
+static inline mpd_size_t
+sub_size_t(mpd_size_t a, mpd_size_t b)
+{
+    if (b > a) {
+        mpd_err_fatal("sub_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+    }
+    return a - b;
+}
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+  #error "adapt mul_size_t() and mulmod_size_t()"
+#endif
+
+static inline mpd_size_t
+mul_size_t(mpd_size_t a, mpd_size_t b)
+{
+    mpd_uint_t hi, lo;
+
+    _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b);
+    if (hi) {
+        mpd_err_fatal("mul_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+    }
+    return lo;
+}
+
+static inline mpd_size_t
+add_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow)
+{
+    mpd_size_t ret;
+
+    *overflow = 0;
+    ret = a + b;
+    if (ret < a) *overflow = 1;
+    return ret;
+}
+
+static inline mpd_size_t
+mul_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow)
+{
+    mpd_uint_t hi, lo;
+
+    _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b);
+    *overflow = (mpd_size_t)hi;
+    return lo;
+}
+
+static inline mpd_ssize_t
+mod_mpd_ssize_t(mpd_ssize_t a, mpd_ssize_t m)
+{
+    mpd_ssize_t r = a % m;
+    return (r < 0) ? r + m : r;
+}
+
+static inline mpd_size_t
+mulmod_size_t(mpd_size_t a, mpd_size_t b, mpd_size_t m)
+{
+    mpd_uint_t hi, lo;
+    mpd_uint_t q, r;
+
+    _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b);
+    _mpd_div_words(&q, &r, hi, lo, (mpd_uint_t)m);
+
+    return r;
+}
+#endif /* LIBMPDEC_TYPEARITH_H_ */
diff --git a/libmpdec/umodarith.h b/libmpdec/umodarith.h
new file mode 100644 (file)
index 0000000..e66faf1
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_UMODARITH_H_
+#define LIBMPDEC_UMODARITH_H_
+
+
+#include "constants.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+/* Bignum: Low level routines for unsigned modular arithmetic. These are
+   used in the fast convolution functions for very large coefficients. */
+
+
+/**************************************************************************/
+/*                        ANSI modular arithmetic                         */
+/**************************************************************************/
+
+/*
+ * Restrictions: a < m and b < m
+ * ACL2 proof: umodarith.lisp: addmod-correct
+ */
+static inline mpd_uint_t
+addmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    mpd_uint_t s;
+
+    s = a + b;
+    s = (s < a) ? s - m : s;
+    s = (s >= m) ? s - m : s;
+
+    return s;
+}
+
+/*
+ * Restrictions: a < m and b < m
+ * ACL2 proof: umodarith.lisp: submod-2-correct
+ */
+static inline mpd_uint_t
+submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    mpd_uint_t d;
+
+    d = a - b;
+    d = (a < b) ? d + m : d;
+
+    return d;
+}
+
+/*
+ * Restrictions: a < 2m and b < 2m
+ * ACL2 proof: umodarith.lisp: section ext-submod
+ */
+static inline mpd_uint_t
+ext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    mpd_uint_t d;
+
+    a = (a >= m) ? a - m : a;
+    b = (b >= m) ? b - m : b;
+
+    d = a - b;
+    d = (a < b) ? d + m : d;
+
+    return d;
+}
+
+/*
+ * Reduce double word modulo m.
+ * Restrictions: m != 0
+ * ACL2 proof: umodarith.lisp: section dw-reduce
+ */
+static inline mpd_uint_t
+dw_reduce(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
+{
+    mpd_uint_t r1, r2, w;
+
+    _mpd_div_word(&w, &r1, hi, m);
+    _mpd_div_words(&w, &r2, r1, lo, m);
+
+    return r2;
+}
+
+/*
+ * Subtract double word from a.
+ * Restrictions: a < m
+ * ACL2 proof: umodarith.lisp: section dw-submod
+ */
+static inline mpd_uint_t
+dw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
+{
+    mpd_uint_t d, r;
+
+    r = dw_reduce(hi, lo, m);
+    d = a - r;
+    d = (a < r) ? d + m : d;
+
+    return d;
+}
+
+#ifdef CONFIG_64
+
+/**************************************************************************/
+/*                        64-bit modular arithmetic                       */
+/**************************************************************************/
+
+/*
+ * A proof of the algorithm is in literature/mulmod-64.txt. An ACL2
+ * proof is in umodarith.lisp: section "Fast modular reduction".
+ *
+ * Algorithm: calculate (a * b) % p:
+ *
+ *   a) hi, lo <- a * b       # Calculate a * b.
+ *
+ *   b) hi, lo <-  R(hi, lo)  # Reduce modulo p.
+ *
+ *   c) Repeat step b) until 0 <= hi * 2**64 + lo < 2*p.
+ *
+ *   d) If the result is less than p, return lo. Otherwise return lo - p.
+ */
+
+static inline mpd_uint_t
+x64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    mpd_uint_t hi, lo, x, y;
+
+
+    _mpd_mul_words(&hi, &lo, a, b);
+
+    if (m & (1ULL<<32)) { /* P1 */
+
+        /* first reduction */
+        x = y = hi;
+        hi >>= 32;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 32;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        /* second reduction */
+        x = y = hi;
+        hi >>= 32;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 32;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        return (hi || lo >= m ? lo - m : lo);
+    }
+    else if (m & (1ULL<<34)) { /* P2 */
+
+        /* first reduction */
+        x = y = hi;
+        hi >>= 30;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 34;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        /* second reduction */
+        x = y = hi;
+        hi >>= 30;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 34;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        /* third reduction */
+        x = y = hi;
+        hi >>= 30;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 34;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        return (hi || lo >= m ? lo - m : lo);
+    }
+    else { /* P3 */
+
+        /* first reduction */
+        x = y = hi;
+        hi >>= 24;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 40;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        /* second reduction */
+        x = y = hi;
+        hi >>= 24;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 40;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        /* third reduction */
+        x = y = hi;
+        hi >>= 24;
+
+        x = lo - x;
+        if (x > lo) hi--;
+
+        y <<= 40;
+        lo = y + x;
+        if (lo < y) hi++;
+
+        return (hi || lo >= m ? lo - m : lo);
+    }
+}
+
+static inline void
+x64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+    *a = x64_mulmod(*a, w, m);
+    *b = x64_mulmod(*b, w, m);
+}
+
+static inline void
+x64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+            mpd_uint_t m)
+{
+    *a0 = x64_mulmod(*a0, b0, m);
+    *a1 = x64_mulmod(*a1, b1, m);
+}
+
+static inline mpd_uint_t
+x64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
+{
+    mpd_uint_t r = 1;
+
+    while (exp > 0) {
+        if (exp & 1)
+            r = x64_mulmod(r, base, umod);
+        base = x64_mulmod(base, base, umod);
+        exp >>= 1;
+    }
+
+    return r;
+}
+
+/* END CONFIG_64 */
+#else /* CONFIG_32 */
+
+
+/**************************************************************************/
+/*                        32-bit modular arithmetic                       */
+/**************************************************************************/
+
+#if defined(ANSI)
+#if !defined(LEGACY_COMPILER)
+/* HAVE_UINT64_T */
+static inline mpd_uint_t
+std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    return ((mpd_uuint_t) a * b) % m;
+}
+
+static inline void
+std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+    *a = ((mpd_uuint_t) *a * w) % m;
+    *b = ((mpd_uuint_t) *b * w) % m;
+}
+
+static inline void
+std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+            mpd_uint_t m)
+{
+    *a0 = ((mpd_uuint_t) *a0 * b0) % m;
+    *a1 = ((mpd_uuint_t) *a1 * b1) % m;
+}
+/* END HAVE_UINT64_T */
+#else
+/* LEGACY_COMPILER */
+static inline mpd_uint_t
+std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+    mpd_uint_t hi, lo, q, r;
+    _mpd_mul_words(&hi, &lo, a, b);
+    _mpd_div_words(&q, &r, hi, lo, m);
+    return r;
+}
+
+static inline void
+std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+    *a = std_mulmod(*a, w, m);
+    *b = std_mulmod(*b, w, m);
+}
+
+static inline void
+std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+            mpd_uint_t m)
+{
+    *a0 = std_mulmod(*a0, b0, m);
+    *a1 = std_mulmod(*a1, b1, m);
+}
+/* END LEGACY_COMPILER */
+#endif
+
+static inline mpd_uint_t
+std_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
+{
+    mpd_uint_t r = 1;
+
+    while (exp > 0) {
+        if (exp & 1)
+            r = std_mulmod(r, base, umod);
+        base = std_mulmod(base, base, umod);
+        exp >>= 1;
+    }
+
+    return r;
+}
+#endif /* ANSI CONFIG_32 */
+
+
+/**************************************************************************/
+/*                    Pentium Pro modular arithmetic                      */
+/**************************************************************************/
+
+/*
+ * A proof of the algorithm is in literature/mulmod-ppro.txt. The FPU
+ * control word must be set to 64-bit precision and truncation mode
+ * prior to using these functions.
+ *
+ * Algorithm: calculate (a * b) % p:
+ *
+ *   p    := prime < 2**31
+ *   pinv := (long double)1.0 / p (precalculated)
+ *
+ *   a) n = a * b              # Calculate exact product.
+ *   b) qest = n * pinv        # Calculate estimate for q = n / p.
+ *   c) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient.
+ *   d) r = n - q * p          # Calculate remainder.
+ *
+ * Remarks:
+ *
+ *   - p = dmod and pinv = dinvmod.
+ *   - dinvmod points to an array of three uint32_t, which is interpreted
+ *     as an 80 bit long double by fldt.
+ *   - Intel compilers prior to version 11 do not seem to handle the
+ *     __GNUC__ inline assembly correctly.
+ *   - random tests are provided in tests/extended/ppro_mulmod.c
+ */
+
+#if defined(PPRO)
+#if defined(ASM)
+
+/* Return (a * b) % dmod */
+static inline mpd_uint_t
+ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
+{
+    mpd_uint_t retval;
+
+    __asm__ (
+            "fildl  %2\n\t"
+            "fildl  %1\n\t"
+            "fmulp  %%st, %%st(1)\n\t"
+            "fldt   (%4)\n\t"
+            "fmul   %%st(1), %%st\n\t"
+            "flds   %5\n\t"
+            "fadd   %%st, %%st(1)\n\t"
+            "fsubrp %%st, %%st(1)\n\t"
+            "fldl   (%3)\n\t"
+            "fmulp  %%st, %%st(1)\n\t"
+            "fsubrp %%st, %%st(1)\n\t"
+            "fistpl %0\n\t"
+            : "=m" (retval)
+            : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63)
+            : "st", "memory"
+    );
+
+    return retval;
+}
+
+/*
+ * Two modular multiplications in parallel:
+ *      *a0 = (*a0 * w) % dmod
+ *      *a1 = (*a1 * w) % dmod
+ */
+static inline void
+ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
+              double *dmod, uint32_t *dinvmod)
+{
+    __asm__ (
+            "fildl  %2\n\t"
+            "fildl  (%1)\n\t"
+            "fmul   %%st(1), %%st\n\t"
+            "fxch   %%st(1)\n\t"
+            "fildl  (%0)\n\t"
+            "fmulp  %%st, %%st(1) \n\t"
+            "fldt   (%4)\n\t"
+            "flds   %5\n\t"
+            "fld    %%st(2)\n\t"
+            "fmul   %%st(2)\n\t"
+            "fadd   %%st(1)\n\t"
+            "fsub   %%st(1)\n\t"
+            "fmull  (%3)\n\t"
+            "fsubrp %%st, %%st(3)\n\t"
+            "fxch   %%st(2)\n\t"
+            "fistpl (%0)\n\t"
+            "fmul   %%st(2)\n\t"
+            "fadd   %%st(1)\n\t"
+            "fsubp  %%st, %%st(1)\n\t"
+            "fmull  (%3)\n\t"
+            "fsubrp %%st, %%st(1)\n\t"
+            "fistpl (%1)\n\t"
+            : : "r" (a0), "r" (a1), "m" (w),
+                "r" (dmod), "r" (dinvmod),
+                "m" (MPD_TWO63)
+            : "st", "memory"
+    );
+}
+
+/*
+ * Two modular multiplications in parallel:
+ *      *a0 = (*a0 * b0) % dmod
+ *      *a1 = (*a1 * b1) % dmod
+ */
+static inline void
+ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+             double *dmod, uint32_t *dinvmod)
+{
+    __asm__ (
+            "fildl  %3\n\t"
+            "fildl  (%2)\n\t"
+            "fmulp  %%st, %%st(1)\n\t"
+            "fildl  %1\n\t"
+            "fildl  (%0)\n\t"
+            "fmulp  %%st, %%st(1)\n\t"
+            "fldt   (%5)\n\t"
+            "fld    %%st(2)\n\t"
+            "fmul   %%st(1), %%st\n\t"
+            "fxch   %%st(1)\n\t"
+            "fmul   %%st(2), %%st\n\t"
+            "flds   %6\n\t"
+            "fldl   (%4)\n\t"
+            "fxch   %%st(3)\n\t"
+            "fadd   %%st(1), %%st\n\t"
+            "fxch   %%st(2)\n\t"
+            "fadd   %%st(1), %%st\n\t"
+            "fxch   %%st(2)\n\t"
+            "fsub   %%st(1), %%st\n\t"
+            "fxch   %%st(2)\n\t"
+            "fsubp  %%st, %%st(1)\n\t"
+            "fxch   %%st(1)\n\t"
+            "fmul   %%st(2), %%st\n\t"
+            "fxch   %%st(1)\n\t"
+            "fmulp  %%st, %%st(2)\n\t"
+            "fsubrp %%st, %%st(3)\n\t"
+            "fsubrp %%st, %%st(1)\n\t"
+            "fxch   %%st(1)\n\t"
+            "fistpl (%2)\n\t"
+            "fistpl (%0)\n\t"
+            : : "r" (a0), "m" (b0), "r" (a1), "m" (b1),
+                "r" (dmod), "r" (dinvmod),
+                "m" (MPD_TWO63)
+            : "st", "memory"
+    );
+}
+/* END PPRO GCC ASM */
+#elif defined(MASM)
+
+/* Return (a * b) % dmod */
+static inline mpd_uint_t __cdecl
+ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
+{
+    mpd_uint_t retval;
+
+    __asm {
+        mov     eax, dinvmod
+        mov     edx, dmod
+        fild    b
+        fild    a
+        fmulp   st(1), st
+        fld     TBYTE PTR [eax]
+        fmul    st, st(1)
+        fld     MPD_TWO63
+        fadd    st(1), st
+        fsubp   st(1), st
+        fld     QWORD PTR [edx]
+        fmulp   st(1), st
+        fsubp   st(1), st
+        fistp   retval
+    }
+
+    return retval;
+}
+
+/*
+ * Two modular multiplications in parallel:
+ *      *a0 = (*a0 * w) % dmod
+ *      *a1 = (*a1 * w) % dmod
+ */
+static inline mpd_uint_t __cdecl
+ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
+              double *dmod, uint32_t *dinvmod)
+{
+    __asm {
+        mov     ecx, dmod
+        mov     edx, a1
+        mov     ebx, dinvmod
+        mov     eax, a0
+        fild    w
+        fild    DWORD PTR [edx]
+        fmul    st, st(1)
+        fxch    st(1)
+        fild    DWORD PTR [eax]
+        fmulp   st(1), st
+        fld     TBYTE PTR [ebx]
+        fld     MPD_TWO63
+        fld     st(2)
+        fmul    st, st(2)
+        fadd    st, st(1)
+        fsub    st, st(1)
+        fmul    QWORD PTR [ecx]
+        fsubp   st(3), st
+        fxch    st(2)
+        fistp   DWORD PTR [eax]
+        fmul    st, st(2)
+        fadd    st, st(1)
+        fsubrp  st(1), st
+        fmul    QWORD PTR [ecx]
+        fsubp   st(1), st
+        fistp   DWORD PTR [edx]
+    }
+}
+
+/*
+ * Two modular multiplications in parallel:
+ *      *a0 = (*a0 * b0) % dmod
+ *      *a1 = (*a1 * b1) % dmod
+ */
+static inline void __cdecl
+ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+             double *dmod, uint32_t *dinvmod)
+{
+    __asm {
+        mov     ecx, dmod
+        mov     edx, a1
+        mov     ebx, dinvmod
+        mov     eax, a0
+        fild    b1
+        fild    DWORD PTR [edx]
+        fmulp   st(1), st
+        fild    b0
+        fild    DWORD PTR [eax]
+        fmulp   st(1), st
+        fld     TBYTE PTR [ebx]
+        fld     st(2)
+        fmul    st, st(1)
+        fxch    st(1)
+        fmul    st, st(2)
+        fld     DWORD PTR MPD_TWO63
+        fld     QWORD PTR [ecx]
+        fxch    st(3)
+        fadd    st, st(1)
+        fxch    st(2)
+        fadd    st, st(1)
+        fxch    st(2)
+        fsub    st, st(1)
+        fxch    st(2)
+        fsubrp  st(1), st
+        fxch    st(1)
+        fmul    st, st(2)
+        fxch    st(1)
+        fmulp   st(2), st
+        fsubp   st(3), st
+        fsubp   st(1), st
+        fxch    st(1)
+        fistp   DWORD PTR [edx]
+        fistp   DWORD PTR [eax]
+    }
+}
+#endif /* PPRO MASM (_MSC_VER) */
+
+
+/* Return (base ** exp) % dmod */
+static inline mpd_uint_t
+ppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod)
+{
+    mpd_uint_t r = 1;
+
+    while (exp > 0) {
+        if (exp & 1)
+            r = ppro_mulmod(r, base, dmod, dinvmod);
+        base = ppro_mulmod(base, base, dmod, dinvmod);
+        exp >>= 1;
+    }
+
+    return r;
+}
+#endif /* PPRO */
+#endif /* CONFIG_32 */
+
+
+#endif /* LIBMPDEC_UMODARITH_H_ */
diff --git a/libmpdec/vcdiv64.asm b/libmpdec/vcdiv64.asm
new file mode 100644 (file)
index 0000000..aa94632
--- /dev/null
@@ -0,0 +1,47 @@
+;
+; Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+;
+; 1. Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in the
+;    documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+
+
+PUBLIC    _mpd_div_words
+_TEXT    SEGMENT
+q$ = 8
+r$ = 16
+hi$ = 24
+lo$ = 32
+d$ = 40
+_mpd_div_words PROC
+    mov    r10, rdx
+    mov    rdx, r8
+    mov    rax, r9
+    div    QWORD PTR d$[rsp]
+    mov    QWORD PTR [r10], rdx
+    mov    QWORD PTR [rcx], rax
+    ret    0
+_mpd_div_words ENDP
+_TEXT    ENDS
+END
+
+
diff --git a/tests++/Makefile.in b/tests++/Makefile.in
new file mode 100644 (file)
index 0000000..120fb5b
--- /dev/null
@@ -0,0 +1,90 @@
+
+# ==============================================================================
+#                       Unix Makefile for libmpdec++ tests
+# ==============================================================================
+
+SRCDIR = ../libmpdec
+
+ENABLE_STATIC = @ENABLE_STATIC@
+ENABLE_SHARED = @ENABLE_SHARED@
+
+LIBSTATIC = @LIBSTATIC@
+LIBSHARED = @LIBSHARED@
+LINK_STATIC = @LINK_STATIC@
+LINK_DYNAMIC = @LINK_DYNAMIC@
+
+SRCDIR_CXX = ../libmpdec++
+LIBSTATIC_CXX = @LIBSTATIC_CXX@
+LIBSHARED_CXX = @LIBSHARED_CXX@
+
+LIBSHARED_USE_AR = @LIBSHARED_USE_AR@
+
+CXX = @CXX@
+MPD_PTHREAD = @MPD_PTHREAD@
+MPD_CXX = $(strip $(CXX) $(MPD_PTHREAD))
+
+FILTER_FOR_STATIC = @FILTER_FOR_STATIC@
+
+CONFIGURE_CXXFLAGS = @CONFIGURE_CXXFLAGS@
+MPD_CXXFLAGS_SHARED = $(strip $(filter-out $(CXXFLAGS),$(CONFIGURE_CXXFLAGS)) $(CXXFLAGS))
+MPD_CXXFLAGS = $(strip $(filter-out $(FILTER_FOR_STATIC),$(MPD_CXXFLAGS_SHARED)))
+
+LINK_LIBSTATIC = $(strip $(LINK_STATIC) $(SRCDIR_CXX)/$(LIBSTATIC_CXX) $(SRCDIR)/$(LIBSTATIC) $(LINK_DYNAMIC))
+
+
+TEST_LIBS = $(SRCDIR)/$(LIBSTATIC) $(SRCDIR_CXX)/$(LIBSTATIC_CXX)
+ifeq ($(LIBSHARED_USE_AR), yes)
+TEST_SHLIBS = $(SRCDIR)/$(LIBSHARED) $(SRCDIR_CXX)/$(LIBSTATIC_CXX)
+else
+TEST_SHLIBS = $(SRCDIR)/$(LIBSHARED) $(SRCDIR_CXX)/$(LIBSHARED_CXX)
+endif
+
+
+MPD_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+MPD_TARGETS += runtest apitest
+endif
+
+ifeq ($(ENABLE_SHARED), yes)
+MPD_TARGETS += runtest_shared apitest_shared
+endif
+
+
+default: $(MPD_TARGETS)
+
+
+# Short test:
+RUNTEST_SOURCES = runtest.cc test.cc
+RUNTEST_HEADERS = test.hh vctest.hh ../config.h $(SRCDIR)/mpdecimal.h $(SRCDIR_CXX)/decimal.hh
+
+runtest:\
+Makefile $(RUNTEST_SOURCES) $(RUNTEST_HEADERS) $(TEST_LIBS)
+       $(MPD_CXX) -I.. -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS) -o runtest runtest.cc test.cc $(LINK_LIBSTATIC) -lm
+
+runtest_shared:\
+Makefile $(RUNTEST_SOURCES) $(RUNTEST_HEADERS) $(TEST_SHLIBS)
+       $(MPD_CXX) -I.. -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS_SHARED) -o runtest_shared runtest.cc test.cc -L$(SRCDIR) -L$(SRCDIR_CXX) -lmpdec++ -lmpdec -lm
+
+
+# API test:
+APITEST_SOURCES = apitest.cc test.cc
+APITEST_HEADERS = test.hh vctest.hh $(SRCDIR)/mpdecimal.h $(SRCDIR_CXX)/decimal.hh
+
+apitest:\
+Makefile $(APITEST_SOURCES) $(APITEST_HEADERS) $(TEST_LIBS)
+       $(MPD_CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS) -o apitest apitest.cc test.cc $(LINK_LIBSTATIC) -lm
+
+apitest_shared:\
+Makefile $(APITEST_SOURCES) $(APITEST_HEADERS) $(TEST_SHLIBS)
+       $(MPD_CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS_SHARED) -o apitest_shared apitest.cc test.cc -L$(SRCDIR) -L$(SRCDIR_CXX) -lmpdec++ -lmpdec -lm
+
+
+FORCE:
+
+clean: FORCE
+       rm -f *.o *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock
+       rm -f runtest runtest_shared apitest apitest_shared
+
+distclean: FORCE
+       $(MAKE) clean
+       rm -rf Makefile dectest.zip testdata
diff --git a/tests++/Makefile.vc b/tests++/Makefile.vc
new file mode 100644 (file)
index 0000000..a579b9e
--- /dev/null
@@ -0,0 +1,79 @@
+
+SRCDIR = ..\libmpdec
+LIBSTATIC = libmpdec-4.0.1.lib
+LIBSHARED = libmpdec-4.0.1.dll
+LIBIMPORT = libmpdec-4.0.1.dll.lib
+
+SRCDIR_CXX = ..\libmpdec++
+LIBSTATIC_CXX = libmpdec++-4.0.1.lib
+LIBSHARED_CXX = libmpdec++-4.0.1.dll
+LIBIMPORT_CXX = libmpdec++-4.0.1.dll.lib
+
+!if "$(DEBUG)" == "1"
+OPT = /MTd /Od /Zi /EHsc
+OPT_SHARED = /MDd /Od /Zi /EHsc
+!else
+OPT = /MT /O2 /GS /EHsc /DNDEBUG
+OPT_SHARED = /MD /O2 /GS /EHsc /DNDEBUG
+!endif
+
+!if "$(CC)" == "clang-cl"
+WARN = /W4 /wd4200 /wd4204 /wd4221 /wd4714 -Wno-undefined-inline /D_CRT_SECURE_NO_WARNINGS
+!else
+WARN = /W4 /wd4200 /wd4204 /wd4221 /wd4714 /D_CRT_SECURE_NO_WARNINGS
+!endif
+
+MPD_CXXFLAGS = $(WARN) /nologo $(OPT)
+MPD_CXXFLAGS_SHARED = $(WARN) /nologo $(OPT_SHARED)
+
+
+default: runtest runtest_shared apitest apitest_shared copy_dll
+
+
+runtest:\
+Makefile runtest.cc test.cc $(SRCDIR)\mpdecimal.h $(SRCDIR_CXX)\decimal.hh test.hh vctest.hh \
+$(SRCDIR)\$(LIBSTATIC) $(SRCDIR_CXX)\$(LIBSTATIC_CXX)
+       $(CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS) /Fe:runtest runtest.cc test.cc $(SRCDIR_CXX)\$(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+
+runtest_shared:\
+Makefile runtest.cc test.cc $(SRCDIR)\mpdecimal.h $(SRCDIR_CXX)\decimal.hh test.hh vctest.hh \
+$(SRCDIR_CXX)\$(LIBIMPORT_CXX) $(SRCDIR)\$(LIBIMPORT)
+       $(CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS_SHARED) /Fe:runtest_shared runtest.cc test.cc $(SRCDIR_CXX)\$(LIBIMPORT_CXX) $(SRCDIR)\$(LIBIMPORT)
+
+
+apitest:\
+Makefile apitest.cc test.cc $(SRCDIR)\mpdecimal.h $(SRCDIR_CXX)\decimal.hh test.hh vctest.hh \
+$(SRCDIR_CXX)\$(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+       $(CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS) /Fe:apitest apitest.cc test.cc $(SRCDIR_CXX)\$(LIBSTATIC_CXX) $(SRCDIR)\$(LIBSTATIC)
+
+apitest_shared:\
+Makefile apitest.cc test.cc $(SRCDIR)\mpdecimal.h $(SRCDIR_CXX)\decimal.hh test.hh vctest.hh \
+$(SRCDIR_CXX)\$(LIBIMPORT_CXX) $(SRCDIR)\$(LIBIMPORT)
+       $(CXX) -I$(SRCDIR) -I$(SRCDIR_CXX) $(MPD_CXXFLAGS_SHARED) /Fe:apitest_shared apitest.cc test.cc $(SRCDIR_CXX)\$(LIBIMPORT_CXX) $(SRCDIR)\$(LIBIMPORT)
+
+
+FORCE:
+
+copy_dll:
+       copy /y "$(SRCDIR)\$(LIBSHARED)" .
+       copy /y "$(SRCDIR_CXX)\$(LIBSHARED_CXX)" .
+
+clean: FORCE
+       -@if exist *.obj del *.obj
+       -@if exist *.dll del *.dll
+       -@if exist *.exp del *.exp
+       -@if exist *.lib del *.lib
+       -@if exist *.ilk del *.ilk
+       -@if exist *.pdb del *.pdb
+       -@if exist *.pgc del *.pgc
+       -@if exist *.pgd del *.pgd
+       -@if exist *.manifest del *.manifest
+       -@if exist *.exe del *.exe
+       -@if exist runtest del runtest
+       -@if exist apitest del apitest
+
+distclean: FORCE
+       nmake clean
+       -@if exist testdata rd /q /s testdata
+       -@if exist Makefile del Makefile
+
diff --git a/tests++/README.txt b/tests++/README.txt
new file mode 100644 (file)
index 0000000..5dc0633
--- /dev/null
@@ -0,0 +1,27 @@
+
+
+Download official tests and add the tests to the testdata directory:
+====================================================================
+
+# Unix: If wget is installed, just execute `make check` from
+# the top level directory.
+
+# Windows: See vcbuild directory.
+
+
+#
+# If gettests.sh or gettests.bat fails:
+#
+#   mkdir testdata && cp testdata_dist/* testdata
+#
+# Get http://speleotrove.com/decimal/dectest.zip and extract the archive
+# so that the directory structure is testdata/*.decTest.
+#
+# Change into the top level directory, build the library, change back
+# to the test directory, run:
+#
+#   make && ./runshort.sh
+#
+
+
+
diff --git a/tests++/additional.topTest b/tests++/additional.topTest
new file mode 100644 (file)
index 0000000..4614693
--- /dev/null
@@ -0,0 +1,30 @@
+
+-- Additional Tests
+
+Dectest: ./testdata/baseconv.decTest
+
+Dectest: ./testdata/binop_eq.decTest
+
+Dectest: ./testdata/divmod.decTest
+Dectest: ./testdata/divmod_eq.decTest
+
+Dectest: ./testdata/fma_eq.decTest
+
+Dectest: ./testdata/format.decTest
+
+Dectest: ./testdata/invroot.decTest
+
+Dectest: ./testdata/largeint.decTest
+
+Dectest: ./testdata/powmod.decTest
+Dectest: ./testdata/powmod_eq.decTest
+
+Dectest: ./testdata/shiftlr.decTest
+
+Dectest: ./testdata/getint.decTest
+
+Dectest: ./testdata/cov.decTest
+Dectest: ./testdata/extra.decTest
+
+Dectest: ./testdata/maxprec.decTest
+
diff --git a/tests++/apitest.cc b/tests++/apitest.cc
new file mode 100644 (file)
index 0000000..d00c09b
--- /dev/null
@@ -0,0 +1,2861 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <climits>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <list>
+#include <map>
+#include <random>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include "mpdecimal.h"
+
+#include "decimal.hh"
+#include "test.hh"
+#include "vctest.hh"
+
+
+using decimal::Context;
+using decimal::context_template;
+using decimal::context;
+
+using decimal::ROUND_UP;
+using decimal::ROUND_DOWN;
+using decimal::ROUND_CEILING;
+using decimal::ROUND_FLOOR;
+using decimal::ROUND_HALF_UP;
+using decimal::ROUND_HALF_DOWN;
+using decimal::ROUND_HALF_EVEN;
+using decimal::ROUND_05UP;
+using decimal::ROUND_TRUNC;
+using decimal::ROUND_GUARD;
+
+using decimal::DecIEEEInvalidOperation;
+using decimal::DecConversionSyntax;
+using decimal::DecInvalidOperation;
+using decimal::DecDivisionImpossible;
+using decimal::DecDivisionUndefined;
+
+using decimal::DecDivisionByZero;
+using decimal::DecOverflow;
+using decimal::DecUnderflow;
+using decimal::DecSubnormal;
+using decimal::DecInexact;
+using decimal::DecRounded;
+using decimal::DecClamped;
+
+using decimal::DecMaxStatus;
+
+using decimal::MaxContext;
+using decimal::IEEEContext;
+using decimal::DECIMAL32;
+using decimal::DECIMAL64;
+using decimal::DECIMAL128;
+
+using decimal::DecimalException;
+
+using decimal::IEEEInvalidOperation;
+using decimal::ConversionSyntax;
+using decimal::InvalidOperation;
+using decimal::DivisionImpossible;
+using decimal::DivisionUndefined;
+
+using decimal::DivisionByZero;
+using decimal::Overflow;
+using decimal::Underflow;
+using decimal::Subnormal;
+using decimal::Inexact;
+using decimal::Rounded;
+using decimal::Clamped;
+
+using decimal::ValueError;
+using decimal::RuntimeError;
+using decimal::MallocError;
+
+using decimal::Decimal;
+
+using decimal::util::safe_downcast;
+
+using test::Failure;
+
+
+/******************************************************************************/
+/*                Default context for some generated test cases               */
+/******************************************************************************/
+
+static const Context pycontext{ 28, 999999, -999999, ROUND_HALF_EVEN,
+                                DecIEEEInvalidOperation | DecDivisionByZero | DecOverflow,
+                                0, 0 };
+
+/******************************************************************************/
+/*                            Exception hierarchy                             */
+/******************************************************************************/
+
+static void
+ExceptionHierarchyTest()
+{
+    assertEqual(context, context_template);
+
+    assertTrue((std::is_base_of<std::exception, DecimalException>::value));
+
+        assertTrue((std::is_base_of<DecimalException, IEEEInvalidOperation>::value));
+            assertTrue((std::is_base_of<IEEEInvalidOperation, ConversionSyntax>::value));
+            assertTrue((std::is_base_of<IEEEInvalidOperation, DivisionImpossible>::value));
+            assertTrue((std::is_base_of<IEEEInvalidOperation, DivisionUndefined>::value));
+            assertTrue((std::is_base_of<IEEEInvalidOperation, InvalidOperation>::value));
+
+        assertTrue((std::is_base_of<DecimalException, DivisionByZero>::value));
+        assertTrue((std::is_base_of<DecimalException, Overflow>::value));
+        assertTrue((std::is_base_of<DecimalException, Underflow>::value));
+        assertTrue((std::is_base_of<DecimalException, Subnormal>::value));
+        assertTrue((std::is_base_of<DecimalException, Inexact>::value));
+        assertTrue((std::is_base_of<DecimalException, Rounded>::value));
+        assertTrue((std::is_base_of<DecimalException, Clamped>::value));
+
+    assertTrue((std::is_base_of<std::exception, ValueError>::value));
+    assertTrue((std::is_base_of<std::exception, RuntimeError>::value));
+    assertTrue((std::is_base_of<std::exception, MallocError>::value));
+}
+
+/******************************************************************************/
+/*                          IEEE interchange contexts                         */
+/******************************************************************************/
+
+static void
+IEEEContextTest()
+{
+    assertEqual(context, context_template);
+
+    Context c = IEEEContext(DECIMAL32);
+    assertEqual(c.prec(), 7);
+    assertEqual(c.emax(), 96);
+    assertEqual(c.emin(), -95);
+    assertEqual(c.round(), ROUND_HALF_EVEN);
+    assertEqual(c.traps(), 0U);
+    assertEqual(c.status(), 0U);
+    assertEqual(c.clamp(), 1);
+    assertEqual(c.allcr(), 1);
+    assertEqual(c.etiny(), -101);
+    assertEqual(c.etop(), 90);
+
+    c = IEEEContext(DECIMAL64);
+    assertEqual(c.prec(), 16);
+    assertEqual(c.emax(), 384);
+    assertEqual(c.emin(), -383);
+    assertEqual(c.round(), ROUND_HALF_EVEN);
+    assertEqual(c.traps(), 0U);
+    assertEqual(c.status(), 0U);
+    assertEqual(c.clamp(), 1);
+    assertEqual(c.allcr(), 1);
+    assertEqual(c.etiny(), -398);
+    assertEqual(c.etop(), 369);
+
+    c = IEEEContext(DECIMAL128);
+    assertEqual(c.prec(), 34);
+    assertEqual(c.emax(), 6144);
+    assertEqual(c.emin(), -6143);
+    assertEqual(c.round(), ROUND_HALF_EVEN);
+    assertEqual(c.traps(), 0U);
+    assertEqual(c.status(), 0U);
+    assertEqual(c.clamp(), 1);
+    assertEqual(c.allcr(), 1);
+    assertEqual(c.etiny(), -6176);
+    assertEqual(c.etop(), 6111);
+
+    assertRaises(ValueError, [](){ IEEEContext(-1); });
+    assertRaises(ValueError, [](){ IEEEContext(0); });
+    assertRaises(ValueError, [](){ IEEEContext(16); });
+    assertRaises(ValueError, [](){ IEEEContext(1024); });
+}
+
+/******************************************************************************/
+/*                              Context get/set                               */
+/******************************************************************************/
+
+static void
+ContextGetSetTest()
+{
+    assertEqual(context, context_template);
+
+    Context c = context;
+
+    c.prec(34);
+    c.emax(3000);
+    c.emin(-3000);
+    c.round(ROUND_HALF_UP);
+    c.traps(DecMaxStatus);
+    c.status(DecMaxStatus);
+    c.clamp(1);
+    c.allcr(0);
+
+    assertEqual(c.prec(), 34);
+    assertEqual(c.emax(), 3000);
+    assertEqual(c.emin(), -3000);
+    assertEqual(c.round(), ROUND_HALF_UP);
+    assertEqual(c.traps(), DecMaxStatus);
+    assertEqual(c.status(), DecMaxStatus);
+    assertEqual(c.clamp(), 1);
+    assertEqual(c.allcr(), 0);
+    assertEqual(c.etiny(), -3033);
+    assertEqual(c.etop(), 2967);
+
+    /* etop is the same, even though it is only relevant for clamp==1 */
+    c.clamp(0);
+    assertEqual(c.etiny(), -3033);
+    assertEqual(c.etop(), 2967);
+
+    c.clear_status(DecDivisionByZero);
+    assertEqual(c.status(), DecMaxStatus & ~DecDivisionByZero);
+
+    c.clear_status();
+    assertEqual(c.status(), 0U);
+
+    c.add_status(DecClamped|DecUnderflow);
+    assertEqual(c.status(), DecClamped|DecUnderflow);
+
+    c.add_status(DecInvalidOperation);
+    assertEqual(c.status(), DecClamped|DecUnderflow|DecInvalidOperation);
+
+    c.clear_traps(DecDivisionUndefined);
+    assertEqual(c.traps(), DecMaxStatus & ~DecDivisionUndefined);
+
+    c.clear_traps();
+    assertEqual(c.traps(), 0U);
+
+    c.add_traps(DecClamped|DecUnderflow);
+    assertEqual(c.traps(), DecClamped|DecUnderflow);
+
+    c.add_traps(DecInvalidOperation);
+    assertEqual(c.traps(), DecClamped|DecUnderflow|DecInvalidOperation);
+}
+
+
+/******************************************************************************/
+/*                          Context input validation                          */
+/******************************************************************************/
+
+static void
+ContextInputValidationTest()
+{
+    assertEqual(context, context_template);
+
+    Context c = context;
+
+    /* prec */
+    c.prec(1111);
+    assertEqual(c.prec(), 1111);
+    assertRaises(ValueError, [&](){ c.prec(-1); });
+    assertRaises(ValueError, [&](){ c.prec(MPD_SSIZE_MAX); });
+
+    /* emin */
+    c.emin(-1111);
+    assertEqual(c.emin(), -1111);
+    assertRaises(ValueError, [&](){ c.emin(1); });
+    assertRaises(ValueError, [&](){ c.emin(MPD_SSIZE_MIN); });
+
+    /* emax */
+    c.emax(1111);
+    assertEqual(c.emax(), 1111);
+    assertRaises(ValueError, [&](){ c.emax(-1); });
+    assertRaises(ValueError, [&](){ c.emax(MPD_SSIZE_MAX); });
+
+    /* round */
+    assertRaises(ValueError, [&](){ c.round(-1); });
+    assertRaises(ValueError, [&](){ c.round(ROUND_GUARD); });
+
+    /* traps */
+    assertRaises(ValueError, [&](){ c.traps(DecMaxStatus+1); });
+    assertRaises(ValueError, [&](){ c.traps(UINT32_MAX); });
+
+    /* clamp */
+    assertRaises(ValueError, [&](){ c.clamp(-1); });
+    assertRaises(ValueError, [&](){ c.clamp(2); });
+    assertRaises(ValueError, [&](){ c.clamp(INT_MAX); });
+
+    /* constructor */
+    assertRaises(ValueError, [&](){ Context(1, 1, -1, 999999); });
+}
+
+/******************************************************************************/
+/*                                Small context                               */
+/******************************************************************************/
+
+static void
+SmallContextTest()
+{
+    assertEqual(context, context_template);
+
+    Context &c = context;
+
+    Context xc{pycontext};
+    xc.prec(1);
+    xc.emax(1);
+    xc.emin(-1);
+
+    assertEqual(Decimal(9, xc), 9);
+
+    xc.clear_status();
+    assertRaises(ConversionSyntax, [&](){ Decimal("xyz", xc); });
+    assertEqual(xc.status(), DecConversionSyntax);
+    assertEqual(c.status(), 0U);
+
+    xc.clear_status();
+    assertEqual(Decimal(2).exp(xc), 7);
+    assertRaises(Overflow, [&](){ Decimal(8).exp(xc); });
+    assertTrue(xc.status() & DecOverflow);
+    assertEqual(c.status(), 0U);
+
+    xc.clear_status();
+    assertEqual(Decimal(2).ln(xc), Decimal("0.7"));
+    assertRaises(InvalidOperation, [&](){ Decimal(-1).ln(xc); });
+    assertTrue(xc.status() & DecInvalidOperation);
+    assertFalse(c.status() & DecInvalidOperation);
+
+    assertEqual(Decimal(0).log10(xc), Decimal("-inf"));
+    assertEqual(Decimal(-1).next_minus(xc), -2);
+    assertEqual(Decimal(-1).next_plus(xc), Decimal("-0.9"));
+    assertEqual(Decimal("9.73").reduce(xc), Decimal("1E+1"));
+    assertEqual(Decimal("9999").to_integral(xc), 9999);
+    assertEqual(Decimal("-2000").to_integral_exact(xc), -2000);
+    assertEqual(Decimal("0.0625").sqrt(xc), Decimal("0.2"));
+
+    assertEqual(Decimal("0.0625").compare(3, xc), -1);
+
+    xc.clear_status();
+    assertRaises(InvalidOperation, [&](){ Decimal("0").compare_signal(Decimal("nan"), xc); });
+    assertTrue(xc.status() & DecInvalidOperation);
+    assertFalse(c.status() & DecInvalidOperation);
+
+    assertEqual(Decimal("0.01").max(Decimal("0.0101"), xc), Decimal("0.0"));
+    assertEqual(Decimal("0.01").max(Decimal("0.0101"), xc), Decimal("0.0"));
+    assertEqual(Decimal("0.2").max_mag(Decimal("-0.3"), xc),
+                Decimal("-0.3"));
+    assertEqual(Decimal("0.02").min(Decimal("-0.03"), xc), Decimal("-0.0"));
+    assertEqual(Decimal("0.02").min_mag(Decimal("-0.03"), xc),
+                Decimal("0.0"));
+    assertEqual(Decimal("0.2").next_toward(Decimal("-1"), xc), Decimal("0.1"));
+
+    xc.clear_status();
+    assertRaises(InvalidOperation, [&](){ Decimal("0.2").quantize(Decimal("1e10"), xc); });
+    assertTrue(xc.status() & DecInvalidOperation);
+    assertFalse(c.status() & DecInvalidOperation);
+    assertEqual(Decimal("9.99").rem_near(Decimal("1.5"), xc), Decimal("-0.5"));
+
+    assertEqual(Decimal("9.9").fma(7, Decimal("0.9"), xc), Decimal("7E+1"));
+
+    assertFalse(Decimal("0.01").isnormal(xc));
+    assertTrue(Decimal("0.01").issubnormal(xc));
+
+    assertEqual(Decimal(-111).logb(xc), 2);
+    assertEqual(Decimal(0).logical_invert(xc), 1);
+    assertEqual(Decimal("0.01").number_class(xc), std::string("+Subnormal"));
+    assertEqual(Decimal("0.21").to_eng(), "0.21");
+    assertEqual(Decimal("9.99e10").to_eng(), "99.9E+9");
+
+    assertEqual(Decimal("11").logical_and(Decimal("10"), xc), 0);
+    assertEqual(Decimal("11").logical_or(Decimal("10"), xc), 1);
+    assertEqual(Decimal("01").logical_xor(Decimal("10"), xc), 1);
+    assertEqual(Decimal("23").rotate(1, xc), 3);
+    assertEqual(Decimal("23").rotate(1, xc), 3);
+
+    xc.clear_status();
+    assertRaises(Overflow, [&](){ Decimal("23").scaleb(1, xc); });
+    assertTrue(xc.status() & DecOverflow);
+    assertFalse(c.status() & DecOverflow);
+    assertEqual(Decimal("23").shift(-1, xc), 0);
+
+    assertEqual(Decimal(1).canonical(), 1);
+}
+
+/******************************************************************************/
+/*                           Context representation                           */
+/******************************************************************************/
+
+static void
+ContextReprTest()
+{
+    assertEqual(context, context_template);
+
+    context.prec(425000000);
+    context.emax(425000000);
+    context.emin(-425000000);
+    context.round(ROUND_HALF_UP);
+    context.clamp(1);
+    context.allcr(1);
+    context.traps(DecMaxStatus);
+    context.status(DecMaxStatus);
+
+    const char *t =
+"Context(prec=425000000, emax=425000000, emin=-425000000, round=ROUND_HALF_UP, clamp=1, "
+"traps=[IEEEInvalidOperation, DivisionByZero, Overflow, Underflow, "
+       "Subnormal, Inexact, Rounded, Clamped], "
+"status=[IEEEInvalidOperation, DivisionByZero, Overflow, Underflow, "
+        "Subnormal, Inexact, Rounded, Clamped])";
+
+    auto s = context.repr();
+    assertEqualStr(s, t);
+
+    s = test::str(context);
+    assertEqualStr(s, t);
+}
+
+/******************************************************************************/
+/*                          Exact conversions (default)                       */
+/******************************************************************************/
+
+static const char *large_prec[] = {
+/* int */
+"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890",
+
+/* negative int */
+"-12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890",
+
+/* float */
+"1.2345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890E+999999",
+
+/* negative float */
+"-1.2345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890E+999999",
+
+/* tiny float */
+"1.2345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890E-999999",
+
+/* negative tiny float */
+"1.2345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890E-999999",
+
+/* nan */
+"NaN12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"234567890123456789012345678901234567890",
+
+/* negative nan */
+"-NaN12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890",
+
+/* snan */
+"sNaN12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890",
+
+/* negative snan */
+"-sNaN12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+"1234567890123456789012345678901234567890",
+};
+
+template<class T>
+static void
+signed_construction()
+{
+    const T min = std::numeric_limits<T>::min();
+    const T max = std::numeric_limits<T>::max();
+
+    const std::vector<T> values = {min, -1, 0, 1, max};
+    for (const T& v : values) {
+        const Decimal d = {v};
+        const std::string ds = d.to_sci();
+        const std::string vs = std::to_string(v);
+        assertEqual(d, v);
+        assertEqual(v, d);
+        assertEqual(ds, vs);
+        assertEqual(vs, ds);
+    }
+
+    const std::vector<T> signs = {-1, 1};
+    for (int n = 0; n < std::numeric_limits<T>::digits; n++) {
+        for (const T& sign : signs) {
+            for (T x = -5; x < 5;  x++) {
+                const T i = static_cast<T>(sign * ((static_cast<T>(1) << n) + x));
+                const Decimal d = {i};
+                assertEqual(d, i);
+                assertEqual(i, d);
+            }
+        }
+    }
+}
+
+template<class T>
+static void
+unsigned_construction()
+{
+    const T max = std::numeric_limits<T>::max();
+
+    const std::vector<T> values = {0, 1, max};
+    for (const T& v : values) {
+        const Decimal d = {v};
+        const std::string ds = d.to_sci();
+        const std::string vs = std::to_string(v);
+        assertEqual(d, v);
+        assertEqual(v, d);
+        assertEqual(ds, vs);
+        assertEqual(vs, ds);
+    }
+
+    for (int n = 0; n < std::numeric_limits<T>::digits; n++) {
+        for (T x = 0; x < 5;  x++) {
+            const T i = static_cast<T>((static_cast<T>(1) << n) + x);
+            const Decimal d = {i};
+            assertEqual(d, i);
+            assertEqual(i, d);
+        }
+    }
+}
+
+static void
+ExactConstructionTest()
+{
+    assertEqual(context, context_template);
+
+    context.prec(1);
+    context.emax(1);
+    context.emin(-1);
+    context.traps(DecMaxStatus);
+
+    /*****************************************************************/
+    /*                       Implicit conversions                    */
+    /*****************************************************************/
+
+    /* from empty */
+    Decimal empty;
+    assertTrue(empty.issnan());
+
+    /* from const Decimal& */
+    for (auto& s : {"-NaN", "-4096", "4096", "4.5E+3", "Infinity"}) {
+        const Decimal init{Decimal(s)};
+        Decimal x = {init};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    for (auto& s : large_prec) {
+        const Decimal init{Decimal(s)};
+        Decimal x = {init};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    /* from Decimal&& */
+    for (auto& s : {"-NaN", "-4096", "4096", "4.5E+3", "Infinity"}) {
+        Decimal init{Decimal(s)};
+        Decimal x = {std::move(init)};
+        assertTrue(init.issnan());
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    for (auto& s : large_prec) {
+        Decimal init{Decimal(s)};
+        Decimal x = {std::move(init)};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    /* from integers */
+    signed_construction<signed char>();
+    signed_construction<short>();
+    signed_construction<int>();
+    signed_construction<long>();
+    signed_construction<long long>();
+
+    signed_construction<int8_t>();
+    signed_construction<int16_t>();
+    signed_construction<int32_t>();
+    signed_construction<int64_t>();
+
+    unsigned_construction<unsigned char>();
+    unsigned_construction<unsigned short>();
+    unsigned_construction<unsigned int>();
+    unsigned_construction<unsigned long>();
+    unsigned_construction<unsigned long long>();
+
+    unsigned_construction<uint8_t>();
+    unsigned_construction<uint16_t>();
+    unsigned_construction<uint32_t>();
+    unsigned_construction<uint64_t>();
+
+    /*****************************************************************/
+    /*                       Explicit conversions                    */
+    /*****************************************************************/
+
+    /* from string */
+    for (auto& s : {"-NaN", "-4096", "4096", "4.5E+3", "Infinity"}) {
+        Decimal x{s};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    for (auto& s : large_prec) {
+        Decimal x{s};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    /* from std::string */
+    for (auto& s : {"-sNaN", "123", "1E+999999", "1E-999999"}) {
+        Decimal x{std::string(s)};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    for (auto& s : large_prec) {
+        Decimal x{std::string(s)};
+        assertEqualStr(x, s);
+        assertEqualStr(s, x);
+    }
+
+    assertEqual(context.status(), 0U);
+
+    /* from string, out of bounds for exact conversion */
+    context.traps(DecInvalidOperation);
+
+    std::string s = std::string("0E") + std::to_string(INT64_MAX);
+    assertRaises(InvalidOperation, [&](){ Decimal x{s}; });
+
+    s = std::string("0E") + std::to_string(INT64_MIN);
+    assertRaises(InvalidOperation, [&](){ Decimal x{s}; });
+
+    s = std::string("1E") + std::to_string(INT64_MAX);
+    assertRaises(InvalidOperation, [&](){ Decimal x{s}; });
+
+    s = std::string("1E") + std::to_string(INT64_MIN);
+    assertRaises(InvalidOperation, [&](){ Decimal x{s}; });
+
+    /* pass explicit context for error reporting */
+    Context c = Context();
+    c.clear_traps();
+    s = std::string("0E") + std::to_string(INT64_MAX);
+    Decimal res = Decimal::exact(s, c);
+    assertEqual(c.status(), DecInvalidOperation);
+
+    /* large values */
+    const char *decstring = "9.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999e425000000";
+    const char *large_exp = "9.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999e99999999999999999999";
+    Decimal large{decstring};
+
+    c.traps(DecMaxStatus-1);
+    c.prec(1);
+
+    Decimal xlarge = Decimal(large);
+    assertEqual(xlarge, large);
+
+    xlarge = Decimal(std::move(large));
+    assertEqual(xlarge, Decimal(decstring));
+    assertTrue(large.issnan());
+
+    assertRaises(InvalidOperation, [&](){ Decimal x = Decimal(large_exp); });
+
+    s = std::string(large_exp);
+    assertRaises(InvalidOperation, [&](){ Decimal x = Decimal(s); });
+
+    context.clear_status();
+    context.clear_traps();
+}
+
+
+/******************************************************************************/
+/*                     Conversions that respect the context                   */
+/******************************************************************************/
+
+template<class T>
+static void
+signed_construction_ctx(Context& ctx)
+{
+    const T min = std::numeric_limits<T>::min();
+    const T max = std::numeric_limits<T>::max();
+
+    for (const T& v : {min, max}) {
+        ctx.clear_traps();
+        ctx.clear_status();
+        const Decimal expected = Decimal(std::to_string(v), ctx);
+        const uint32_t expected_status = ctx.status();
+
+        ctx.clear_status();
+        const Decimal calc = Decimal(v, ctx);
+        const uint32_t calc_status = ctx.status();
+
+        assertEqual(calc, expected);
+        assertEqual(calc_status, expected_status);
+
+        ctx.traps(DecInexact);
+        ctx.clear_status();
+        assertRaises(Inexact, [&](){ Decimal(v, ctx); });
+        assertEqual(ctx.status(), DecInexact|DecRounded);
+        ctx.clear_traps();
+    }
+}
+
+template<class T>
+static void
+unsigned_construction_ctx(Context& ctx)
+{
+    const T v = std::numeric_limits<T>::max();
+    ctx.clear_traps();
+
+    ctx.clear_status();
+    const Decimal expected = Decimal(std::to_string(v), ctx);
+    const uint32_t expected_status = ctx.status();
+
+    ctx.clear_status();
+    const Decimal calc = Decimal(v, ctx);
+    const uint32_t calc_status = ctx.status();
+
+    assertEqual(calc, expected);
+    assertEqual(calc_status, expected_status);
+
+    ctx.traps(DecInexact);
+    ctx.clear_status();
+    assertRaises(Inexact, [&](){ Decimal(v, ctx); });
+    assertEqual(ctx.status(), DecInexact|DecRounded);
+    ctx.clear_traps();
+}
+
+static void
+InexactConstructionTest()
+{
+    assertEqual(context, context_template);
+    context.traps(DecMaxStatus);
+
+    Context ctx{context_template};
+    ctx.prec(1);
+    ctx.emax(1);
+    ctx.emin(-1);
+
+    /*****************************************************************/
+    /*                       Explicit conversions                    */
+    /*****************************************************************/
+
+    /* from const Decimal& */
+    ctx.clear_traps();
+    ctx.clear_status();
+
+    Decimal nan = Decimal("-NaN123");
+    Decimal integer = Decimal("-4096");
+    Decimal floating = Decimal("4.5E+1");
+
+
+    Decimal x = Decimal(nan, ctx);
+    assertEqualStr(x, "NaN");
+    assertEqual(ctx.status(), DecConversionSyntax);
+
+    ctx.clear_status();
+    x = Decimal(integer, ctx);
+    assertEqualStr(x, "-Infinity");
+    assertEqual(ctx.status(), (DecInexact | DecOverflow | DecRounded));
+
+    ctx.clear_status();
+    x = Decimal(floating, ctx);
+    assertEqualStr(x, "4E+1");
+    assertEqual(ctx.status(), (DecInexact | DecRounded));
+
+    ctx.traps(DecMaxStatus);
+    ctx.clear_status();
+    assertRaises(ConversionSyntax, [&](){ Decimal(nan, ctx); });
+    assertEqual(ctx.status(), DecConversionSyntax);
+
+    ctx.clear_status();
+    assertRaises(Overflow, [&](){ Decimal(integer, ctx); });
+    assertEqual(ctx.status(), (DecInexact | DecOverflow | DecRounded));
+
+    ctx.clear_status();
+    assertRaises(Inexact, [&](){ Decimal(floating, ctx); });
+    assertEqual(ctx.status(), (DecInexact | DecRounded));
+    ctx.clear_traps();
+
+    /* from integers */
+    ctx.prec(1);
+    ctx.emax(19);
+    ctx.emin(-19);
+
+    signed_construction_ctx<signed char>(ctx);
+    signed_construction_ctx<short>(ctx);
+    signed_construction_ctx<int>(ctx);
+    signed_construction_ctx<long>(ctx);
+    signed_construction_ctx<long long>(ctx);
+
+    signed_construction_ctx<int8_t>(ctx);
+    signed_construction_ctx<int16_t>(ctx);
+    signed_construction_ctx<int32_t>(ctx);
+    signed_construction_ctx<int64_t>(ctx);
+
+    unsigned_construction_ctx<unsigned char>(ctx);
+    unsigned_construction_ctx<unsigned short>(ctx);
+    unsigned_construction_ctx<unsigned int>(ctx);
+    unsigned_construction_ctx<unsigned long>(ctx);
+    unsigned_construction_ctx<unsigned long long>(ctx);
+
+    unsigned_construction_ctx<uint8_t>(ctx);
+    unsigned_construction_ctx<uint16_t>(ctx);
+    unsigned_construction_ctx<uint32_t>(ctx);
+    unsigned_construction_ctx<uint64_t>(ctx);
+
+    /* from string */
+    ctx.prec(3);
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    Decimal d = Decimal("456789");
+    assertEqualStr(d, "456789");
+    d = Decimal("456789", ctx);
+    assertEqualStr(d, "4.57E+5");
+
+    ctx.traps(DecInexact);
+    assertRaises(Inexact, [&](){ Decimal("456789", ctx); });
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    ctx.traps(DecRounded);
+    assertRaises(Rounded, [&](){ Decimal("456789", ctx); });
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    ctx.traps(DecConversionSyntax);
+    assertRaises(ConversionSyntax, [&](){ Decimal("NaN12345", ctx); });
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    /* from std::string */
+    d = Decimal(std::string("456789"));
+    assertEqualStr(d, "456789");
+    d = Decimal(std::string("456789"), ctx);
+    assertEqualStr(d, "4.57E+5");
+
+    ctx.traps(DecInexact);
+    assertRaises(Inexact, [&](){ Decimal(std::string("456789"), ctx); });
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    ctx.traps(DecRounded);
+    assertRaises(Rounded, [&](){ Decimal(std::string("456789"), ctx); });
+    ctx.clear_status();
+    ctx.clear_traps();
+
+    ctx.traps(DecConversionSyntax);
+    assertRaises(ConversionSyntax, [&](){ Decimal(std::string("NaN12345"), ctx); });
+
+#ifndef __mips__ /* miscompilation */
+    /* large values */
+    const char *decstring = "9.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999e425000000";
+    const char *large_exp = "9.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999e99999999999999999999";
+    Decimal large{decstring};
+
+    ctx.traps(DecMaxStatus-1);
+    ctx.prec(100);
+    ctx.emax(425000000);
+    ctx.emin(-425000000);
+
+    Decimal xlarge = Decimal(large, ctx);
+    assertEqual(xlarge, large);
+
+    ctx.prec(1);
+    assertRaises(Overflow, [&](){ Decimal x = Decimal(large_exp, ctx); });
+
+    std::string s = std::string(large_exp);
+    assertRaises(Overflow, [&](){ Decimal x = Decimal(s, ctx); });
+#endif
+
+    ctx.clear_status();
+    ctx.clear_traps();
+}
+
+/******************************************************************************/
+/*                         Exceptions during construction                     */
+/******************************************************************************/
+
+static void
+ConstructionExceptionTest()
+{
+    assertEqual(context, context_template);
+
+    /*** from string ***/
+
+    /* invalid syntax */
+    context.add_traps(DecConversionSyntax);
+    assertRaises(ConversionSyntax, [](){ Decimal(""); });
+    assertRaises(ConversionSyntax, [](){ Decimal("xyz"); });
+    assertRaises(ConversionSyntax, [](){ Decimal("1 23"); });
+    assertRaises(ConversionSyntax, [](){ Decimal("123\n"); });
+    context.clear_traps(DecConversionSyntax);
+
+    context.add_traps(DecIEEEInvalidOperation);
+    assertRaises(IEEEInvalidOperation, [](){ Decimal("xyz"); });
+    assertRaises(IEEEInvalidOperation, [](){ Decimal("1 23"); });
+    assertRaises(IEEEInvalidOperation, [](){ Decimal("123\n"); });
+    context.clear_traps(DecIEEEInvalidOperation);
+
+    /* too large for exact conversion */
+    context.add_traps(DecInvalidOperation);
+    assertRaises(InvalidOperation, [](){ Decimal("1e9999999999999999999"); });
+    assertRaises(InvalidOperation, [](){ Decimal("-1e9999999999999999999"); });
+    assertRaises(InvalidOperation, [](){ Decimal("1e-9999999999999999999"); });
+    assertRaises(InvalidOperation, [](){ Decimal("-1e-9999999999999999999"); });
+    context.clear_traps(DecInvalidOperation);
+
+    /*** from std::string ***/
+
+    /* invalid syntax */
+    context.add_traps(DecConversionSyntax);
+    assertRaises(ConversionSyntax, [](){ Decimal(std::string("xyz")); });
+    assertRaises(ConversionSyntax, [](){ Decimal(std::string("1 23")); });
+    context.clear_traps(DecConversionSyntax);
+
+    context.add_traps(DecIEEEInvalidOperation);
+    assertRaises(IEEEInvalidOperation, [](){ Decimal(std::string("xyz")); });
+    assertRaises(IEEEInvalidOperation, [](){ Decimal(std::string("1 23")); });
+    context.clear_traps(DecIEEEInvalidOperation);
+
+    /* too large for exact conversion */
+    context.add_traps(DecInvalidOperation);
+    assertRaises(InvalidOperation, [](){ Decimal(std::string("1e9999999999999999999")); });
+    assertRaises(InvalidOperation, [](){ Decimal(std::string("-1e9999999999999999999")); });
+    assertRaises(InvalidOperation, [](){ Decimal(std::string("1e-9999999999999999999")); });
+    assertRaises(InvalidOperation, [](){ Decimal(std::string("-1e-9999999999999999999")); });
+    context.clear_traps(DecInvalidOperation);
+}
+
+/******************************************************************************/
+/*                                  Accessors                                 */
+/******************************************************************************/
+
+static void
+AccessorTest()
+{
+    assertEqual(context, context_template);
+
+    Decimal d = Decimal("1.234E+200");
+    assertEqual(d.sign(), 1);
+    assertEqual(d.exponent(), 197);
+    assertEqual(d.coeff(), 1234);
+    assertRaises(ValueError, [&](){ d.payload(); });
+
+    d = Decimal("-1.234E-20");
+    assertEqual(d.sign(), -1);
+    assertEqual(d.exponent(), -23);
+    assertEqual(d.coeff(), 1234);
+    assertRaises(ValueError, [&](){ d.payload(); });
+
+    d = Decimal("inf");
+    assertEqual(d.sign(), 1);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+    assertRaises(ValueError, [&](){ d.payload(); });
+
+    d = Decimal("-inf");
+    assertEqual(d.sign(), -1);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+    assertRaises(ValueError, [&](){ d.payload(); });
+
+    d = Decimal("nan");
+    assertEqual(d.sign(), 1);
+    assertEqual(d.payload(), 0);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+
+    d = Decimal("-nan");
+    assertEqual(d.sign(), -1);
+    assertEqual(d.payload(), 0);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+
+    d = Decimal("snan");
+    assertEqual(d.payload(), 0);
+    assertEqual(d.sign(), 1);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+
+    d = Decimal("-snan");
+    assertEqual(d.payload(), 0);
+    assertEqual(d.sign(), -1);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+
+    d = Decimal("nan123");
+    assertEqual(d.sign(), 1);
+    assertEqual(d.payload(), 123);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+
+    std::string payload = "123456789123456789123456789123456789123456789123456789123456789123456789"
+                          "123456789123456789123456789123456789123456789123456789123456789123456789";
+    d = Decimal("-nan" + payload);
+    assertEqual(d.sign(), -1);
+    assertEqualStr(d.payload(), payload);
+    assertRaises(ValueError, [&](){ d.exponent(); });
+    assertRaises(ValueError, [&](){ d.coeff(); });
+}
+
+
+/******************************************************************************/
+/*                             Assignment operators                           */
+/******************************************************************************/
+
+template <class T>
+static void
+signed_assignment()
+{
+    const T min = std::numeric_limits<T>::min();
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {min, -2, 2, max};
+
+    for (const T& v : values) {
+        Decimal x = v;
+        assertEqual(x, Decimal(v));
+
+        x = Decimal(-10000); x += v;
+        assertEqual(x, Decimal(-10000) + v);
+
+        x = Decimal(2122); x -= v;
+        assertEqual(x, Decimal(2122) - v);
+
+        x = Decimal("1.231e10"); x *= v;
+        assertEqual(x, Decimal("1.231e10") * v);
+
+        x = Decimal("225e-10"); x /= v;
+        assertEqual(x, Decimal("225e-10") / v);
+
+        x = Decimal("25"); x %= v;
+        assertEqual(x, Decimal("25") % v);
+    }
+}
+
+template <class T>
+static void
+unsigned_assignment()
+{
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {2, max};
+
+    for (const T& v : values) {
+        Decimal x = v;
+        assertEqual(x, Decimal(v));
+
+        x = Decimal(-10000); x += v;
+        assertEqual(x, Decimal(-10000) + v);
+
+        x = Decimal(2122); x -= v;
+        assertEqual(x, Decimal(2122) - v);
+
+        x = Decimal("1.231e10"); x *= v;
+        assertEqual(x, Decimal("1.231e10") * v);
+
+        x = Decimal("225e-10"); x /= v;
+        assertEqual(x, Decimal("225e-10") / v);
+
+        x = Decimal("25"); x %= v;
+        assertEqual(x, Decimal("25") % v);
+    }
+}
+
+static void
+AssignmentOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    /* Decimal */
+    context.prec(10000);
+
+    Decimal x = Decimal(10).pow(1200);
+    assertEqual(x, Decimal(10).pow(1200));
+
+    x = Decimal(10).pow(1200); x += Decimal("1.1127312");
+    assertEqual(x, Decimal(10).pow(1200) + Decimal("1.1127312"));
+
+    x = Decimal(10).pow(1200); x -= Decimal("1.1127312");
+    assertEqual(x, Decimal(10).pow(1200) - Decimal("1.1127312"));
+
+    x = -Decimal(10).pow(1200); x *= Decimal("1.1127312");
+    assertEqual(x, -Decimal(10).pow(1200) * Decimal("1.1127312"));
+
+    x = -Decimal(10).pow(1200); x /= Decimal("1.1127312");
+    assertEqual(x, -Decimal(10).pow(1200) / Decimal("1.1127312"));
+
+    x = Decimal(10).pow(1200); x %= Decimal("1.1127312");
+    assertEqual(x, Decimal(10).pow(1200) % Decimal("1.1127312"));
+
+    /* integers */
+    context.prec(3);
+    context.clear_status();
+
+    signed_assignment<signed char>();
+    signed_assignment<short>();
+    signed_assignment<int>();
+    signed_assignment<long>();
+    signed_assignment<long long>();
+
+    signed_assignment<int8_t>();
+    signed_assignment<int16_t>();
+    signed_assignment<int32_t>();
+    signed_assignment<int64_t>();
+
+    unsigned_assignment<unsigned char>();
+    unsigned_assignment<unsigned short>();
+    unsigned_assignment<unsigned int>();
+    unsigned_assignment<unsigned long>();
+    unsigned_assignment<unsigned long long>();
+
+    unsigned_assignment<uint8_t>();
+    unsigned_assignment<uint16_t>();
+    unsigned_assignment<uint32_t>();
+    unsigned_assignment<uint64_t>();
+}
+
+static void
+PointerAssignmentOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    const Decimal *d = new Decimal;
+    assertTrue(d->issnan());
+
+    const Decimal *x = d;
+    assertEqual(x, d);
+
+/* CheriBSD: the operator<< overload for uintptr_t is missing (purecap mode) */
+#if !defined(__CHERI__)
+    x += 10;
+    assertEqual(reinterpret_cast<uintptr_t>(x), reinterpret_cast<uintptr_t>(d) + 10 * (sizeof *d));
+
+    x -= 10;
+    assertEqual(reinterpret_cast<uintptr_t>(x), reinterpret_cast<uintptr_t>(d));
+#endif
+
+    delete x;
+}
+
+
+/******************************************************************************/
+/*                             Comparison operators                           */
+/******************************************************************************/
+
+template<class T>
+static void
+signed_comparison()
+{
+    const T min = std::numeric_limits<T>::min();
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {min, -1, 0, 1, max};
+
+    const Decimal less = Decimal("-1000000000000000000000000000000000");
+    const Decimal less_equal = Decimal(min);
+    const Decimal greater_equal = Decimal(max);
+    const Decimal greater = Decimal("1000000000000000000000000000000000");
+
+    for (const T& v : values) {
+        assertTrue(v == Decimal(v));
+        assertTrue(Decimal(v) == v);
+        assertFalse(v != Decimal(v));
+        assertFalse(Decimal(v) != v);
+
+        assertTrue(2 != Decimal(v));
+        assertTrue(Decimal(v) != 2);
+        assertFalse(2 == Decimal(v));
+        assertFalse(Decimal(v) == 2);
+
+        assertTrue(v < greater);
+        assertTrue(less < v);
+
+        assertTrue(v <= greater_equal);
+        assertTrue(less_equal <= v);
+
+        assertTrue(v >= less_equal);
+        assertTrue(greater_equal >= v);
+
+        assertTrue(v > less);
+        assertTrue(greater > v);
+    }
+}
+
+template<class T>
+static void
+unsigned_comparison()
+{
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {0, 1, max};
+
+    Decimal less = Decimal("-1000000000000000000000000000000000");
+    Decimal less_equal = Decimal(0);
+    Decimal greater_equal = Decimal(max);
+    Decimal greater = Decimal("1000000000000000000000000000000000");
+
+    for (const T& v : values) {
+        assertTrue(v == Decimal(v));
+        assertTrue(Decimal(v) == v);
+        assertFalse(v != Decimal(v));
+        assertFalse(Decimal(v) != v);
+
+        assertTrue(2 != Decimal(v));
+        assertTrue(Decimal(v) != 2);
+        assertFalse(2 == Decimal(v));
+        assertFalse(Decimal(v) == 2);
+
+        assertTrue(v < greater);
+        assertTrue(less < v);
+
+        assertTrue(v <= greater_equal);
+        assertTrue(less_equal <= v);
+
+        assertTrue(v >= less_equal);
+        assertTrue(greater_equal >= v);
+
+        assertTrue(v > less);
+        assertTrue(greater > v);
+    }
+}
+
+static void
+ComparisonOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    context.emax(1);
+    context.emin(-1);
+    context.clear_status();
+    context.traps(DecMaxStatus);
+
+    /* integer comparisons */
+    signed_comparison<signed char>();
+    signed_comparison<short>();
+    signed_comparison<int>();
+    signed_comparison<long>();
+    signed_comparison<long long>();
+
+    signed_comparison<int8_t>();
+    signed_comparison<int16_t>();
+    signed_comparison<int32_t>();
+    signed_comparison<int64_t>();
+
+    unsigned_comparison<unsigned char>();
+    unsigned_comparison<unsigned short>();
+    unsigned_comparison<unsigned int>();
+    unsigned_comparison<unsigned long>();
+    unsigned_comparison<unsigned long long>();
+
+    unsigned_comparison<uint8_t>();
+    unsigned_comparison<uint16_t>();
+    unsigned_comparison<uint32_t>();
+    unsigned_comparison<uint64_t>();
+
+
+    /* Decimal */
+    Context ctx = MaxContext();
+    ctx.traps(DecMaxStatus);
+    assertTrue(Decimal(10).pow(1200, ctx) == Decimal(10).pow(1200, ctx));
+    assertTrue(Decimal(10).pow(1201, ctx) != Decimal(10).pow(1200, ctx));
+    assertTrue(Decimal(10).pow(1200, ctx) < Decimal(10).pow(1201, ctx));
+    assertTrue(Decimal(10).pow(1200, ctx) <= Decimal(10).pow(1200, ctx));
+    assertTrue(Decimal(10).pow(1200, ctx) >= Decimal(10).pow(1200, ctx));
+    assertTrue(Decimal(10).pow(1201, ctx) > Decimal(10).pow(1200, ctx));
+    assertEqual(ctx.status(), 0U);
+
+    /* Decimal NaN */
+    context.clear_status();
+    context.traps(DecInvalidOperation);
+
+    assertFalse(Decimal("NaN") == 2);
+    assertFalse(2 == Decimal("NaN"));
+    assertFalse(Decimal("NaN") == Decimal("NaN"));
+    assertTrue(Decimal("NaN") != Decimal("NaN"));
+    assertEqual(context.status(), 0U);
+
+    assertRaises(InvalidOperation, [](){ void(Decimal("NaN") < 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("NaN") <= 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("NaN") >= 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("NaN") > 2); });
+    assertEqual(context.status(), DecInvalidOperation);
+
+    /* Decimal sNaN */
+    context.clear_status();
+    context.traps(DecInvalidOperation);
+
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") == 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") != 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") < 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") <= 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") >= 2); });
+    assertRaises(InvalidOperation, [](){ void(Decimal("sNaN") > 2); });
+    assertEqual(context.status(), DecInvalidOperation);
+}
+
+static void
+PointerComparisonOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    const Decimal *d = new Decimal;
+    assertTrue(d->issnan());
+
+    const Decimal *x = d + 10;
+    assertFalse(x == d);
+    assertTrue(x != d);
+    assertTrue(d < x);
+    assertTrue(x <= x);
+    assertTrue(x >= d);
+    assertTrue(x > d);
+
+    delete d;
+}
+
+/******************************************************************************/
+/*                         Unary arithmetic operators                         */
+/******************************************************************************/
+
+static void
+UnaryOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    context.prec(3);
+    assertEqualStr(+Decimal(10000), "1.00E+4");
+    assertEqualStr(-Decimal(10000), "-1.00E+4");
+}
+
+static void
+PointerUnaryOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    context.prec(3);
+    Decimal *d = new Decimal(10000);
+
+    Decimal *x = +d;
+    assertEqual(x, d);
+
+    ++x;
+    assertEqual(x-1, d);
+
+    --x;
+    assertEqual(x, d);
+
+    x++;
+    assertEqual(x-1, d);
+
+    x--;
+    assertEqual(x, d);
+
+    bool b = !x;
+    assertEqual(b, false);
+
+    assertEqual(*x, 10000);
+    assertEqual(**(&x), 10000);
+
+    delete d;
+}
+
+/******************************************************************************/
+/*                         Binary arithmetic operators                        */
+/******************************************************************************/
+
+template <class T>
+static void
+signed_arithmetic()
+{
+    const T min = std::numeric_limits<T>::min();
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {min, -2, 2, max};
+
+    for (const T& v : values) {
+        assertEqual(Decimal(10) + v, Decimal(10) + Decimal(v));
+        assertEqual(v + Decimal(10), Decimal(v) + Decimal(10));
+
+        assertEqual(Decimal(27) - v, Decimal(27) - Decimal(v));
+        assertEqual(v - Decimal(27), Decimal(v) - Decimal(27));
+
+        assertEqual(Decimal(1729) * v, Decimal(1729) * Decimal(v));
+        assertEqual(v * Decimal(1729), Decimal(1729) * Decimal(v));
+
+        assertEqual(Decimal(225) / v, Decimal(225) / Decimal(v));
+        assertEqual(v / Decimal(225), Decimal(v) / Decimal(225));
+
+        assertEqual(Decimal(15222) % v, Decimal(15222) % Decimal(v));
+        assertEqual(v % Decimal(15222), Decimal(v) % Decimal(15222));
+    }
+}
+
+template <class T>
+static void
+unsigned_arithmetic()
+{
+    const T max = std::numeric_limits<T>::max();
+    const std::vector<T> values = {2, max};
+
+    for (const T& v : values) {
+        assertEqual(Decimal(10) + v, Decimal(10) + Decimal(v));
+        assertEqual(v + Decimal(10), Decimal(v) + Decimal(10));
+
+        assertEqual(Decimal(27) - v, Decimal(27) - Decimal(v));
+        assertEqual(v - Decimal(27), Decimal(v) - Decimal(27));
+
+        assertEqual(Decimal(1729) * v, Decimal(1729) * Decimal(v));
+        assertEqual(v * Decimal(1729), Decimal(1729) * Decimal(v));
+
+        assertEqual(Decimal(225) / v, Decimal(225) / Decimal(v));
+        assertEqual(v / Decimal(225), Decimal(v) / Decimal(225));
+
+        assertEqual(Decimal(15222) % v, Decimal(15222) % Decimal(v));
+        assertEqual(v % Decimal(15222), Decimal(v) % Decimal(15222));
+    }
+}
+
+static void
+ArithmeticOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    /* Decimal */
+    context.prec(9);
+    assertEqual(Decimal(20) + Decimal(2), 22);
+    assertEqual(Decimal(5) + 123456789000, Decimal(123456789000));
+    assertEqual(Decimal(22) - Decimal(2), 20);
+    assertEqual(Decimal(20) * Decimal(20), 400);
+    assertEqual(Decimal(10) / Decimal(2), 5);
+    assertEqual(Decimal(10) % Decimal(3), 1);
+
+    /* integers */
+    context.prec(1000);
+
+    signed_arithmetic<signed char>();
+    signed_arithmetic<short>();
+    signed_arithmetic<int>();
+    signed_arithmetic<long>();
+    signed_arithmetic<long long>();
+
+    signed_arithmetic<int8_t>();
+    signed_arithmetic<int16_t>();
+    signed_arithmetic<int32_t>();
+    signed_arithmetic<int64_t>();
+
+    unsigned_arithmetic<unsigned char>();
+    unsigned_arithmetic<unsigned short>();
+    unsigned_arithmetic<unsigned int>();
+    unsigned_arithmetic<unsigned long>();
+    unsigned_arithmetic<unsigned long long>();
+
+    unsigned_arithmetic<uint8_t>();
+    unsigned_arithmetic<uint16_t>();
+    unsigned_arithmetic<uint32_t>();
+    unsigned_arithmetic<uint64_t>();
+}
+
+static void
+PointerArithmeticOperatorTest()
+{
+    assertEqual(context, context_template);
+
+    const Decimal *d = new Decimal;
+    assertTrue(d->issnan());
+
+/* CheriBSD: the operator<< overload for uintptr_t is missing (purecap mode) */
+#if !defined(__CHERI__)
+    const Decimal *x = d + 10;
+    assertEqual(reinterpret_cast<uintptr_t>(x), reinterpret_cast<uintptr_t>(d) + 10 * (sizeof *d));
+
+    x = x - 10;
+    assertEqual(reinterpret_cast<uintptr_t>(x), reinterpret_cast<uintptr_t>(d));
+#endif
+
+    delete d;
+}
+
+/******************************************************************************/
+/*                                  Predicates                                */
+/******************************************************************************/
+
+static void
+PredicateTest()
+{
+    assertEqual(context, context_template);
+
+    const Decimal finite("10.1");
+    const Decimal infinite("-inf");
+    const Decimal nan("nan");
+    const Decimal snan("-snan");
+    const Decimal zero("-0");
+    const Decimal integer("9.999E+100000");
+    const Decimal subnormal = Decimal(0).next_plus();
+
+    assertTrue(finite.iscanonical());
+    assertTrue(finite.isfinite());
+    assertFalse(finite.isinfinite());
+    assertFalse(finite.isspecial());
+    assertFalse(finite.isnan());
+    assertFalse(finite.isqnan());
+    assertFalse(finite.issnan());
+    assertFalse(finite.issigned());
+    assertFalse(finite.iszero());
+    assertFalse(finite.isinteger());
+    assertTrue(finite.isnormal());
+    assertFalse(finite.issubnormal());
+
+    assertTrue(infinite.iscanonical());
+    assertFalse(infinite.isfinite());
+    assertTrue(infinite.isinfinite());
+    assertTrue(infinite.isspecial());
+    assertFalse(infinite.isnan());
+    assertFalse(infinite.isqnan());
+    assertFalse(infinite.isqnan());
+    assertTrue(infinite.issigned());
+    assertFalse(infinite.iszero());
+    assertFalse(infinite.isinteger());
+    assertFalse(infinite.isnormal());
+    assertFalse(infinite.issubnormal());
+
+    assertTrue(nan.iscanonical());
+    assertFalse(nan.isfinite());
+    assertFalse(nan.isinfinite());
+    assertTrue(nan.isspecial());
+    assertTrue(nan.isnan());
+    assertTrue(nan.isqnan());
+    assertFalse(nan.issnan());
+    assertFalse(nan.issigned());
+    assertFalse(nan.iszero());
+    assertFalse(nan.isinteger());
+    assertFalse(nan.isnormal());
+    assertFalse(nan.issubnormal());
+
+    assertTrue(snan.iscanonical());
+    assertFalse(snan.isfinite());
+    assertFalse(snan.isinfinite());
+    assertTrue(snan.isspecial());
+    assertTrue(snan.isnan());
+    assertFalse(snan.isqnan());
+    assertTrue(snan.issnan());
+    assertTrue(snan.issigned());
+    assertFalse(snan.iszero());
+    assertFalse(snan.isinteger());
+    assertFalse(snan.isnormal());
+    assertFalse(snan.issubnormal());
+
+    assertTrue(zero.iscanonical());
+    assertTrue(zero.isfinite());
+    assertFalse(zero.isinfinite());
+    assertFalse(zero.isspecial());
+    assertFalse(zero.isnan());
+    assertFalse(zero.isqnan());
+    assertFalse(zero.issnan());
+    assertTrue(zero.issigned());
+    assertTrue(zero.iszero());
+    assertTrue(zero.isinteger());
+    assertFalse(zero.isnormal());
+    assertFalse(zero.issubnormal());
+
+    assertTrue(integer.iscanonical());
+    assertTrue(integer.isfinite());
+    assertFalse(integer.isinfinite());
+    assertFalse(integer.isspecial());
+    assertFalse(integer.isnan());
+    assertFalse(integer.isqnan());
+    assertFalse(integer.issnan());
+    assertFalse(integer.issigned());
+    assertFalse(integer.iszero());
+    assertTrue(integer.isinteger());
+    assertTrue(integer.isnormal());
+    assertFalse(integer.issubnormal());
+
+    assertTrue(subnormal.iscanonical());
+    assertTrue(subnormal.isfinite());
+    assertFalse(subnormal.isinfinite());
+    assertFalse(subnormal.isspecial());
+    assertFalse(subnormal.isnan());
+    assertFalse(subnormal.isqnan());
+    assertFalse(subnormal.issnan());
+    assertFalse(subnormal.issigned());
+    assertFalse(subnormal.iszero());
+    assertFalse(subnormal.isinteger());
+    assertFalse(subnormal.isnormal());
+    assertTrue(subnormal.issubnormal());
+
+    Context ctx{1, 1, -1};
+    Decimal sub = Decimal("0.00000001");
+    assertFalse(sub.isnormal(ctx));
+    assertTrue(sub.issubnormal(ctx));
+}
+
+/******************************************************************************/
+/*                              Unary functions                               */
+/******************************************************************************/
+
+static void
+UnaryFunctionTest()
+{
+    assertEqual(context, context_template);
+
+    /* Unary functions, no context arg */
+    Decimal x = Decimal("-123.113E+25100");
+
+    assertEqual(x.adjexp(), 25102);
+    assertEqual(Decimal("1234e9999").adjexp(), 10002);
+
+    assertRaises(ValueError, [](){ Decimal("nan").adjexp(); });
+    assertRaises(ValueError, [](){ Decimal("inf").adjexp(); });
+
+    assertEqual(Decimal::radix(), 10);
+
+    assertEqual(x.canonical(), x);
+
+    x = Decimal(123456789);
+    assertEqual(x.copy(), x);
+
+    for (const auto& s : large_prec) {
+        x = Decimal(s);
+        assertEqualStr(x.copy(), x);
+    }
+
+    x = Decimal(-123456789);
+    assertEqual(x.copy_abs(), 123456789);
+
+    x = Decimal(123456789);
+    assertEqual(x.copy_negate(), -123456789);
+
+    context.prec(10000);
+    auto large = -Decimal(1172).pow(1712);
+    assertEqual(large.copy_abs(), -large);
+    assertEqual(large.copy_negate(), -large);
+    assertEqual(large.copy_sign(1), -large);
+    context.prec(9);
+
+    /* Unary functions, optional context arg */
+    Context ctx{1, 1, -1};
+
+    assertEqualStr(Decimal("-0.00001").number_class(), "-Normal");
+    assertEqualStr(Decimal("-0.00001").number_class(ctx), "-Subnormal");
+
+    assertEqual(Decimal(-123456789).abs(), 123456789);
+    assertEqual(Decimal(-15).abs(ctx), Decimal("2E+1"));
+
+    assertEqual(Decimal(-1123).exp(), Decimal("1.93774588E-488"));
+    assertEqual(Decimal(2).exp(ctx), 7);
+
+    assertEqual(Decimal(1123).invroot(), Decimal("0.0298407766"));
+    assertEqual(Decimal(7).invroot(ctx), Decimal("0.4"));
+
+    assertEqual(Decimal(0).logical_invert(), Decimal("111111111"));
+    assertEqual(Decimal(0).logical_invert(ctx), Decimal("1"));
+
+    assertEqual(Decimal(10).ln(), Decimal("2.30258509"));
+    assertEqual(Decimal(10).ln(ctx), Decimal("2"));
+
+    assertEqual(Decimal(20).log10(), Decimal("1.30103000"));
+    assertEqual(Decimal(20).log10(ctx), Decimal("1"));
+
+    assertEqual(Decimal("2250000000000000000000000000").logb(), Decimal("27"));
+    assertEqual(Decimal("2250000000000000000000000000").logb(ctx), Decimal("3E+1"));
+
+    assertEqual(Decimal("2250000000000000000000000000").minus(), Decimal("-2.25000000E+27"));
+    assertEqual(Decimal("25").minus(ctx), Decimal("-2E+1"));
+
+    assertEqual(Decimal("2250000000000000000000000000").next_minus(), Decimal("2.24999999E+27"));
+    assertEqual(Decimal("2250000000000000000000000000").next_minus(ctx), Decimal("9E+1"));
+
+    assertEqual(Decimal("2250000000000000000000000000").plus(), Decimal("2.25000000E+27"));
+    assertEqual(Decimal("28").plus(ctx), Decimal("3E+1"));
+
+    assertEqual(Decimal("2250000000000000000000000000").reduce(), Decimal("2.25E+27"));
+    assertEqual(Decimal("-28").reduce(ctx), Decimal("-3E+1"));
+
+    assertEqual(Decimal("22500000.12").to_integral(), Decimal("22500000"));
+    assertEqual(Decimal("22500000.12").to_integral(ctx), Decimal("22500000"));
+
+    assertEqual(Decimal("22500000.12").to_integral_exact(), Decimal("22500000"));
+    assertEqual(Decimal("22500000.12").to_integral_exact(ctx), Decimal("22500000"));
+
+    assertEqual(Decimal("90025").sqrt(), Decimal("300.041664"));
+    assertEqual(Decimal("99").sqrt(ctx), Decimal("1E+1"));
+
+    ctx.round(ROUND_UP);
+    x = Decimal("99999999999999999999999999.9").to_integral(ctx);
+    assertEqual(x, Decimal("100000000000000000000000000"));
+
+    x = Decimal("99999999999999999999999999.9").to_integral_exact(ctx);
+    assertEqual(x, Decimal("100000000000000000000000000"));
+
+    ctx.add_traps(DecInexact);
+    assertRaises(Inexact, [&](){ Decimal("999.9").to_integral_exact(ctx); });
+}
+
+static void
+CeilTest()
+{
+    assertEqual(context, context_template);
+
+    context.traps(DecMaxStatus);
+
+    const std::vector<std::pair<const char *, long long>> pairs = {
+        {"123.00", 123},
+        {"3.2", 4},
+        {"3.54", 4},
+        {"3.899", 4},
+        {"-2.3", -2},
+        {"-11.0", -11},
+        {"0.0", 0},
+        {"-0E3", 0},
+        {"898211712379812736.1", 898211712379812737LL},
+    };
+
+    for (auto& p : pairs) {
+        const char *s = p.first;
+        long long i = p.second;
+
+        assertEqual(Decimal(s).ceil(), i);
+    }
+
+    context.status(0);
+    assertRaises(InvalidOperation, [](){ Decimal("NaN").ceil(); });
+    assertRaises(InvalidOperation, [](){ Decimal("NaN123").ceil(); });
+    assertRaises(InvalidOperation, [](){ Decimal("Inf").ceil(); });
+    assertRaises(InvalidOperation, [](){ Decimal("-Inf").ceil(); });
+    assertRaises(InvalidOperation, [](){ Decimal("sNaN").ceil(); });
+    assertEqual(context.status(), DecInvalidOperation);
+}
+
+static void
+FloorTest()
+{
+    assertEqual(context, context_template);
+
+    context.traps(DecMaxStatus);
+
+    const std::vector<std::pair<const char *, long long>> pairs = {
+        {"123.00", 123},
+        {"3.2", 3},
+        {"3.54", 3},
+        {"3.899", 3},
+        {"-2.3", -3},
+        {"-11.0", -11},
+        {"0.0", 0},
+        {"-0E3", 0},
+        {"898211712379812736.1", 898211712379812736LL},
+    };
+
+    for (auto& p : pairs) {
+        const char *s = p.first;
+        long long i = p.second;
+
+        assertEqual(Decimal(s).floor(), i);
+    }
+
+    context.status(0);
+    assertRaises(InvalidOperation, [](){ Decimal("NaN").floor(); });
+    assertRaises(InvalidOperation, [](){ Decimal("NaN123").floor(); });
+    assertRaises(InvalidOperation, [](){ Decimal("Inf").floor(); });
+    assertRaises(InvalidOperation, [](){ Decimal("-Inf").floor(); });
+    assertRaises(InvalidOperation, [](){ Decimal("sNaN").floor(); });
+    assertEqual(context.status(), DecInvalidOperation);
+}
+
+static void
+TruncTest()
+{
+    assertEqual(context, context_template);
+
+    char buf[10];
+    context.traps(DecMaxStatus);
+
+    const std::vector<std::pair<const char *, long long>> pairs = {
+        {"123.00", 123},
+        {"3.2", 3},
+        {"3.54", 3},
+        {"3.899", 3},
+        {"-2.3", -2},
+        {"-11.0", -11},
+        {"-11.8", -11},
+        {"0.0", 0},
+        {"-0E3", 0},
+        {"898211712379812736.1", 898211712379812736LL},
+    };
+
+    for (auto& p : pairs) {
+        const char *s = p.first;
+        long long i = p.second;
+
+        assertEqual(Decimal(s).trunc(), i);
+    }
+
+    context.round(ROUND_DOWN);
+    for (int i = -250; i < 250; i++) {
+        int n = snprintf(buf, sizeof buf, "%0.2f", i / 100.0);
+        assertTrue(n >= 0 && n < 10);
+        Decimal d = Decimal(static_cast<const char *>(buf));
+        Decimal r = d.to_integral();
+        assertEqual(d.trunc(), r);
+    }
+
+    context.status(0);
+    assertRaises(InvalidOperation, [](){ Decimal("NaN").trunc(); });
+    assertRaises(InvalidOperation, [](){ Decimal("NaN123").trunc(); });
+    assertRaises(InvalidOperation, [](){ Decimal("Inf").trunc(); });
+    assertRaises(InvalidOperation, [](){ Decimal("-Inf").trunc(); });
+    assertRaises(InvalidOperation, [](){ Decimal("sNaN").trunc(); });
+    assertEqual(context.status(), DecInvalidOperation);
+}
+
+/******************************************************************************/
+/*                              Binary functions                              */
+/******************************************************************************/
+
+static void
+BinaryFunctionTest()
+{
+    assertEqual(context, context_template);
+
+    /* Binary functions, no context arg */
+    assertEqual(Decimal("123").compare_total(Decimal("sNaN")), Decimal("-1"));
+    assertEqual(Decimal("-123").compare_total_mag(Decimal("10")), Decimal("1"));
+
+    /* Binary functions */
+    Context ctx{3, 100, -100};
+    ctx.round(ROUND_UP);
+    ctx.clamp(1);
+    ctx.traps(DecIEEEInvalidOperation);
+
+    assertEqualStr(Decimal(1234567890).add(Decimal("-9988888.23")), "1.22457900E+9");
+    assertEqualStr(Decimal(1234567890).add(Decimal("-9988888.23"), ctx), "1.23E+9");
+
+    assertEqualStr(Decimal(1234567890).div(Decimal("-9988888.23")), "-123.594124");
+    assertEqualStr(Decimal(1234567890).div(Decimal("-9988888.23"), ctx), "-124");
+
+    assertEqualStr(Decimal(1234567890).divint(Decimal("-9988888.23")), "-123");
+    assertEqualStr(Decimal(1234567890).divint(Decimal("-9988888.23"), ctx), "-123");
+
+    assertEqualStr(Decimal(1234567890).compare(Decimal("-9988888.23")), 1);
+    assertEqualStr(Decimal(1234567890).compare(Decimal("-9988888.23"), ctx), 1);
+
+    assertEqualStr(Decimal(1234567890).compare_signal(Decimal("-9988888.23")), 1);
+    assertEqualStr(Decimal(1234567890).compare_signal(Decimal("-9988888.23"), ctx), 1);
+
+    assertEqualStr(Decimal("101111").logical_and(Decimal("110001101")), "1101");
+    assertEqualStr(Decimal("101111").logical_and(Decimal("110001101"), ctx), "101");
+
+    assertEqualStr(Decimal("101111").logical_or(Decimal("110001101")), "110101111");
+    assertEqualStr(Decimal("101111").logical_or(Decimal("110001101"), ctx), "111");
+
+    assertEqualStr(Decimal("101111").logical_xor(Decimal("110001101")), "110100010");
+    assertEqualStr(Decimal("101111").logical_xor(Decimal("110001101"), ctx), "10");
+
+    assertEqualStr(Decimal(1234567890).max(Decimal("-9988888.23")), "1.23456789E+9");
+    assertEqualStr(Decimal(1234567890).max(Decimal("-9988888.23"), ctx), "1.24E+9");
+
+    assertEqualStr(Decimal(1234567890).max_mag(Decimal("-9988888.23")), "1.23456789E+9");
+    assertEqualStr(Decimal(1234567890).max_mag(Decimal("-9988888.23"), ctx), "1.24E+9");
+
+    assertEqualStr(Decimal(1234567890).min(Decimal("-9988888.23")), "-9988888.23");
+    assertEqualStr(Decimal(1234567890).min(Decimal("-9988888.23"), ctx), "-9.99E+6");
+
+    assertEqualStr(Decimal(1234567890).min_mag(Decimal("-9988888.23")), "-9988888.23");
+    assertEqualStr(Decimal(1234567890).min_mag(Decimal("-9988888.23"), ctx), "-9.99E+6");
+
+    assertEqualStr(Decimal(1234567890).mul(Decimal("-9988888.23")), "-1.23319607E+16");
+    assertEqualStr(Decimal(1234567890).mul(Decimal("-9988888.23"), ctx), "-1.24E+16");
+
+    assertEqualStr(Decimal(1234567890).next_toward(Decimal("-9988888.23")), "1.23456788E+9");
+    assertEqualStr(Decimal(1234567890).next_toward(Decimal("-9988888.23"), ctx), "1.23E+9");
+
+    assertEqualStr(Decimal(1234567890).pow(Decimal("-9988888.23")), "0E-1000007");
+    assertEqualStr(Decimal(1234567890).pow(Decimal("-9988888.23"), ctx), "1E-102");
+    ctx.clear_status();
+    assertEqual(Decimal("1.0").pow(100, ctx), Decimal("1.00"));
+    assertTrue(ctx.status() & DecRounded);
+    ctx.clear_status();
+    ctx.emax(1);
+    ctx.emin(-1);
+    assertEqual(Decimal(10000).pow(Decimal("0.5"), ctx), Decimal("inf"));
+    assertTrue(ctx.status() & DecOverflow);
+    ctx.emax(100);
+    ctx.emin(-100);
+
+    assertEqualStr(Decimal(1234567890).rem(Decimal("-9988888.23")), "5934637.71");
+    assertEqualStr(Decimal(1234567890).rem(Decimal("-9988888.23"), ctx), "5.94E+6");
+
+    assertEqualStr(Decimal(1234567890).rem_near(Decimal("-9988888.23")), "-4054250.52");
+    assertEqualStr(Decimal(1234567890).rem_near(Decimal("-9988888.23"), ctx), "-4.06E+6");
+
+    assertEqualStr(Decimal(1234567890).rotate(2), "456789023");
+    assertEqualStr(Decimal(1234567890).rotate(2, ctx), "89");
+
+    assertEqualStr(Decimal(20).quantize(Decimal("1E-2")), "20.00");
+    assertRaises(InvalidOperation, [&](){ Decimal(20).quantize(Decimal("1E-2"), ctx); });
+    assertEqualStr(Decimal(20).quantize(Decimal("1E-1"), ctx), "20.0");
+
+    assertEqualStr(Decimal(20).scaleb(Decimal("100")), "2.0E+101");
+    ctx.add_traps(DecOverflow);
+    assertRaises(Overflow, [&](){ Decimal(20).scaleb(Decimal("100"), ctx); });
+    assertEqualStr(Decimal(20).scaleb(Decimal("10"), ctx), "2.0E+11");
+
+    assertEqualStr(Decimal(1).shift(8), Decimal("100000000"));
+    assertRaises(InvalidOperation, [&](){ Decimal(1).shift(8, ctx); });
+    assertEqualStr(Decimal(1).shift(2, ctx), 100);
+
+    assertEqualStr(Decimal(1234567890).sub(Decimal("-9988888.23")), "1.24455678E+9");
+    assertEqualStr(Decimal(1234567890).sub(Decimal("-9988888.23"), ctx), "1.25E+9");
+}
+
+/******************************************************************************/
+/*                                   Divmod                                   */
+/******************************************************************************/
+
+static void
+DivmodTest()
+{
+    assertEqual(context, context_template);
+
+    /* Binary functions */
+    Context ctx{3, 100, -100};
+    ctx.round(ROUND_UP);
+    ctx.clamp(1);
+    ctx.traps(DecIEEEInvalidOperation);
+
+    auto result = Decimal("1234567890").divmod(12716);
+    assertEqual(result.first, 97087);
+    assertEqual(result.second, 9598);
+
+    assertRaises(DivisionImpossible, [&](){ Decimal("1234567890").divmod(12716, ctx); });
+
+    result = Decimal("1234567890").divmod(12716172);
+    assertEqualStr(result.first, "97");
+    assertEqualStr(result.second, "1099206");
+
+    result = Decimal("10912837129").divmod(1001);
+    assertEqual(result.first, Decimal("10901935"));
+    assertEqual(result.second, Decimal("194"));
+
+    result = Decimal("NaN").divmod(7);
+    assertTrue(result.first.isqnan());
+    assertTrue(result.second.isqnan());
+
+    context.clear_traps();
+    context.clear_status();
+    result = Decimal("inf").divmod(Decimal("inf"));
+    assertTrue(result.first.isqnan());
+    assertTrue(result.second.isqnan());
+    assertTrue(context.status() & DecInvalidOperation);
+
+    context.clear_status();
+    result = Decimal("inf").divmod(Decimal("101"));
+    assertTrue(result.first.isinfinite());
+    assertTrue(result.second.isqnan());
+    assertTrue(context.status() & DecInvalidOperation);
+
+    context.clear_status();
+    result = Decimal("0").divmod(Decimal("0"));
+    assertTrue(result.first.isqnan());
+    assertTrue(result.second.isqnan());
+    assertTrue(context.status() & DecDivisionUndefined);
+
+    context.clear_status();
+    result = Decimal("11").divmod(Decimal("0"));
+    assertTrue(result.first.isinfinite());
+    assertTrue(result.second.isqnan());
+    assertTrue(context.status() & DecInvalidOperation);
+    assertTrue(context.status() & DecDivisionByZero);
+}
+
+/******************************************************************************/
+/*                                  Quantize                                  */
+/******************************************************************************/
+
+static void
+QuantizeTest()
+{
+    assertEqual(context, context_template);
+
+    context.clear_status();
+
+    Context c = Context(9, 99999, -99999, ROUND_DOWN);
+    assertEqual(Decimal("7.335").quantize(Decimal(".01")), Decimal("7.34"));
+    assertEqual(Decimal("7.335").quantize(Decimal(".01"), c), Decimal("7.33"));
+
+    c = Context(28, 99999, -99999);
+    c.traps(DecInvalidOperation);
+    assertRaises(InvalidOperation, [&](){ Decimal("10e99999").quantize(Decimal("1e100000"), c); });
+
+    c = Context();
+    c.round(ROUND_DOWN);
+    Decimal d = Decimal("0.871831e800");
+    Decimal x = d.quantize(Decimal("1e797"), c);
+    assertEqual(x, Decimal("8.71E+799"));
+}
+
+/******************************************************************************/
+/*                              Ternary functions                             */
+/******************************************************************************/
+
+static void
+TernaryFunctionTest()
+{
+    assertEqual(context, context_template);
+
+    Context ctx{3, 100, -100};
+    ctx.round(ROUND_UP);
+    ctx.clamp(1);
+    ctx.traps(DecIEEEInvalidOperation);
+
+    assertEqual(Decimal("1234").fma(919, Decimal("3.2507355")), Decimal("1134049.25"));
+    assertEqual(Decimal("1234").fma(919, Decimal("3.2507355"), ctx), Decimal("1.14E+6"));
+
+    assertEqual(Decimal("1234").powmod(919, Decimal("123456789")), Decimal("119347714"));
+    assertRaises(InvalidOperation, [&](){ Decimal("1234").powmod(919, Decimal("123456789"), ctx); });
+    assertEqual(Decimal("1234").powmod(919, Decimal("996")), Decimal("892"));
+
+    ctx.prec(2);
+    assertRaises(InvalidOperation, [&](){ Decimal("1000").powmod(1, Decimal("501"), ctx); });
+}
+
+/******************************************************************************/
+/*                             Irregular functions                            */
+/******************************************************************************/
+
+static void
+IrregularFunctionTest()
+{
+    assertEqual(context, context_template);
+
+    Context ctx{3, 100, -100};
+    ctx.round(ROUND_UP);
+    ctx.clamp(1);
+    ctx.traps(DecIEEEInvalidOperation);
+
+    assertEqual(Decimal("1").cmp(10), -1);
+    assertEqual(Decimal("10").cmp(10), 0);
+    assertEqual(Decimal("10").cmp(1), 1);
+    assertEqual(Decimal("1").cmp(Decimal("inf")), -1);
+    assertEqual(Decimal("1").cmp(Decimal("-inf")), 1);
+    assertEqual(Decimal("inf").cmp(Decimal("1")), 1);
+    assertEqual(Decimal("-inf").cmp(Decimal("1")), -1);
+    assertEqual(Decimal("-inf").cmp(Decimal("inf")), -1);
+    assertEqual(Decimal("inf").cmp(Decimal("-inf")), 1);
+    assertEqual(Decimal("10").cmp(Decimal("nan")), INT_MAX);
+    assertEqual(Decimal("inf").cmp(Decimal("nan")), INT_MAX);
+    assertEqual(Decimal("nan").cmp(Decimal("nan")), INT_MAX);
+    assertEqual(Decimal("nan").cmp(Decimal("snan")), INT_MAX);
+    assertEqual(Decimal("snan").cmp(Decimal("snan")), INT_MAX);
+
+    assertEqual(Decimal("1").cmp_total(10), -1);
+    assertEqual(Decimal("10").cmp_total(10), 0);
+    assertEqual(Decimal("10").cmp_total(1), 1);
+    assertEqual(Decimal("1").cmp_total(Decimal("inf")), -1);
+    assertEqual(Decimal("1").cmp_total(Decimal("-inf")), 1);
+    assertEqual(Decimal("inf").cmp_total(Decimal("1")), 1);
+    assertEqual(Decimal("-inf").cmp_total(Decimal("1")), -1);
+    assertEqual(Decimal("-inf").cmp_total(Decimal("inf")), -1);
+    assertEqual(Decimal("inf").cmp_total(Decimal("-inf")), 1);
+    assertEqual(Decimal("10").cmp_total(Decimal("nan")), -1);
+    assertEqual(Decimal("inf").cmp_total(Decimal("nan")), -1);
+    assertEqual(Decimal("nan").cmp_total(Decimal("inf")), 1);
+    assertEqual(Decimal("nan").cmp_total(Decimal("nan")), 0);
+    assertEqual(Decimal("nan").cmp_total(Decimal("snan")), 1);
+    assertEqual(Decimal("snan").cmp_total(Decimal("snan")), 0);
+
+    assertEqual(Decimal("1").compare_total_mag(10), -1);
+    assertEqual(Decimal("-10").compare_total_mag(1), 1);
+    assertEqual(Decimal("10").compare_total_mag(1), 1);
+    assertEqual(Decimal("1").compare_total_mag(Decimal("inf")), -1);
+    assertEqual(Decimal("1").compare_total_mag(Decimal("-inf")), -1);
+    assertEqual(Decimal("inf").compare_total_mag(Decimal("1")), 1);
+    assertEqual(Decimal("-inf").compare_total_mag(Decimal("1")), 1);
+    assertEqual(Decimal("-inf").compare_total_mag(Decimal("inf")), 0);
+    assertEqual(Decimal("inf").compare_total_mag(Decimal("-inf")), 0);
+    assertEqual(Decimal("10").compare_total_mag(Decimal("nan")), -1);
+    assertEqual(Decimal("inf").compare_total_mag(Decimal("nan")), -1);
+    assertEqual(Decimal("nan").compare_total_mag(Decimal("inf")), 1);
+    assertEqual(Decimal("nan").compare_total_mag(Decimal("nan")), 0);
+    assertEqual(Decimal("nan").compare_total_mag(Decimal("snan")), 1);
+    assertEqual(Decimal("snan").compare_total_mag(Decimal("snan")), 0);
+
+    assertEqual(Decimal("10").copy_sign(-1), -10);
+
+    ctx.round(ROUND_DOWN);
+    assertEqual(Decimal("123456785").rescale(1, ctx), Decimal("1.2345678E+8"));
+    ctx.round(ROUND_UP);
+    assertEqual(Decimal("123456785").rescale(1, ctx), Decimal("1.2345679E+8"));
+
+    assertTrue(Decimal("123456785").same_quantum(10));
+    assertFalse(Decimal("123456785").same_quantum(Decimal("1E+1")));
+
+    assertEqual(Decimal("1").shiftn(8), Decimal("1E+8"));
+    assertRaises(InvalidOperation, [&](){ Decimal("1").shiftn(8, ctx); });
+    assertEqual(Decimal("1000").shiftn(-1), Decimal("100"));
+
+    assertEqual(Decimal("1").shiftl(100), Decimal("1E+100"));
+    assertEqual(Decimal("1").shiftl(100, ctx), Decimal("1E+100"));
+    assertRaises(ValueError, [](){ Decimal("NaN").shiftl(100); });
+    assertRaises(ValueError, [](){ Decimal("sNaN").shiftl(100); });
+    assertRaises(ValueError, [](){ Decimal("Infinity").shiftl(100); });
+
+    assertEqual(Decimal("1000000000000").shiftr(1), Decimal("100000000000"));
+    assertEqual(Decimal("1000000000000").shiftr(1, ctx), Decimal("100000000000"));
+    assertRaises(ValueError, [](){ Decimal("NaN").shiftl(100); });
+    assertRaises(ValueError, [](){ Decimal("sNaN").shiftl(100); });
+    assertRaises(ValueError, [](){ Decimal("Infinity").shiftl(100); });
+
+    assertEqual(Decimal::ln10(9), Decimal("2.30258509"));
+}
+
+/******************************************************************************/
+/*                                   Apply                                    */
+/******************************************************************************/
+
+static void
+ApplyTest()
+{
+    assertEqual(context, context_template);
+
+    context.prec(2);
+
+    Decimal d = Decimal("1.234E+200");
+    assertEqual(+d, Decimal("1.2E+200"));
+    assertEqual(d.apply(context), Decimal("1.2E+200"));
+    assertEqual(Decimal("1.2E+200", context), Decimal("1.2E+200"));
+
+    d = Decimal("-0");
+    assertEqualStr(+d, "0");
+    assertEqualStr(d.apply(context), "-0");
+    assertEqualStr(Decimal("-0", context), "-0");
+
+    d = Decimal("NaN0123456789");
+    assertEqualStr(+d, "NaN89");
+    assertEqualStr(d.apply(context), "NaN89");
+    assertEqualStr(Decimal(d, context), "NaN");
+
+    context.add_traps(DecIEEEInvalidOperation);
+    assertRaises(ConversionSyntax, [&](){ Decimal(d, context); });
+}
+
+/******************************************************************************/
+/*                             Integer conversion                             */
+/******************************************************************************/
+
+static void
+IntegerConversionTest()
+{
+    assertEqual(context, context_template);
+
+    assertEqual(Decimal(INT64_MIN).i64(), INT64_MIN);
+    assertEqual(Decimal(INT64_MAX).i64(), INT64_MAX);
+    assertEqual(Decimal("1E+3").i64(), 1000);
+    assertRaises(ValueError, [](){ Decimal("1E+20").i64(); });
+    assertRaises(ValueError, [](){ Decimal("-1E+20").i64(); });
+    assertRaises(ValueError, [](){ Decimal("1E-20").i64(); });
+    assertRaises(ValueError, [](){ Decimal("nan").i64(); });
+    assertRaises(ValueError, [](){ Decimal("inf").i64(); });
+
+    assertEqual(Decimal(INT32_MIN).i32(), INT32_MIN);
+    assertEqual(Decimal(INT32_MAX).i32(), INT32_MAX);
+    assertEqual(Decimal("1E+3").i32(), 1000);
+    assertRaises(ValueError, [](){ Decimal("1E+11").i32(); });
+    assertRaises(ValueError, [](){ Decimal("-1E+11").i32(); });
+    assertRaises(ValueError, [](){ Decimal("1E-11").i32(); });
+    assertRaises(ValueError, [](){ Decimal("nan").i32(); });
+    assertRaises(ValueError, [](){ Decimal("inf").i32(); });
+
+    assertEqual(Decimal(UINT64_MAX).u64(), UINT64_MAX);
+    assertEqual(Decimal("1E+3").u64(), 1000U);
+    assertRaises(ValueError, [](){ Decimal("-1").u64(); });
+    assertRaises(ValueError, [](){ Decimal("1E+20").u64(); });
+    assertRaises(ValueError, [](){ Decimal("-1E+20").u64(); });
+    assertRaises(ValueError, [](){ Decimal("1E-20").u64(); });
+    assertRaises(ValueError, [](){ Decimal("nan").u64(); });
+    assertRaises(ValueError, [](){ Decimal("inf").u64(); });
+
+    assertEqual(Decimal(UINT32_MAX).u32(), UINT32_MAX);
+    assertEqual(Decimal("1E+3").u32(), 1000U);
+    assertRaises(ValueError, [](){ Decimal("-1").u32(); });
+    assertRaises(ValueError, [](){ Decimal("1E+11").u32(); });
+    assertRaises(ValueError, [](){ Decimal("-1E+11").u32(); });
+    assertRaises(ValueError, [](){ Decimal("1E-11").u32(); });
+    assertRaises(ValueError, [](){ Decimal("nan").u32(); });
+    assertRaises(ValueError, [](){ Decimal("inf").u32(); });
+}
+
+/******************************************************************************/
+/*                              Exact arithmetic                              */
+/******************************************************************************/
+
+static void
+ExactArithTest()
+{
+    assertEqual(context, context_template);
+
+    context = MaxContext();
+
+    assertEqual(Decimal(0).exp(), 1);
+    assertEqual(Decimal(1).ln(), 0);
+    assertEqual(Decimal(1).log10(), 0);
+    assertEqual(Decimal(100).log10(), 2);
+    assertEqual(Decimal("1E+223").log10(), 223);
+    assertEqual(Decimal("1E+19").logb(), 19);
+    assertEqual(Decimal(4).sqrt(), 2);
+    assertEqual(Decimal("40E+9").sqrt(), Decimal("2.0E+5"));
+    assertEqual(Decimal(10).divint(3), 3);
+    assertEqual(Decimal(4) / 2, 2);
+    assertEqual(Decimal(400).pow(-1), Decimal("0.0025"));
+}
+
+/******************************************************************************/
+/*                        Containers and data structures                      */
+/******************************************************************************/
+
+static void
+DataStructuresTest(void)
+{
+    assertEqual(context, context_template);
+
+    /* array */
+    Decimal *x = new Decimal[10]();
+
+    for (size_t i = 0; i < 10; i++) {
+        assertTrue(x[i].issnan());
+        x[i] = i;
+    }
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(x[i], i);
+    }
+
+    delete[] x;
+
+    /* array of pointers */
+    Decimal **y = new Decimal *[10];
+
+    for (size_t i = 0; i < 10; i++) {
+        y[i] = new Decimal();
+        assertTrue((*y[i]).issnan());
+        *y[i] = i;
+    }
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(*y[i], i);
+    }
+    for (size_t i = 0; i < 10; i++) {
+        delete y[i];
+    }
+
+    delete[] y;
+
+    /* linked list */
+    std::list<Decimal> lst;
+
+    for (size_t i = 0; i < 10; i++) {
+        lst.push_front(Decimal(i));
+    }
+    lst.sort();
+    size_t n = 0;
+    for (const Decimal& d : lst) {
+        assertEqual(d, n++);
+    }
+
+    /* map */
+    std::map<const char *, Decimal> map1;
+    std::vector<const char *> keys = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+    for (auto& k : keys) {
+        map1[k] = Decimal(k);
+    }
+    for (auto& k : keys) {
+        assertEqual(map1.at(k), Decimal(k));
+    }
+
+    /* map */
+    std::map<Decimal, const char *> map2;
+    std::vector<const char *> values = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+    for (auto& v : values) {
+        map2[Decimal(v)] = v;
+    }
+
+    for (auto& v : values) {
+        assertEqual(map2.at(Decimal(v)), v);
+    }
+}
+
+static void
+LargeDataStructuresTest(void)
+{
+    assertEqual(context, context_template);
+
+    std::vector<Decimal> powers(10);
+
+    context.prec(10000);
+    context.traps(DecMaxStatus);
+
+    for (size_t i = 0; i < 10; i++) {
+        powers[i] = Decimal(i).pow(2100);
+    }
+
+    /* array */
+    Decimal *x = new Decimal[10]();
+
+    for (size_t i = 0; i < 10; i++) {
+        assertTrue(x[i].issnan());
+        x[i] = powers[i];
+    }
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(x[i], powers[i]);
+    }
+
+    delete[] x;
+
+    /* array of pointers */
+    Decimal **y = new Decimal *[10];
+
+    for (size_t i = 0; i < 10; i++) {
+        y[i] = new Decimal();
+        assertTrue((*y[i]).issnan());
+        *y[i] = powers[i];
+    }
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(*y[i], powers[i]);
+    }
+    for (size_t i = 0; i < 10; i++) {
+        delete y[i];
+    }
+
+    delete[] y;
+
+    /* linked list */
+    std::list<Decimal> lst;
+
+    for (size_t i = 0; i < 10; i++) {
+        lst.push_front(powers[i]);
+    }
+    lst.sort();
+    size_t n = 0;
+    for (const Decimal& d : lst) {
+        assertEqual(d, powers[n++]);
+    }
+
+    /* map */
+    std::map<const char *, Decimal> map1;
+    std::vector<const char *> keys = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+    for (size_t i = 0; i < 10; i++) {
+        map1[keys[i]] = powers[i];
+    }
+
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(map1.at(keys[i]), powers[i]);
+    }
+
+    std::map<Decimal, Decimal> map2;
+    std::vector<const char *> values = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+    for (size_t i = 0; i < 10; i++) {
+        map2[Decimal(values[i])] = powers[i];
+    }
+
+    for (size_t i = 0; i < 10; i++) {
+        assertEqual(map2.at(Decimal(values[i])), powers[i]);
+    }
+}
+
+/******************************************************************************/
+/*                               String and repr                              */
+/******************************************************************************/
+
+static void
+StringReprTest()
+{
+    assertEqual(context, context_template);
+
+    context.traps(DecMaxStatus);
+
+    std::stringstream ss;
+
+    Decimal d = Decimal("225E+1000");
+
+    ss << d;
+    assertEqual(ss.str(), "2.25E+1002");
+
+    assertEqual(d.repr(), "Decimal(\"2.25E+1002\")");
+    assertEqual(d.repr(true), "Decimal(\"2.25E+1002\")");
+    assertEqual(d.repr(false), "Decimal(\"2.25e+1002\")");
+
+    assertEqual(context.status(), 0U);
+}
+
+/******************************************************************************/
+/*                                   Format                                   */
+/******************************************************************************/
+
+static void
+FormatTest()
+{
+    assertEqual(context, context_template);
+
+    const std::vector<std::vector<const char *>> test_cases = {
+        {"e", "0E-15", "0e-15"},
+        {"e", "2.3E-15", "2.3e-15"},
+        {"e", "2.30E+2", "2.30e+2"},
+        {"e", "2.30000E-15", "2.30000e-15"},
+        {"e", "1.23456789123456789e40", "1.23456789123456789e+40"},
+        {"e", "1.5", "1.5e+0"},
+        {"e", "0.15", "1.5e-1"},
+        {"e", "0.015", "1.5e-2"},
+        {"e", "0.0000000000015", "1.5e-12"},
+        {"e", "15.0", "1.50e+1"},
+        {"e", "-15", "-1.5e+1"},
+        {"e", "0", "0e+0"},
+        {"e", "0E1", "0e+1"},
+        {"e", "0.0", "0e-1"},
+        {"e", "0.00", "0e-2"},
+        {".6e", "0E-15", "0.000000e-9"},
+        {".6e", "0", "0.000000e+6"},
+        {".6e", "9.999999", "9.999999e+0"},
+        {".6e", "9.9999999", "1.000000e+1"},
+        {".6e", "-1.23e5", "-1.230000e+5"},
+        {".6e", "1.23456789e-3", "1.234568e-3"},
+        {"f", "0", "0"},
+        {"f", "0.0", "0.0"},
+        {"f", "0E-2", "0.00"},
+        {"f", "0.00E-8", "0.0000000000"},
+        {"f", "0E1", "0"},
+        {"f", "3.2E1", "32"},
+        {"f", "3.2E2", "320"},
+        {"f", "3.20E2", "320"},
+        {"f", "3.200E2", "320.0"},
+        {"f", "3.2E-6", "0.0000032"},
+        {".6f", "0E-15", "0.000000"},
+        {".6f", "0E1", "0.000000"},
+        {".6f", "0", "0.000000"},
+        {".0f", "0", "0"},
+        {".0f", "0e-2", "0"},
+        {".0f", "3.14159265", "3"},
+        {".1f", "3.14159265", "3.1"},
+        {".4f", "3.14159265", "3.1416"},
+        {".6f", "3.14159265", "3.141593"},
+        {".7f", "3.14159265", "3.1415926"},
+        {".8f", "3.14159265", "3.14159265"},
+        {".9f", "3.14159265", "3.141592650"},
+
+        {"g", "0", "0"},
+        {"g", "0.0", "0.0"},
+        {"g", "0E1", "0e+1"},
+        {"G", "0E1", "0E+1"},
+        {"g", "0E-5", "0.00000"},
+        {"g", "0E-6", "0.000000"},
+        {"g", "0E-7", "0e-7"},
+        {"g", "-0E2", "-0e+2"},
+        {".0g", "3.14159265", "3"},
+        {".0n", "3.14159265", "3"},
+        {".1g", "3.14159265", "3"},
+        {".2g", "3.14159265", "3.1"},
+        {".5g", "3.14159265", "3.1416"},
+        {".7g", "3.14159265", "3.141593"},
+        {".8g", "3.14159265", "3.1415926"},
+        {".9g", "3.14159265", "3.14159265"},
+        {".10g", "3.14159265", "3.14159265"},
+
+        {"%", "0E1", "0%"},
+        {"%", "0E0", "0%"},
+        {"%", "0E-1", "0%"},
+        {"%", "0E-2", "0%"},
+        {"%", "0E-3", "0.0%"},
+        {"%", "0E-4", "0.00%"},
+
+        {".3%", "0", "0.000%"},
+        {".3%", "0E10", "0.000%"},
+        {".3%", "0E-10", "0.000%"},
+        {".3%", "2.34", "234.000%"},
+        {".3%", "1.234567", "123.457%"},
+        {".0%", "1.23", "123%"},
+
+        {"e", "NaN", "NaN"},
+        {"f", "-NaN123", "-NaN123"},
+        {"+g", "NaN456", "+NaN456"},
+        {".3e", "Inf", "Infinity"},
+        {".16f", "-Inf", "-Infinity"},
+        {".0g", "-sNaN", "-sNaN"},
+
+        {"", "1.00", "1.00"},
+
+        {"6", "123", "   123"},
+        {"<6", "123", "123   "},
+        {">6", "123", "   123"},
+        {"^6", "123", " 123  "},
+        {"=+6", "123", "+  123"},
+        {"#<10", "NaN", "NaN#######"},
+        {"#<10", "-4.3", "-4.3######"},
+        {"#<+10", "0.0130", "+0.0130###"},
+        {"#< 10", "0.0130", " 0.0130###"},
+        {"@>10", "-Inf", "@-Infinity"},
+        {"#>5", "-Inf", "-Infinity"},
+        {"?^5", "123", "?123?"},
+        {"%^6", "123", "%123%%"},
+        {" ^6", "-45.6", "-45.6 "},
+        {"/=10", "-45.6", "-/////45.6"},
+        {"/=+10", "45.6", "+/////45.6"},
+        {"/= 10", "45.6", " /////45.6"},
+
+        {",", "1234567", "1,234,567"},
+        {",", "123456", "123,456"},
+        {",", "12345", "12,345"},
+        {",", "1234", "1,234"},
+        {",", "123", "123"},
+        {",", "12", "12"},
+        {",", "1", "1"},
+        {",", "0", "0"},
+        {",", "-1234567", "-1,234,567"},
+        {",", "-123456", "-123,456"},
+        {"7,", "123456", "123,456"},
+        {"8,", "123456", " 123,456"},
+        {"08,", "123456", "0,123,456"},
+        {"+08,", "123456", "+123,456"},
+        {" 08,", "123456", " 123,456"},
+        {"08,", "-123456", "-123,456"},
+        {"+09,", "123456", "+0,123,456"},
+
+        {"07,", "1234.56", "1,234.56"},
+        {"08,", "1234.56", "1,234.56"},
+        {"09,", "1234.56", "01,234.56"},
+        {"010,", "1234.56", "001,234.56"},
+        {"011,", "1234.56", "0,001,234.56"},
+        {"012,", "1234.56", "0,001,234.56"},
+        {"08,.1f", "1234.5", "01,234.5"},
+
+        {",", "1.23456789", "1.23456789"},
+        {",%", "123.456789", "12,345.6789%"},
+        {",e", "123456", "1.23456e+5"},
+        {",E", "123456", "1.23456E+5"},
+
+        {"a=-7.0", "0.12345", "aaaa0.1"},
+
+        {"<^+15.20%", "inf", "<<+Infinity%<<<"},
+        {"\x07>,%", "sNaN1234567", "sNaN1234567%"},
+        {"=10.10%", "NaN123", "   NaN123%"},
+    };
+
+    for (const std::vector<const char *>& v : test_cases) {
+        const char *fmt = v.at(0);
+        const char *d = v.at(1);
+        const char *result = v.at(2);
+        assertEqual(Decimal(d).format(fmt), result);
+        assertEqual(Decimal(std::string(d)).format(std::string(fmt)), result);
+    }
+
+    assertRaises(ValueError, [](){ Decimal(1).format("<>=10.10"); });
+    const std::string fmt = "=" + std::to_string(MPD_SSIZE_MAX) + ".1";
+    assertRaises(ValueError, [&](){ Decimal("1.23456789").format(fmt); });
+}
+
+/******************************************************************************/
+/*                                  Run tests                                 */
+/******************************************************************************/
+
+struct test_case {
+    const char *name;
+    void (*f)(void);
+};
+
+static const size_t NUM_TESTS = 35;
+static const std::array<struct test_case, NUM_TESTS> test_cases {
+  {
+    /* Context tests */
+    { "ExceptionHierarchyTest",        ExceptionHierarchyTest },
+    { "IEEEContextTest",               IEEEContextTest },
+    { "ContextGetSetTest",             ContextGetSetTest },
+    { "ContextInputValidationTest",    ContextInputValidationTest },
+    { "SmallContextTest",              SmallContextTest },
+    { "ContextReprTest",               ContextReprTest },
+
+    /* Decimal tests */
+    { "ExactConstructionTest",         ExactConstructionTest },
+    { "InexactConstructionTest",       InexactConstructionTest },
+    { "ConstructionExceptionTest",     ConstructionExceptionTest },
+
+    { "AccessorTest",                  AccessorTest },
+
+    { "UnaryOperatorTest",             UnaryOperatorTest },
+    { "PointerUnaryOperatorTest",      PointerUnaryOperatorTest },
+
+    { "ComparisonOperatorTest",        ComparisonOperatorTest },
+    { "PointerComparisonOperatorTest", PointerComparisonOperatorTest },
+
+    { "AssignmentOperatorTest",        AssignmentOperatorTest },
+    { "PointerAssignmentOperatorTest", PointerAssignmentOperatorTest },
+
+    { "ArithmeticOperatorTest",        ArithmeticOperatorTest },
+    { "PointerArithmeticOperatorTest", PointerArithmeticOperatorTest },
+
+    { "PredicateTest",                 PredicateTest },
+    { "UnaryFunctionTest",             UnaryFunctionTest },
+
+    { "CeilTest",                      CeilTest },
+    { "FloorTest",                     FloorTest },
+    { "TruncTest",                     TruncTest },
+
+    { "BinaryFunctionTest",            BinaryFunctionTest },
+    { "DivmodTest",                    DivmodTest },
+    { "QuantizeTest",                  QuantizeTest },
+
+    { "TernaryFunctionTest",           TernaryFunctionTest },
+
+    { "IrregularFunctionTest",         IrregularFunctionTest },
+    { "ApplyTest",                     ApplyTest },
+
+    { "IntegerConversionTest",         IntegerConversionTest },
+
+    { "ExactArithTest",                ExactArithTest },
+
+    { "DataStructuresTest",            DataStructuresTest },
+    { "LargeDataStructuresTest",       LargeDataStructuresTest },
+
+    { "StringReprTest",                StringReprTest },
+    { "FormatTest",                    FormatTest },
+  }
+};
+
+static int
+exit_status(const std::vector<std::string>& status)
+{
+    for (auto p : status) {
+        if (p != "PASS") {
+            return EXIT_FAILURE;
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+/* run a single function */
+static void
+do_test(const struct test_case& t, std::vector<std::string>& status, size_t i)
+{
+    try {
+        assertEqual(context, context_template);
+
+        t.f();
+    } catch (Failure& e) {
+        status[i] = e.what();
+    }
+}
+
+/* process a function list */
+static int
+do_tests(const std::array<struct test_case, NUM_TESTS>& tests)
+{
+    const size_t n = tests.size();
+    std::vector<std::string> status(n, "PASS");
+
+    for (size_t i = 0; i < n; i++) {
+        std::cout << tests[i].name << " ... " << std::flush;
+        do_test(tests[i], status, i);
+        context = context_template;
+        std::cout << status[i] << "\n" << std::flush;
+    }
+
+    std::cout << "\n" << std::flush;
+
+    return exit_status(status);
+}
+
+/* process a file list, threaded */
+static int
+do_tests_threaded(const std::array<struct test_case, NUM_TESTS>& tests)
+{
+    const size_t n = tests.size();
+    std::vector<std::string> status(n, "PASS");
+    std::vector<std::thread> t(n);
+
+    for (size_t i = 0; i < n; i++) {
+        t[i] = std::thread(do_test, tests[i], std::ref(status), i);
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        t[i].join();
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        std::cout << tests[i].name << " ... " << status[i] << "\n" << std::flush;
+    }
+
+    std::cout << "\n" << std::flush;
+
+    return exit_status(status);
+}
+
+/* repeatedly process a randomized function list */
+static int
+do_tests_repeat(const std::array<struct test_case, NUM_TESTS>& tests)
+{
+    const size_t n = tests.size();
+    std::vector<std::string> status(n, "PASS");
+    std::random_device rd;
+    std::mt19937 g(rd());
+    std::vector<size_t> a(n);
+
+    for (size_t i = 0; i < n; i++) {
+        a[i] = i;
+    }
+
+    for (int64_t i = 0; i < 100; i++) {
+        std::shuffle(a.begin(), a.end(), g);
+
+        for (size_t k = 0; k < n; k++) {
+            do_test(tests[a[k]], status, a[k]);
+            context = context_template;
+        }
+
+        if (exit_status(status) != EXIT_SUCCESS) {
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+/* process a file list, threaded */
+static int
+do_tests_threaded_repeat(const std::array<struct test_case, NUM_TESTS>& tests)
+{
+    const size_t n = tests.size();
+    std::vector<std::string> status(n, "PASS");
+    std::vector<std::thread> t(n);
+    std::random_device rd;
+    std::mt19937 g(rd());
+    std::vector<size_t> a(n);
+
+    for (size_t i = 0; i < n; i++) {
+        a[i] = i;
+    }
+
+    for (int64_t i = 0; i < 100; i++) {
+        std::shuffle(a.begin(), a.end(), g);
+
+        for (size_t k = 0; k < n; k++) {
+            t[k] = std::thread(do_test, tests[a[k]], std::ref(status), a[k]);
+        }
+
+        for (size_t k = 0; k < n; k++) {
+            t[k].join();
+        }
+
+        if (exit_status(status) != EXIT_SUCCESS) {
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+
+/*
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77704
+ *
+ * valgrind --tool=helgrind shows a data race in libstdc++.  The race
+ * disappears when streams are used before calling any threads.
+ */
+static void
+init_libc(void)
+{
+    std::ostringstream ss;
+    ss << 1;
+}
+
+static void
+usage(void)
+{
+    std::cerr << "apitest: usage: apitest [--custom] [--thread] [--repeat]" << std::endl;
+    exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+    std::vector<std::string> args(argv + (argc!=0), argv + argc);
+    bool custom_alloc = false;
+    bool thread = false;
+    bool repeat = false;
+
+    for (auto arg : args) {
+        if (!custom_alloc && arg == "--custom") {
+            custom_alloc = true;
+        }
+        else if (!thread && arg == "--thread") {
+            thread = true;
+        }
+        else if (!repeat && arg == "--repeat") {
+            repeat = true;
+        }
+        else {
+            usage();
+        }
+    }
+
+    /* Initialize streams (see above) */
+    init_libc();
+
+    /* Initialize custom allocation functions */
+    test::init_alloc(custom_alloc, /*check_alloc=*/false);
+
+    /* Initialize context template */
+    context_template = Context(9, 999999, -999999, ROUND_HALF_EVEN, 0, 0, 0);
+
+    /* Initialize context for the main thread */
+    context = context_template;
+
+    /* Check version numbers */
+    static_assert(MPD_MAJOR_VERSION == 4, "MPD_MAJOR_VERSION must be 4");
+    static_assert(MPD_MINOR_VERSION == 0, "MPD_MINOR_VERSION must be 0");
+    static_assert(MPD_MICRO_VERSION == 1, "MPD_MICRO_VERSION must be 1");
+
+    if (mpd_version() != std::string("4.0.1")) {
+        err_exit("mpd_version() != 4.0.1");
+    }
+    if (MPD_VERSION != std::string("4.0.1")) {
+        err_exit("MPD_VERSION != 4.0.1");
+    }
+
+    /* Check fundamental libmpdec++ invariant */
+    assertEqual(MPD_MINALLOC, decimal::MINALLOC);
+
+
+    if (thread) {
+        if (repeat) {
+            return do_tests_threaded_repeat(test_cases);
+        }
+        else {
+            return do_tests_threaded(test_cases);
+        }
+    }
+    else {
+        if (repeat) {
+            return do_tests_repeat(test_cases);
+        }
+        else {
+            return do_tests(test_cases);
+        }
+    }
+}
diff --git a/tests++/gettests.bat b/tests++/gettests.bat
new file mode 100644 (file)
index 0000000..92f9cdd
--- /dev/null
@@ -0,0 +1,9 @@
+@ECHO OFF\r
+\r
+cd ..\tests\r
+call gettests.bat\r
+cd ..\tests++\r
+\r
+if not exist testdata mkdir testdata\r
+if not exist testdata\baseconv.decTest copy /y ..\tests\testdata_dist\* testdata\r
+if not exist testdata\add.decTest copy /y ..\tests\testdata\* testdata\r
diff --git a/tests++/gettests.sh b/tests++/gettests.sh
new file mode 100755 (executable)
index 0000000..cecb7f0
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+cd ../tests && ./gettests.sh "$1" || exit 1
+cd ../tests++ && cp -Rp ../tests/testdata . || exit 1
+
diff --git a/tests++/official.topTest b/tests++/official.topTest
new file mode 100644 (file)
index 0000000..c5fce0a
--- /dev/null
@@ -0,0 +1,163 @@
+
+-- All tests from the dectest directory. Tests that are skipped are
+-- commented out.
+
+Dectest: ./testdata/abs.decTest
+Dectest: ./testdata/add.decTest
+Dectest: ./testdata/and.decTest
+Dectest: ./testdata/base.decTest
+Dectest: ./testdata/clamp.decTest
+Dectest: ./testdata/class.decTest
+Dectest: ./testdata/compare.decTest
+Dectest: ./testdata/comparetotal.decTest
+Dectest: ./testdata/comparetotmag.decTest
+Dectest: ./testdata/copyabs.decTest
+Dectest: ./testdata/copy.decTest
+Dectest: ./testdata/copynegate.decTest
+Dectest: ./testdata/copysign.decTest
+Dectest: ./testdata/ddAbs.decTest
+Dectest: ./testdata/ddAdd.decTest
+Dectest: ./testdata/ddAnd.decTest
+Dectest: ./testdata/ddBase.decTest
+-- Dectest: ./testdata/ddCanonical.decTest
+Dectest: ./testdata/ddClass.decTest
+Dectest: ./testdata/ddCompare.decTest
+Dectest: ./testdata/ddCompareSig.decTest
+Dectest: ./testdata/ddCompareTotal.decTest
+Dectest: ./testdata/ddCompareTotalMag.decTest
+Dectest: ./testdata/ddCopyAbs.decTest
+Dectest: ./testdata/ddCopy.decTest
+Dectest: ./testdata/ddCopyNegate.decTest
+Dectest: ./testdata/ddCopySign.decTest
+Dectest: ./testdata/ddDivide.decTest
+Dectest: ./testdata/ddDivideInt.decTest
+-- Dectest: ./testdata/ddEncode.decTest
+Dectest: ./testdata/ddFMA.decTest
+Dectest: ./testdata/ddInvert.decTest
+Dectest: ./testdata/ddLogB.decTest
+Dectest: ./testdata/ddMax.decTest
+Dectest: ./testdata/ddMaxMag.decTest
+Dectest: ./testdata/ddMin.decTest
+Dectest: ./testdata/ddMinMag.decTest
+Dectest: ./testdata/ddMinus.decTest
+Dectest: ./testdata/ddMultiply.decTest
+Dectest: ./testdata/ddNextMinus.decTest
+Dectest: ./testdata/ddNextPlus.decTest
+Dectest: ./testdata/ddNextToward.decTest
+Dectest: ./testdata/ddOr.decTest
+Dectest: ./testdata/ddPlus.decTest
+Dectest: ./testdata/ddQuantize.decTest
+Dectest: ./testdata/ddReduce.decTest
+Dectest: ./testdata/ddRemainder.decTest
+Dectest: ./testdata/ddRemainderNear.decTest
+Dectest: ./testdata/ddRotate.decTest
+Dectest: ./testdata/ddSameQuantum.decTest
+Dectest: ./testdata/ddScaleB.decTest
+Dectest: ./testdata/ddShift.decTest
+Dectest: ./testdata/ddSubtract.decTest
+Dectest: ./testdata/ddToIntegral.decTest
+Dectest: ./testdata/ddXor.decTest
+-- Dectest: ./testdata/decDouble.decTest
+-- Dectest: ./testdata/decQuad.decTest
+-- Dectest: ./testdata/decSingle.decTest
+Dectest: ./testdata/divide.decTest
+Dectest: ./testdata/divideint.decTest
+Dectest: ./testdata/dqAbs.decTest
+Dectest: ./testdata/dqAdd.decTest
+Dectest: ./testdata/dqAnd.decTest
+Dectest: ./testdata/dqBase.decTest
+-- Dectest: ./testdata/dqCanonical.decTest
+Dectest: ./testdata/dqClass.decTest
+Dectest: ./testdata/dqCompare.decTest
+Dectest: ./testdata/dqCompareSig.decTest
+Dectest: ./testdata/dqCompareTotal.decTest
+Dectest: ./testdata/dqCompareTotalMag.decTest
+Dectest: ./testdata/dqCopyAbs.decTest
+Dectest: ./testdata/dqCopy.decTest
+Dectest: ./testdata/dqCopyNegate.decTest
+Dectest: ./testdata/dqCopySign.decTest
+Dectest: ./testdata/dqDivide.decTest
+Dectest: ./testdata/dqDivideInt.decTest
+-- Dectest: ./testdata/dqEncode.decTest
+Dectest: ./testdata/dqFMA.decTest
+Dectest: ./testdata/dqInvert.decTest
+Dectest: ./testdata/dqLogB.decTest
+Dectest: ./testdata/dqMax.decTest
+Dectest: ./testdata/dqMaxMag.decTest
+Dectest: ./testdata/dqMin.decTest
+Dectest: ./testdata/dqMinMag.decTest
+Dectest: ./testdata/dqMinus.decTest
+Dectest: ./testdata/dqMultiply.decTest
+Dectest: ./testdata/dqNextMinus.decTest
+Dectest: ./testdata/dqNextPlus.decTest
+Dectest: ./testdata/dqNextToward.decTest
+Dectest: ./testdata/dqOr.decTest
+Dectest: ./testdata/dqPlus.decTest
+Dectest: ./testdata/dqQuantize.decTest
+Dectest: ./testdata/dqReduce.decTest
+Dectest: ./testdata/dqRemainder.decTest
+Dectest: ./testdata/dqRemainderNear.decTest
+Dectest: ./testdata/dqRotate.decTest
+Dectest: ./testdata/dqSameQuantum.decTest
+Dectest: ./testdata/dqScaleB.decTest
+Dectest: ./testdata/dqShift.decTest
+Dectest: ./testdata/dqSubtract.decTest
+Dectest: ./testdata/dqToIntegral.decTest
+Dectest: ./testdata/dqXor.decTest
+Dectest: ./testdata/dsBase.decTest
+-- Dectest: ./testdata/dsEncode.decTest
+Dectest: ./testdata/exp.decTest
+Dectest: ./testdata/fma.decTest
+Dectest: ./testdata/inexact.decTest
+Dectest: ./testdata/invert.decTest
+Dectest: ./testdata/ln.decTest
+Dectest: ./testdata/log10.decTest
+Dectest: ./testdata/logb.decTest
+Dectest: ./testdata/max.decTest
+Dectest: ./testdata/maxmag.decTest
+Dectest: ./testdata/min.decTest
+Dectest: ./testdata/minmag.decTest
+Dectest: ./testdata/minus.decTest
+Dectest: ./testdata/multiply.decTest
+Dectest: ./testdata/nextminus.decTest
+Dectest: ./testdata/nextplus.decTest
+Dectest: ./testdata/nexttoward.decTest
+Dectest: ./testdata/or.decTest
+Dectest: ./testdata/plus.decTest
+Dectest: ./testdata/power.decTest
+Dectest: ./testdata/powersqrt.decTest
+Dectest: ./testdata/quantize.decTest
+Dectest: ./testdata/randombound32.decTest
+Dectest: ./testdata/randoms.decTest
+Dectest: ./testdata/reduce.decTest
+Dectest: ./testdata/remainder.decTest
+Dectest: ./testdata/remaindernear.decTest
+Dectest: ./testdata/rescale.decTest
+Dectest: ./testdata/rotate.decTest
+Dectest: ./testdata/rounding.decTest
+Dectest: ./testdata/samequantum.decTest
+Dectest: ./testdata/scaleb.decTest
+Dectest: ./testdata/shift.decTest
+Dectest: ./testdata/squareroot.decTest
+Dectest: ./testdata/subtract.decTest
+-- Dectest: ./testdata/testall.decTest
+Dectest: ./testdata/tointegral.decTest
+Dectest: ./testdata/tointegralx.decTest
+-- Dectest: ./testdata/trim.decTest
+Dectest: ./testdata/xor.decTest
+
+
+-- Summary of functions that are not implemented or do not need testing:
+
+-- Dectest: ./testdata/ddCanonical.decTest  ==>  same as copy()
+-- Dectest: ./testdata/ddEncode.decTest
+-- Dectest: ./testdata/decDouble.decTest
+-- Dectest: ./testdata/decQuad.decTest
+-- Dectest: ./testdata/decSingle.decTest
+-- Dectest: ./testdata/dqCanonical.decTest  ==> same as copy()
+-- Dectest: ./testdata/dqEncode.decTest
+-- Dectest: ./testdata/dsEncode.decTest
+-- Dectest: ./testdata/testall.decTest
+-- Dectest: ./testdata/trim.decTest
+
+
diff --git a/tests++/runshort.sh b/tests++/runshort.sh
new file mode 100755 (executable)
index 0000000..2b94c3e
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+SYSTEM=`uname -s`
+case $SYSTEM in
+    darwin*|Darwin*)
+        # malloc() on OS X does not conform to the C standard.
+        export MallocLogFile=/dev/null
+        export MallocDebugReport=crash
+        ;;
+    *)
+        ;;
+esac
+
+# Download or copy the official test cases (text files).
+./gettests.sh "$1" || exit 1
+
+
+if [ ! -f ./runtest -a ! -f ./runtest_shared ]; then
+    printf "\nERROR: ./runtest and ./runtest_shared not found\n\n\n"; exit 1;
+fi
+
+if [ -f ./runtest ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                        libmpdec++: static library\n"
+    printf "# ========================================================================\n\n"
+
+    if [ x"$1" != x"--local" ]; then
+        printf "Running official tests ...\n\n"
+        ./runtest official.topTest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+    fi
+
+    printf "Running additional tests ...\n\n"
+    ./runtest additional.topTest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (single thread) ... \n\n"
+    ./apitest || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (threaded) ... \n\n"
+    ./apitest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
+
+if [ -f ./runtest_shared ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                        libmpdec++: shared library\n"
+    printf "# ========================================================================\n\n"
+
+    PORTABLE_PWD=`pwd`
+    LD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH"
+    DYLD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$DYLD_LIBRARY_PATH"
+    LD_64_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_64_LIBRARY_PATH"
+    LD_32_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_32_LIBRARY_PATH"
+    LD_LIBRARY_PATH_64="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH_64"
+    LD_LIBRARY_PATH_32="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH_32"
+    PATH="$LD_LIBRARY_PATH:$PATH"
+    export LD_LIBRARY_PATH
+    export DYLD_LIBRARY_PATH
+    export LD_64_LIBRARY_PATH
+    export LD_32_LIBRARY_PATH
+    export LD_LIBRARY_PATH_64
+    export LD_LIBRARY_PATH_32
+
+    if [ x"$1" != x"--local" ]; then
+        printf "Running official tests ...\n\n"
+        ./runtest_shared official.topTest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+    fi
+
+    printf "Running additional tests ...\n\n"
+    ./runtest_shared additional.topTest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (single thread) ... \n\n"
+    ./apitest_shared || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (threaded) ... \n\n"
+    ./apitest_shared --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
diff --git a/tests++/runshort_alloc.sh b/tests++/runshort_alloc.sh
new file mode 100755 (executable)
index 0000000..0caf0f4
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+SYSTEM=`uname -s`
+case $SYSTEM in
+    darwin*|Darwin*)
+        # malloc() on OS X does not conform to the C standard.
+        export MallocLogFile=/dev/null
+        export MallocDebugReport=crash
+        ;;
+    *)
+        ;;
+esac
+
+
+# Download or copy the official test cases (text files).
+./gettests.sh || exit 1
+
+if [ ! -f ./runtest -a ! -f ./runtest_shared ]; then
+    printf "\nERROR: ./runtest and ./runtest_shared not found\n\n\n"; exit 1;
+fi
+
+if [ -f ./runtest ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                        libmpdec++: static library\n"
+    printf "# ========================================================================\n\n"
+
+    printf "Running official tests with allocation failures ...\n\n"
+    ./runtest official.topTest --thread --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+
+    printf "Running additional tests with allocation failures ...\n\n"
+    ./runtest additional.topTest --thread --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (single thread) ... \n\n"
+    ./apitest || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (threaded) ... \n\n"
+    ./apitest --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
+
+if [ -f ./runtest_shared ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                        libmpdec++: shared library\n"
+    printf "# ========================================================================\n\n"
+
+    PORTABLE_PWD=`pwd`
+    LD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH"
+    DYLD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$DYLD_LIBRARY_PATH"
+    LD_64_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_64_LIBRARY_PATH"
+    LD_32_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_32_LIBRARY_PATH"
+    LD_LIBRARY_PATH_64="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH_64"
+    LD_LIBRARY_PATH_32="$PORTABLE_PWD/../libmpdec:$PORTABLE_PWD/../libmpdec++:$LD_LIBRARY_PATH_32"
+    PATH="$LD_LIBRARY_PATH:$PATH"
+    export LD_LIBRARY_PATH
+    export DYLD_LIBRARY_PATH
+    export LD_64_LIBRARY_PATH
+    export LD_32_LIBRARY_PATH
+    export LD_LIBRARY_PATH_64
+    export LD_LIBRARY_PATH_32
+
+    printf "Running official tests with allocation failures ...\n\n"
+    ./runtest_shared official.topTest --thread --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running additional tests with allocation failures ...\n\n"
+    ./runtest_shared additional.topTest --thread --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (single thread) ... \n\n"
+    ./apitest_shared || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running API tests (threaded) ... \n\n"
+    ./apitest_shared --thread || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
diff --git a/tests++/runtest.cc b/tests++/runtest.cc
new file mode 100644 (file)
index 0000000..1dfb0d7
--- /dev/null
@@ -0,0 +1,3210 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifdef _AIX
+  #define __STDC_FORMAT_MACROS
+#endif
+
+#ifndef _MSC_VER
+  #include "config.h"
+  #ifdef HAVE_PTHREAD_H
+    #include <pthread.h>
+  #endif
+#endif
+
+#include <cctype>
+#include <cerrno>
+#include <cinttypes>
+#include <clocale>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+
+#include <algorithm>
+#include <atomic>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "mpdecimal.h"
+
+#include "decimal.hh"
+#include "test.hh"
+#include "vctest.hh"
+
+
+using decimal::ValueError;
+using decimal::MallocError;
+using decimal::RuntimeError;
+using decimal::InvalidOperation;
+using decimal::Decimal;
+using decimal::Context;
+using decimal::context_template;
+using decimal::context;
+
+using decimal::util::safe_downcast;
+
+#if defined(MPD_CONFIG_32)
+using test::set_alloc_limit;
+#endif
+using test::set_alloc;
+using test::set_alloc_fail;
+
+
+enum skip_cmp {SKIP_NONE, SKIP_NAN, SKIP_NONINT};
+
+/*
+ * These extended ranges are required for the official test suite and are
+ * not problematic for its specific test cases. However, they should not
+ * be used in production code.
+ *
+ * The use of the directive "ExtendedRange" is not related to the "Extended"
+ * directive that is briefly referred to in the official tests.
+ */
+#if defined(MPD_CONFIG_64)
+  #define MPD_READ_MAX_PREC 1070000000000000000LL
+#elif defined(MPD_CONFIG_32)
+  #define MPD_READ_MAX_PREC 1070000000
+#else
+  #error "config not defined"
+#endif
+
+static mpd_context_t
+readcontext(const bool extended)
+{
+    mpd_context_t c;
+
+    if (extended) {
+        c.prec = MPD_READ_MAX_PREC;
+        c.emax = MPD_READ_MAX_PREC;
+        c.emin = -MPD_READ_MAX_PREC;
+    }
+    else {
+        c.prec = MPD_MAX_PREC;
+        c.emax = MPD_MAX_EMAX;
+        c.emin = MPD_MIN_EMIN;
+    }
+
+    c.traps = MPD_Malloc_error;
+    c.status = 0;
+    c.newtrap = 0;
+    c.round = MPD_ROUND_HALF_UP;
+    c.clamp = 0;
+    c.allcr = 1;
+
+    return c;
+}
+
+static mpd_context_t
+testcontext(const bool extended)
+{
+    mpd_context_t c;
+
+    if (extended) {
+    #if defined(MPD_CONFIG_64)
+        c.prec = MPD_MAX_PREC;
+        c.emax = MPD_MAX_EMAX;
+        c.emin = MPD_MIN_EMIN;
+    #elif defined(MPD_CONFIG_32)
+        c.prec = 999999999;
+        c.emax = 999999999;
+        c.emin = -999999999;
+    #else
+        #error "config not defined"
+    #endif
+    }
+    else {
+        c.prec = MPD_MAX_PREC;
+        c.emax = MPD_MAX_EMAX;
+        c.emin = MPD_MIN_EMIN;
+    }
+
+    c.traps = MPD_Malloc_error;
+    c.status = 0;
+    c.newtrap = 0;
+    c.round = MPD_ROUND_HALF_UP;
+    c.clamp = 0;
+    c.allcr = 1;
+
+    return c;
+}
+
+static void
+mpd_assert_context_ok(const Context& c, const std::vector<std::string>& token)
+{
+    const mpd_context_t *ctx = c.getconst();
+
+    DECIMAL_ASSERT(0 < ctx->prec && ctx->prec <= MPD_READ_MAX_PREC, token);
+    DECIMAL_ASSERT(0 <= ctx->emax && ctx->emax <= MPD_READ_MAX_PREC, token);
+    DECIMAL_ASSERT(-MPD_READ_MAX_PREC <= ctx->emin && ctx->emin <= 0, token);
+    DECIMAL_ASSERT(0 <= ctx->round && ctx->round < MPD_ROUND_GUARD, token);
+    DECIMAL_ASSERT(ctx->traps <= MPD_Max_status, token);
+    DECIMAL_ASSERT(ctx->status <= MPD_Max_status, token);
+    DECIMAL_ASSERT(ctx->clamp == 0 || ctx->clamp == 1, token);
+    DECIMAL_ASSERT(ctx->allcr == 0 || ctx->allcr == 1, token);
+}
+
+
+/* Known differences that are within the spec */
+struct result_diff {
+    const char *id;
+    const char *calc;
+    const char *expected;
+};
+
+struct status_diff {
+    const char *id;
+    uint32_t calc;
+    uint32_t expected;
+};
+
+static const struct result_diff ulp_cases[] {
+    /* Cases where the result is allowed to differ by less than one ULP.
+     * Only needed if ctx->allcr is 0. */
+    { "expx013", "1.001000", "1.001001" },
+    { "expx020", "1.000000", "1.000001" },
+    { "expx109", "0.999999910000004049999878", "0.999999910000004049999879" },
+    { "expx1036", "1.005088", "1.005087" },
+    { "expx350", "1.0000000", "1.0000001" },
+    { "expx351", "1.0000000", "1.0000001" },
+    { "expx352", "1.0000000", "1.0000001" },
+};
+
+static const struct status_diff status_cases[] {
+    /* With a reduced working precision in mpd_qpow() the status matches. */
+    { "pwsx803", MPD_Inexact|MPD_Rounded|MPD_Subnormal|MPD_Underflow, MPD_Inexact|MPD_Rounded },
+};
+
+static const char *skipit[] {
+    /* NULL reference, decimal16, decimal32, or decimal128 */
+    "absx900",
+    "addx9990",
+    "addx9991",
+    "clam090",
+    "clam091",
+    "clam092",
+    "clam093",
+    "clam094",
+    "clam095",
+    "clam096",
+    "clam097",
+    "clam098",
+    "clam099",
+    "clam189",
+    "clam190",
+    "clam191",
+    "clam192",
+    "clam193",
+    "clam194",
+    "clam195",
+    "clam196",
+    "clam197",
+    "clam198",
+    "clam199",
+    "comx990",
+    "comx991",
+    "cotx9990",
+    "cotx9991",
+    "ctmx9990",
+    "ctmx9991",
+    "ddabs900",
+    "ddadd9990",
+    "ddadd9991",
+    "ddcom9990",
+    "ddcom9991",
+    "ddcot9990",
+    "ddcot9991",
+    "ddctm9990",
+    "ddctm9991",
+    "dddiv9998",
+    "dddiv9999",
+    "dddvi900",
+    "dddvi901",
+    "ddfma2990",
+    "ddfma2991",
+    "ddfma39990",
+    "ddfma39991",
+    "ddlogb900",
+    "ddmax900",
+    "ddmax901",
+    "ddmxg900",
+    "ddmxg901",
+    "ddmin900",
+    "ddmin901",
+    "ddmng900",
+    "ddmng901",
+    "ddmul9990",
+    "ddmul9991",
+    "ddnextm900",
+    "ddnextm900",
+    "ddnextp900",
+    "ddnextp900",
+    "ddnextt900",
+    "ddnextt901",
+    "ddqua998",
+    "ddqua999",
+    "ddred900",
+    "ddrem1000",
+    "ddrem1001",
+    "ddrmn1000",
+    "ddrmn1001",
+    "ddsub9990",
+    "ddsub9991",
+    "ddintx074",
+    "ddintx094",
+    "divx9998",
+    "divx9999",
+    "dvix900",
+    "dvix901",
+    "dqabs900",
+    "dqadd9990",
+    "dqadd9991",
+    "dqcom990",
+    "dqcom991",
+    "dqcot9990",
+    "dqcot9991",
+    "dqctm9990",
+    "dqctm9991",
+    "dqdiv9998",
+    "dqdiv9999",
+    "dqdvi900",
+    "dqdvi901",
+    "dqfma2990",
+    "dqfma2991",
+    "dqadd39990",
+    "dqadd39991",
+    "dqlogb900",
+    "dqmax900",
+    "dqmax901",
+    "dqmxg900",
+    "dqmxg901",
+    "dqmin900",
+    "dqmin901",
+    "dqmng900",
+    "dqmng901",
+    "dqmul9990",
+    "dqmul9991",
+    "dqnextm900",
+    "dqnextp900",
+    "dqnextt900",
+    "dqnextt901",
+    "dqqua998",
+    "dqqua999",
+    "dqred900",
+    "dqrem1000",
+    "dqrem1001",
+    "dqrmn1000",
+    "dqrmn1001",
+    "dqsub9990",
+    "dqsub9991",
+    "dqintx074",
+    "dqintx094",
+    "expx900",
+    "fmax2990",
+    "fmax2991",
+    "fmax39990",
+    "fmax39991",
+    "lnx900",
+    "logx900",
+    "logbx900",
+    "maxx900",
+    "maxx901",
+    "mxgx900",
+    "mxgx901",
+    "mnm900",
+    "mnm901",
+    "mng900",
+    "mng901",
+    "minx900",
+    "mulx990",
+    "mulx991",
+    "nextm900",
+    "nextp900",
+    "nextt900",
+    "nextt901",
+    "plu900",
+    "powx900",
+    "powx901",
+    "pwsx900",
+    "quax1022",
+    "quax1023",
+    "quax1024",
+    "quax1025",
+    "quax1026",
+    "quax1027",
+    "quax1028",
+    "quax1029",
+    "quax0a2",
+    "quax0a3",
+    "quax998",
+    "quax999",
+    "redx900",
+    "remx1000",
+    "remx1001",
+    "rmnx900",
+    "rmnx901",
+    "sqtx9900",
+    "subx9990",
+    "subx9991",
+    /* operand range violations, invalid context */
+    "expx901",
+    "expx902",
+    "expx903",
+    "expx905",
+    "lnx901",
+    "lnx902",
+    "lnx903",
+    "lnx905",
+    "logx901",
+    "logx902",
+    "logx903",
+    "logx905",
+    "powx1183",
+    "powx1184",
+    "powx4001",
+    "powx4002",
+    "powx4003",
+    "powx4005",
+    "powx4008",
+    "powx4010",
+    "powx4012",
+    "powx4014",
+    "scbx164",
+    "scbx165",
+    "scbx166",
+#if defined(MPD_CONFIG_32) && MPD_MINALLOC_MAX <= 4
+    /* Under the allocation failure tests, the result is numerically correct
+       (1 == 1.00000) but without zero padding. This is by design, since in
+       case of MPD_Malloc_error mpd_qsqrt() retries the operation with a lower
+       context precision and allows all exact results.
+
+       The MPD_MINALLOC_MAX < 64 feature is is officially unsupported but works
+       (if the little-endian mpd_ln10_data arrays are adjusted).
+    */
+    "sqtx9045",
+#endif
+    /* skipped for decNumber, too */
+    "powx4302",
+    "powx4303",
+    "powx4303",
+    "powx4342",
+    "powx4343",
+    "pwsx805",
+    /* disagreement for three arg power */
+    "pwmx325",
+    "pwmx326",
+};
+
+static mpd_ssize_t
+strtossize(const char *s, char **end, int base)
+{
+    int64_t retval;
+
+    errno = 0;
+    retval = _mpd_strtossize(s, end, base);
+    if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) {
+        errno = ERANGE;
+    }
+    if (errno == ERANGE) {
+        return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX;
+    }
+
+    return static_cast<mpd_ssize_t>(retval);
+}
+
+static uint64_t
+rnd(void)
+{
+    static thread_local std::mt19937_64 r(time(nullptr));
+
+    return r();
+}
+
+static void
+mpd_init_rand(Decimal &x)
+{
+    Context maxcontext{readcontext(false)};
+    uint64_t r = rnd() % 100;
+    uint8_t sign = rnd() % 2;
+
+    if (r >= 80) {
+        x = Decimal("-1111111111e20200", maxcontext);
+    }
+    else if (r >= 60) {
+        x = Decimal("-1111111111222222222233333333334444444444555555555566666666667777777777"
+                    "888888888899999999990000000000e-1201", maxcontext);
+    }
+    else if (r >= 40) {
+        x = sign ? Decimal("-nan") : Decimal("nan");
+    }
+    else if (r >= 20) {
+        x = sign ? Decimal("-snan") : Decimal("snan");
+    }
+    else {
+        x = sign ? Decimal("-inf") : Decimal("inf");
+    }
+}
+
+static bool
+skip_test(const std::string& id)
+{
+    const auto& loc = std::find(std::begin(skipit), std::end(skipit), id);
+    if (loc != std::end(skipit)) {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+startswith(const std::string& s, const char *prefix)
+{
+    return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+}
+
+static bool
+endswith(const std::string& s, const char *suffix)
+{
+    std::string rs(s);
+    std::string prefix(suffix);
+    std::reverse(rs.begin(), rs.end());
+    std::reverse(prefix.begin(), prefix.end());
+    return startswith(rs, prefix.c_str());
+}
+
+static bool
+eqtoken(const std::string& tok, const char *s)
+{
+    return strcasecmp(tok.c_str(), s) == 0;
+}
+
+static bool
+istokchar(unsigned char c)
+{
+    return std::isalnum(c) || (std::ispunct(c) && c != '"' && c != '\'');
+}
+
+static int
+nexttoken(std::string::const_iterator& start,
+          std::string::const_iterator& end,
+          std::string::const_iterator& next_start,
+          const std::string::const_iterator& nul)
+{
+    end = next_start;
+
+    for (; end != nul; end++) {
+        if (isspace(static_cast<unsigned char>(*end))) {
+            /* empty */
+        }
+        else if (*end == '-' && (end+1) != nul && *(end+1) == '-') {
+            start = end = next_start = nul;
+            return 0;
+        }
+        else if (*end == '"') {
+            start = ++end;
+            for (; end != nul; end++) {
+                if (*end == '"') {
+                    if ((end+1) != nul && *(end+1) == '"') {
+                        end++;  /* official test cases: "1""1" is parsed as a single string. */
+                    }
+                    else {
+                        next_start = end+1;
+                        return 0;
+                    }
+                }
+            }
+            return -1;
+        }
+        else if (*end == '\'') {
+            start = ++end;
+            for (; end != nul; end++) {
+                if (*end == '\'') {
+                    if ((end+1) != nul && *(end+1) == '\'') {
+                        end++;  /* official test cases: '1''1' is parsed as a single string. */
+                    }
+                    else {
+                        next_start = end+1;
+                        return 0;
+                    }
+                }
+            }
+            return -1;
+        }
+        else {
+            start = end;
+            for (; end != nul; end++) {
+                if (std::isspace(static_cast<unsigned char>(*end))) {
+                    break;
+                }
+                if (!istokchar(static_cast<unsigned char>(*end))) {
+                    return -1;
+                }
+            }
+            next_start = end;
+            return 0;
+        }
+    }
+
+    start = next_start = end;
+    return 0;
+}
+
+/* split a line into tokens */
+static std::vector<std::string>
+split(const std::string& line)
+{
+    std::string::const_iterator start = line.begin();
+    std::string::const_iterator end = start;
+    std::string::const_iterator next_start = start;
+    const std::string::const_iterator nul = line.end();
+    std::vector<std::string> token;
+
+    while (true) {
+        const int r = nexttoken(start, end, next_start, nul);
+        if (r < 0) {
+            std::cerr << "parse_error: " << line << std::endl;
+            std::exit(EXIT_FAILURE);
+        }
+        if (end == start && end == next_start) {
+            break;
+        }
+        std::string tok{start, end};
+        token.push_back(tok);
+    }
+
+    return token;
+}
+
+/* returns all expected conditions in a status flag */
+static uint32_t
+scan_conditions(const std::vector<std::string>& token, const size_t n)
+{
+    uint32_t status = 0;
+
+    for (size_t i = n; i < token.size(); i++) {
+        const std::string condition = token[i];
+
+        if (eqtoken(condition, "Clamped")) {
+            status |= MPD_Clamped;
+        }
+        else if (eqtoken(condition, "Conversion_syntax")) {
+            status |= MPD_Conversion_syntax;
+        }
+        else if (eqtoken(condition, "Division_by_zero")) {
+            status |= MPD_Division_by_zero;
+        }
+        else if (eqtoken(condition, "Division_impossible")) {
+            status |= MPD_Division_impossible;
+        }
+        else if (eqtoken(condition, "Division_undefined")) {
+            status |= MPD_Division_undefined;
+        }
+        else if (eqtoken(condition, "Fpu_error")) {
+            status |= MPD_Fpu_error;
+        }
+        else if (eqtoken(condition, "Inexact")) {
+            status |= MPD_Inexact;
+        }
+        else if (eqtoken(condition, "Invalid_context")) {
+            status |= MPD_Invalid_context;
+        }
+        else if (eqtoken(condition, "Invalid_operation")) {
+            status |= MPD_Invalid_operation;
+        }
+        else if (eqtoken(condition, "Malloc_error")) {
+            status |= MPD_Malloc_error;
+        }
+        else if (eqtoken(condition, "Not_implemented")) {
+            status |= MPD_Not_implemented;
+        }
+        else if (eqtoken(condition, "Overflow")) {
+            status |= MPD_Overflow;
+        }
+        else if (eqtoken(condition, "Rounded")) {
+            status |= MPD_Rounded;
+        }
+        else if (eqtoken(condition, "Subnormal")) {
+            status |= MPD_Subnormal;
+        }
+        else if (eqtoken(condition, "Underflow")) {
+            status |= MPD_Underflow;
+        }
+        else {
+            err_token(token, "scan_conditions: unknown status");
+        }
+    }
+
+    return status;
+}
+
+static void
+compare_expected(const std::vector<std::string>& token,
+                 const std::string& calc,
+                 const std::string& expected,
+                 uint32_t expected_status,
+                 const Context& ctx)
+{
+    const std::string id = token.at(0);
+
+    /* known ULP diffs */
+    if (ctx.allcr() == 0) {
+        for (const auto& c : ulp_cases) {
+            if (id == c.id && expected == c.expected && calc == c.calc) {
+                return;
+            }
+        }
+    }
+
+    /* known status diffs */
+    for (const auto& c : status_cases) {
+        if (id == c.id && expected_status == c.expected && ctx.status() == c.calc) {
+            return;
+        }
+    }
+
+    if (calc != expected) {
+        err_token(token, "calc: ", calc, " expected: ", expected);
+    }
+
+    if (ctx.status() != expected_status) {
+        char ctxstatus[MPD_MAX_FLAG_STRING];
+        char expstatus[MPD_MAX_FLAG_STRING];
+
+        mpd_snprint_flags(ctxstatus, MPD_MAX_FLAG_STRING, ctx.status());
+        mpd_snprint_flags(expstatus, MPD_MAX_FLAG_STRING, expected_status);
+
+        err_token(token, "calc: [", ctxstatus, "] expected: [",  expstatus, "]");
+    }
+}
+
+static bool
+equalmem(const Decimal& x, const Decimal& y)
+{
+    const mpd_t *a = x.getconst();
+    const mpd_t *b = y.getconst();
+
+    if ((a->flags & ~MPD_DATAFLAGS) != (b->flags & ~MPD_DATAFLAGS) ||
+        a->exp != b->exp ||
+        a->len != b->len ||
+        a->digits != b->digits) {
+        return false;
+    }
+
+    for (mpd_ssize_t i = 0; i < a->len; i++) {
+        if (a->data[i] != b->data[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void
+check_equalmem(const std::vector<std::string>& token, const Decimal& a, const Decimal& b)
+{
+    if (!equalmem(a, b)) {
+        err_token(token, "const arg changed");
+    }
+}
+
+static unsigned long
+get_testno(const std::vector<std::string>& token)
+{
+    const char *number = strpbrk(token.at(0).c_str(), "0123456789");
+    if (number == nullptr) {
+        err_token(token, "invalid test id: ", token.at(0));
+    }
+    return strtoul(number, nullptr, 10);
+}
+
+/* scan a single operand and the expected result */
+static size_t
+scan_op_expected(Decimal& op,
+                 std::string& expected,
+                 const std::vector<std::string>& token,
+                 Context& ctx)
+{
+    op = Decimal(token.at(2), ctx);
+    if (token.at(3) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    expected = token.at(4);
+
+    return 5;
+}
+
+/* scan decimal operand, string operand and the expected result */
+static size_t
+scan_op_string_expected(Decimal& op1,
+                        std::string& op2,
+                        std::string& result,
+                        const std::vector<std::string>& token,
+                        Context& ctx)
+{
+    op1 = Decimal(token.at(2), ctx);
+    op2 = token.at(3);
+    if (token.at(4) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    result = token.at(5);
+
+    return 6;
+}
+
+/* scan two operands and the expected result */
+static size_t
+scan_op_op_expected(Decimal& op1,
+                    Decimal& op2,
+                    std::string& result,
+                    const std::vector<std::string>& token,
+                    Context& ctx)
+{
+    op1 = Decimal(token.at(2), ctx);
+    op2 = Decimal(token.at(3), ctx);
+    if (token.at(4) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    result = token.at(5);
+
+    return 6;
+}
+
+/* scan one operands and two results */
+static size_t
+scan_op_expected_expected(Decimal& op1,
+                          std::string& result1,
+                          std::string& result2,
+                          const std::vector<std::string>& token,
+                          Context& ctx)
+{
+    op1 = Decimal(token.at(2), ctx);
+    if (token.at(3) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    result1 = token.at(4);
+    result2 = token.at(5);
+
+    return 6;
+}
+
+/* scan two operands and two results */
+static size_t
+scan_op_op_expected_expected(Decimal& op1,
+                             Decimal& op2,
+                             std::string& result1,
+                             std::string& result2,
+                             const std::vector<std::string>& token,
+                             Context& ctx)
+{
+    op1 = Decimal(token.at(2), ctx);
+    op2 = Decimal(token.at(3), ctx);
+    if (token.at(4) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    result1 = token.at(5);
+    result2 = token.at(6);
+
+    return 7;
+}
+
+/* scan three operands and the expected result */
+static size_t
+scan_op_op_op_expected(Decimal& op1,
+                       Decimal& op2,
+                       Decimal& op3,
+                       std::string& result,
+                       const std::vector<std::string>& token,
+                       Context& ctx)
+{
+    op1 = Decimal(token.at(2), ctx);
+    op2 = Decimal(token.at(3), ctx);
+    op3 = Decimal(token.at(4), ctx);
+    if (token.at(5) != "->") {
+        err_token(token, "expected '->' token");
+    }
+    result = token.at(6);
+
+    return 7;
+}
+
+/* Triple tests */
+static void
+Triple(const std::vector<std::string>& token, const Decimal &dec, Context &ctx)
+{
+#ifdef MPD_CONFIG_32
+    /*
+     * 32-bit: as_triple() expects well-formed decimals. Skip test cases
+     * that use the extended exponent, which is safe in the tests but not
+     * in production.
+     */
+    if (!dec.isspecial()) {
+        if (dec.exponent() < MPD_MIN_ETINY || dec.exponent() > MPD_MAX_EMAX) {
+            return;
+        }
+    }
+#endif
+
+    mpd_uint128_triple_t triple = dec.as_uint128_triple();
+    switch (triple.tag) {
+    case MPD_TRIPLE_QNAN: case MPD_TRIPLE_SNAN:
+        DECIMAL_ASSERT(triple.exp == 0, token);
+        break;
+    case MPD_TRIPLE_INF:
+        DECIMAL_ASSERT(triple.hi == 0 && triple.lo == 0 && triple.exp == 0,
+                       token);
+        break;
+    case MPD_TRIPLE_NORMAL:
+        break;
+    case MPD_TRIPLE_ERROR:
+        DECIMAL_ASSERT(triple.sign == 0 && triple.hi == 0 &&
+                       triple.lo == 0 && triple.exp == 0,
+                       token);
+        return;
+    }
+
+    /* Allocation failures in Decimal(triple) */
+    Decimal d = 10;
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+
+        set_alloc_fail(ctx, n);
+        try {
+            d = Decimal(triple);
+        }
+        catch (MallocError&) {
+            set_alloc(ctx);
+            DECIMAL_ASSERT(d == 10, token);
+            continue;
+        }
+
+        set_alloc(ctx);
+        break;
+    }
+
+    check_equalmem(token, d, dec);
+    DECIMAL_ASSERT(d.cmp_total(dec) == 0, token);
+}
+
+/*
+ * This function is used for "toSci", "toEng" and "apply" and does not use
+ * a maxcontext for the conversion of the operand.
+ */
+typedef std::string (Decimal::*String_DecimalContext)(bool) const;
+static void
+Str_DecCtx(String_DecimalContext func,
+           const std::vector<std::string>& token,
+           const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op;
+    Decimal tmp;
+    std::string expected;
+    std::string expected_fail;
+    std::string calc;
+
+    Context& workctx = context;
+    workctx.status(0);
+    const size_t i = scan_op_expected(op, expected, token, workctx);
+    const uint32_t expstatus = scan_conditions(token, i);
+    if (expstatus != workctx.status()) {
+        err_token(token, "op: ", op, " expstatus: ", expstatus, " got: ", workctx.status());
+    }
+    Triple(token, op, workctx);
+
+    /* Allocation failures for Decimal() */
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp);
+        const Decimal save_tmp = tmp;
+
+        workctx.status(0);
+        set_alloc_fail(workctx, n);
+        try {
+            (void)scan_op_expected(tmp, expected_fail, token, workctx);
+        }
+        catch (MallocError&) {
+            set_alloc(workctx);
+            check_equalmem(token, tmp, save_tmp);
+            continue;
+        }
+
+        set_alloc(workctx);
+        break;
+    }
+    /* internal sanity checks */
+    DECIMAL_ASSERT(expected == expected_fail, token);
+    DECIMAL_ASSERT(tmp.cmp_total(op) == 0, token);
+
+    /* make a copy of the operand */
+    mpd_init_rand(tmp);
+    tmp = op;
+
+    workctx.status(0);
+    calc = (tmp.*func)(true);
+
+    /* compare the calculated result with the expected result */
+    compare_expected(token, calc, expected, 0, workctx);
+    check_equalmem(token, tmp, op);
+
+    /* Allocation failures */
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp);
+        tmp = op;
+
+        workctx.status(0);
+        set_alloc_fail(workctx, n);
+        try {
+            calc = (tmp.*func)(true);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, tmp, op);
+            continue;
+        }
+
+        set_alloc(workctx);
+        break;
+    }
+
+    compare_expected(token, calc, expected, 0, workctx);
+    check_equalmem(token, tmp, op);
+}
+
+#ifdef __INTEL_COMPILER
+  #pragma warning(disable : 186)
+#endif
+/* Quick and dirty: parse hex escape sequences */
+static std::string
+parse_escapes_backslash(const char *s)
+{
+    char hex[5];
+    char *result, *cp;
+    unsigned int u;
+    unsigned char b;
+    int n;
+
+    std::shared_ptr<char> ptr(new char[strlen(s)+1], std::default_delete<char[]>());
+    cp = result = ptr.get();
+
+    hex[0] = '0';
+    hex[1] = '\0';
+    while (*s) {
+        if (*s == '\\' && *(s+1) == 'x') {
+            for (n = 1; n < 4; n++) {
+                if (!s[n]) {
+                    err_raise("parse hex escapes: invalid escape sequence");
+                }
+                hex[n] = s[n];
+            }
+            hex[n] = '\0';
+            sscanf(hex, "%x%n", &u, &n);
+            b = safe_downcast<unsigned char, unsigned int>(u);
+            *cp++ = static_cast<char>(b);
+            s += n;
+        }
+        else {
+            *cp++ = *s++;
+        }
+    }
+
+    *cp = '\0';
+    return std::string(result);
+}
+
+static std::string
+parse_escapes_hexstring(const char *s)
+{
+    const std::string hex{s};
+    const size_t len = hex.size();
+    std::vector<char> bytes;
+
+    if (len % 2 != 0) {
+        err_raise("parse hex escapes: invalid escape sequence");
+    }
+
+    for (size_t i = 0; i < len; i += 2) {
+        std::string twodigits = hex.substr(i, 2);
+        const unsigned long ul = strtoul(twodigits.c_str(), nullptr, 16);
+        const unsigned char b = safe_downcast<unsigned char, unsigned long>(ul);
+        bytes.push_back(static_cast<char>(b));
+    }
+
+    return std::string(bytes.data(), bytes.size());
+}
+
+static std::string
+parse_escapes(const char *s)
+{
+    if (startswith(s, "HEX")) {
+        return parse_escapes_hexstring(s+3);
+    }
+    else {
+        return parse_escapes_backslash(s);
+    }
+}
+
+/* This function is used for Decimal::format. */
+static void
+Fmt(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op, tmp;
+    std::string fmt, expected;
+    std::string calc;
+
+    const size_t i = scan_op_string_expected(op, fmt, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, i);
+    Triple(token, op, maxcontext);
+
+    fmt = parse_escapes(fmt.c_str());
+    expected = parse_escapes(expected.c_str());
+
+    mpd_init_rand(tmp);
+    tmp = op;
+
+    context.status(0);
+    try {
+        calc = tmp.format(fmt);
+    }
+    catch (ValueError&) {
+        DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+        DECIMAL_ASSERT(context.status() == 0, token);
+        check_equalmem(token, tmp, op);
+    #ifdef __mips__
+        return;
+    #endif
+    }
+
+    DECIMAL_ASSERT(expstatus == 0 || expstatus == MPD_Invalid_operation, token);
+    if (expstatus == 0) {
+        compare_expected(token, calc, expected, expstatus, context);
+        check_equalmem(token, tmp, op);
+    }
+
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp);
+        tmp = op;
+
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            calc = tmp.format(fmt);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            continue;
+        }
+    #ifndef __mips__ /* miscompilation */
+        catch (ValueError&) {
+            DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+            DECIMAL_ASSERT(context.status() == 0, token);
+        }
+    #endif
+
+        set_alloc(context);
+        break;
+    }
+
+    DECIMAL_ASSERT(expstatus == 0 || expstatus == MPD_Invalid_operation, token);
+    if (expstatus == 0) {
+        compare_expected(token, calc, expected, expstatus, context);
+        check_equalmem(token, tmp, op);
+    }
+}
+
+/* test number class */
+static void
+Class(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op, tmp;
+    std::string expected;
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    mpd_init_rand(tmp);
+    tmp = op;
+
+    context.status(0);
+    std::string calc = tmp.number_class(context);
+    compare_expected(token, calc, expected, expstatus, context);
+    check_equalmem(token, tmp, op);
+}
+
+/* test a unary function */
+typedef Decimal (Decimal::*Decimal_Decimal)() const;
+
+static void
+Dec_Dec_RunSingle(Decimal& result, Decimal& tmp,
+                  const std::vector<std::string>& token,
+                  const Decimal_Decimal func,
+                  const Decimal& op,
+                  const std::string& expected,
+                  const uint32_t expstatus)
+{
+    uint64_t incr = 1;
+    for (uint64_t n = 1; n < UINT64_MAX-100; n += incr) {
+        mpd_init_rand(result);
+        mpd_init_rand(tmp);
+        tmp = op;
+
+        const Decimal save_result = result;
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp.*func)();
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            check_equalmem(token, tmp, op);
+            if (n > 50) {
+                incr = rnd() % 100 + 1;
+            }
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp != &result) {
+        check_equalmem(token, tmp, op);
+    }
+}
+
+static void
+Dec_Dec(Decimal_Decimal func,
+        const std::vector<std::string>& token,
+        const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op, result, tmp;
+    std::string expected;
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    Dec_Dec_RunSingle(result, tmp, token, func, op, expected, expstatus);
+    Dec_Dec_RunSingle(tmp, tmp, token, func, op, expected, expstatus);
+}
+
+/* test a unary function with an optional context argument */
+typedef Decimal (Decimal::*Decimal_DecimalContext)(Context&) const;
+
+static void
+Dec_DecCtx_RunSingle(Decimal& result, Decimal& tmp,
+                     const std::vector<std::string>& token,
+                     const Decimal_DecimalContext func,
+                     const Decimal& op,
+                     const std::string& expected,
+                     const uint32_t expstatus)
+{
+    uint64_t incr = 1;
+    for (uint64_t n = 1; n < UINT64_MAX-100; n += incr) {
+        mpd_init_rand(result);
+        mpd_init_rand(tmp);
+        tmp = op;
+
+        const Decimal save_result = result;
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp.*func)(context);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            check_equalmem(token, tmp, op);
+            if (n > 50) {
+                incr = rnd() % 100 + 1;
+            }
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp != &result) {
+        check_equalmem(token, tmp, op);
+    }
+}
+
+static void
+Dec_DecCtx(Decimal_DecimalContext func,
+           const std::vector<std::string>& token,
+           const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op, result, tmp;
+    std::string expected;
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    Dec_DecCtx_RunSingle(result, tmp, token, func, op, expected, expstatus);
+    Dec_DecCtx_RunSingle(tmp, tmp, token, func, op, expected, expstatus);
+}
+
+/* Same as Dec_DecCtx, but quantize the operand before applying the actual function */
+static void
+Dec_DecCtxWithQuantize(Decimal_DecimalContext func,
+                       const std::vector<std::string>& token,
+                       const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op, scale, result, tmp;
+    std::string expected;
+
+    const size_t n = scan_op_op_expected(op, scale, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+    Triple(token, scale, maxcontext);
+
+    op = op.quantize(scale, maxcontext);
+
+    Dec_DecCtx_RunSingle(result, tmp, token, func, op, expected, expstatus);
+    Dec_DecCtx_RunSingle(tmp, tmp, token, func, op, expected, expstatus);
+}
+
+/* Test a binary function */
+typedef Decimal (Decimal::*Decimal_DecimalDecimalContext)(const Decimal&, Context&) const;
+
+static void
+resolve_status_hack(uint32_t& expstatus, const uint32_t status)
+{
+    /* hack #1 to resolve disagreement with results generated by decimal.py */
+    if ((expstatus & MPD_Invalid_operation) &&
+        (status & MPD_Division_impossible)) {
+        expstatus = MPD_Division_impossible;
+    }
+
+    /* hack #2 to resolve disagreement with results generated by decimal.py */
+    if ((expstatus & MPD_Invalid_operation) &&
+        (status & MPD_Division_undefined)) {
+        expstatus = MPD_Division_undefined;
+    }
+}
+
+static void
+Dec_DecDecCtx_RunSingle(Decimal& result, Decimal& tmp1, Decimal& tmp2,
+                        const std::vector<std::string>& token,
+                        const Decimal_DecimalDecimalContext func,
+                        const Decimal& op1, const Decimal &op2,
+                        const std::string& expected,
+                        const uint32_t expstatus)
+{
+    uint64_t incr = 1;
+    for (uint64_t n = 1; n < UINT64_MAX-100; n += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        tmp1 = op1;
+        tmp2 = op2;
+
+        const Decimal save_result = result;
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp1.*func)(tmp2, context);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            if (n > 50) {
+               incr = rnd() % 100 + 1;
+            }
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp1 != &result) {
+        check_equalmem(token, tmp1, op1);
+    }
+    if (&tmp2 != &result) {
+        check_equalmem(token, tmp2, op2);
+    }
+}
+
+static void
+Dec_DecDecCtx(const Decimal_DecimalDecimalContext func,
+              const std::vector<std::string>& token,
+              const bool scan_equal,
+              const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal result, tmp1, tmp2;
+    Decimal op1, op2;
+    std::string expected;
+    uint32_t expstatus;
+    size_t n;
+
+    if (scan_equal) {
+        n = scan_op_expected(op1, expected, token, maxcontext);
+        op2 = op1;
+    }
+    else {
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    context.status(0);
+    result = (op1.*func)(op2, context);
+
+    Dec_DecDecCtx_RunSingle(result, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+    Dec_DecDecCtx_RunSingle(tmp1, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+    Dec_DecDecCtx_RunSingle(tmp2, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+
+    if (equalmem(op1, op2)) {
+        Dec_DecDecCtx_RunSingle(result, tmp1, tmp1, token, func, op1, op2, expected, expstatus);
+        Dec_DecDecCtx_RunSingle(tmp1, tmp1, tmp1, token, func, op1, op2, expected, expstatus);
+    }
+}
+
+/* Test a binary function with a binary result */
+typedef std::pair<Decimal, Decimal> (Decimal::*DecimalPair_DecimalDecimalContext)(const Decimal&, Context&) const;
+
+static void
+DecPair_DecDecCtx_RunSingle(std::pair<Decimal, Decimal>& result, Decimal& tmp1, Decimal& tmp2,
+                            const std::vector<std::string>& token,
+                            const DecimalPair_DecimalDecimalContext func,
+                            const Decimal& op1, const Decimal &op2,
+                            const std::string& expected1, const std::string& expected2,
+                            const uint32_t expstatus)
+{
+    uint64_t incr = 1;
+    for (uint64_t n = 1; n < UINT64_MAX-100; n += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        tmp1 = op1;
+        tmp2 = op2;
+        const Decimal first = result.first;
+        const Decimal second = result.second;
+
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp1.*func)(tmp2, context);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result.first, first);
+            check_equalmem(token, result.second, second);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            if (n > 50) {
+                incr = rnd() % 100 + 1;
+            }
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    std::string calc = result.first.to_sci();
+    compare_expected(token, calc, expected1, expstatus, context);
+
+    calc = result.second.to_sci();
+    compare_expected(token, calc, expected2, expstatus, context);
+
+    if (&tmp1 != &result.first && &tmp1 != &result.second) {
+        check_equalmem(token, tmp1, op1);
+    }
+    if (&tmp2 != &result.first && &tmp2 != &result.second) {
+        check_equalmem(token, tmp2, op2);
+    }
+}
+
+static void
+DecPair_DecDecCtx(const DecimalPair_DecimalDecimalContext func,
+                  const std::vector<std::string>& token,
+                  const bool scan_equal,
+                  const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    std::pair<Decimal, Decimal> result;
+    Decimal tmp1, tmp2;
+    Decimal op1, op2;
+    std::string expected1, expected2;
+    uint32_t expstatus;
+    size_t n;
+
+    if (scan_equal) {
+        n = scan_op_expected_expected(op1, expected1, expected2,
+                                      token, maxcontext);
+        op2 = op1;
+    }
+    else {
+        n = scan_op_op_expected_expected(op1, op2, expected1, expected2,
+                                         token, maxcontext);
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    context.status(0);
+    result = (op1.*func)(op2, context);
+    resolve_status_hack(expstatus, context.status());
+
+    DecPair_DecDecCtx_RunSingle(result, tmp1, tmp2, token, func, op1, op2, expected1, expected2, expstatus);
+    DecPair_DecDecCtx_RunSingle(result, result.first, tmp2, token, func, op1, op2, expected1, expected2, expstatus);
+    DecPair_DecDecCtx_RunSingle(result, tmp1, result.first, token, func, op1, op2, expected1, expected2, expstatus);
+    DecPair_DecDecCtx_RunSingle(result, result.second, tmp2, token, func, op1, op2, expected1, expected2, expstatus);
+    DecPair_DecDecCtx_RunSingle(result, tmp1, result.second, token, func, op1, op2, expected1, expected2, expstatus);
+
+    if (equalmem(op1, op2)) {
+        DecPair_DecDecCtx_RunSingle(result, tmp1, tmp1, token, func, op1, op2, expected1, expected2, expstatus);
+        DecPair_DecDecCtx_RunSingle(result, result.first, result.first, token, func, op1, op2, expected1, expected2, expstatus);
+        DecPair_DecDecCtx_RunSingle(result, result.second, result.second, token, func, op1, op2, expected1, expected2, expstatus);
+    }
+}
+
+/* Test a ternary function */
+typedef Decimal (Decimal::*Decimal_DecimalDecimalDecimalContext)(const Decimal&, const Decimal&, Context&) const;
+
+static void
+Dec_DecDecDecCtx_RunSingle(Decimal& result, Decimal& tmp1, Decimal& tmp2, Decimal& tmp3,
+                           const std::vector<std::string>& token,
+                           const Decimal_DecimalDecimalDecimalContext func,
+                           const Decimal& op1, const Decimal &op2, const Decimal &op3,
+                           const std::string& expected,
+                           const uint32_t expstatus)
+{
+    uint64_t incr = 1;
+    for (uint64_t n = 1; n < UINT64_MAX-100; n += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_init_rand(tmp3);
+        tmp1 = op1;
+        tmp2 = op2;
+        tmp3 = op3;
+
+        const Decimal save_result = result;
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp1.*func)(tmp2, tmp3, context);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            check_equalmem(token, tmp3, op3);
+            if (n > 100) {
+                incr = rnd() % 100 + 1;
+            }
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp1 != &result) {
+        check_equalmem(token, tmp1, op1);
+    }
+    if (&tmp2 != &result) {
+        check_equalmem(token, tmp2, op2);
+    }
+    if (&tmp3 != &result) {
+        check_equalmem(token, tmp3, op3);
+    }
+}
+
+enum ternary_equal { OpOpOp, EqEqOp, EqOpEq, OpEqEq, EqEqEq };
+static void
+Dec_DecDecDecCtx(const Decimal_DecimalDecimalDecimalContext func,
+                 enum ternary_equal scan_equal,
+                 const std::vector<std::string>& token,
+                 const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal result, tmp1, tmp2, tmp3;
+    Decimal op1, op2, op3;
+    std::string expected;
+    uint32_t expstatus;
+    size_t n;
+
+    switch (scan_equal) {
+    case OpOpOp:
+        n = scan_op_op_op_expected(op1, op2, op3, expected, token, maxcontext);
+        break;
+    case EqEqOp:
+        n = scan_op_op_expected(op1, op3, expected, token, maxcontext);
+        op2 = op1;
+        break;
+    case EqOpEq:
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+        op3 = op1;
+        break;
+    case OpEqEq:
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+        op3 = op2;
+        break;
+    case EqEqEq:
+        n = scan_op_expected(op1, expected, token, maxcontext);
+        op3 = op2 = op1;
+        break;
+    default:
+        err_raise("internal error: unexpected tag");
+        break;
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+    Triple(token, op3, maxcontext);
+
+    Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp2, tmp3, token, func, op1, op2, op3, expected, expstatus);
+    Dec_DecDecDecCtx_RunSingle(result, result, tmp2, tmp3, token, func, op1, op2, op3, expected, expstatus);
+    Dec_DecDecDecCtx_RunSingle(result, tmp1, result, tmp3, token, func, op1, op2, op3, expected, expstatus);
+    Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp2, result, token, func, op1, op2, op3, expected, expstatus);
+
+    if (equalmem(op1, op2)) {
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp1, tmp3, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, result, result, tmp3, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp1, result, token, func, op1, op2, op3, expected, expstatus);
+    }
+
+    if (equalmem(op1, op3)) {
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp2, tmp1, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, result, tmp2, result, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, result, tmp1, token, func, op1, op2, op3, expected, expstatus);
+    }
+
+    if (equalmem(op2, op3)) {
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp2, tmp2, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, result, tmp2, tmp2, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, result, result, token, func, op1, op2, op3, expected, expstatus);
+    }
+
+    if (equalmem(op1, op2) && equalmem(op1, op3)) {
+        Dec_DecDecDecCtx_RunSingle(result, tmp1, tmp1, tmp1, token, func, op1, op2, op3, expected, expstatus);
+        Dec_DecDecDecCtx_RunSingle(result, result, result, result, token, func, op1, op2, op3, expected, expstatus);
+    }
+}
+
+/* Test a binary function with no context argument */
+typedef Decimal (Decimal::*Decimal_DecimalDecimal)(const Decimal&) const;
+
+static void
+Dec_DecDec_RunSingle(Decimal& result, Decimal& tmp1, Decimal& tmp2,
+                     const std::vector<std::string>& token,
+                     const Decimal_DecimalDecimal func,
+                     const Decimal& op1, const Decimal &op2,
+                     const std::string& expected,
+                     const uint32_t expstatus)
+{
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        tmp1 = op1;
+        tmp2 = op2;
+
+        const Decimal save_result = result;
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp1.*func)(tmp2);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp1 != &result) {
+        check_equalmem(token, tmp1, op1);
+    }
+    if (&tmp2 != &result) {
+        check_equalmem(token, tmp2, op2);
+    }
+}
+
+static void
+Dec_DecDec(const Decimal_DecimalDecimal func,
+           const std::vector<std::string>& token,
+           const bool scan_equal,
+           const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal result, tmp1, tmp2;
+    Decimal op1, op2;
+    std::string expected;
+    uint32_t expstatus;
+    size_t n;
+
+    if (scan_equal) {
+        n = scan_op_expected(op1, expected, token, maxcontext);
+        op2 = op1;
+    }
+    else {
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    Dec_DecDec_RunSingle(result, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+    Dec_DecDec_RunSingle(tmp1, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+    Dec_DecDec_RunSingle(tmp2, tmp1, tmp2, token, func, op1, op2, expected, expstatus);
+
+    if (equalmem(op1, op2)) {
+        Dec_DecDec_RunSingle(result, tmp1, tmp1, token, func, op1, op2, expected, expstatus);
+        Dec_DecDec_RunSingle(tmp1, tmp1, tmp1, token, func, op1, op2, expected, expstatus);
+    }
+}
+
+/* Test a binary function that returns an integer result */
+typedef int (Decimal::*Int_DecimalDecimal)(const Decimal&) const;
+
+static void
+Int_DecDec_RunSingle(Decimal& tmp1, Decimal& tmp2,
+                     const enum skip_cmp skip,
+                     const std::vector<std::string>& token,
+                     const Int_DecimalDecimal func,
+                     const Decimal& op1, const Decimal &op2,
+                     const std::string& expected,
+                     const uint32_t expstatus)
+{
+    int int_result = -101;
+
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        tmp1 = op1;
+        tmp2 = op2;
+
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            int_result = (tmp1.*func)(tmp2);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    char buf[11];
+    snprintf(buf, sizeof buf, "%d", int_result);
+    if (skip == SKIP_NONE || int_result != INT_MAX) {
+        compare_expected(token, buf, expected, expstatus, context);
+    }
+    check_equalmem(token, tmp1, op1);
+    check_equalmem(token, tmp2, op2);
+}
+
+static void
+Int_DecDec(const Int_DecimalDecimal func,
+           const std::vector<std::string>& token,
+           const enum skip_cmp skip,
+           const bool scan_equal,
+           const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal tmp1, tmp2;
+    Decimal op1, op2;
+    std::string expected;
+    uint32_t expstatus;
+    size_t n;
+
+    if (scan_equal) {
+        n = scan_op_expected(op1, expected, token, maxcontext);
+        op2 = op1;
+    }
+    else {
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    Int_DecDec_RunSingle(tmp1, tmp2, skip, token, func, op1, op2, expected, expstatus);
+    if (equalmem(op1, op2)) {
+        Int_DecDec_RunSingle(tmp1, tmp1, skip, token, func, op1, op2, expected, expstatus);
+    }
+}
+
+/* Test a binary function that returns a bool result */
+typedef bool (Decimal::*Bool_DecimalDecimal)(const Decimal&) const;
+
+static void
+Bool_DecDec_RunSingle(Decimal& tmp1, Decimal& tmp2,
+                      const enum skip_cmp skip,
+                      const std::vector<std::string>& token,
+                      const Bool_DecimalDecimal func,
+                      const Decimal& op1, const Decimal &op2,
+                      const std::string& expected,
+                      const uint32_t expstatus)
+{
+    int int_result = -101;
+
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        tmp1 = op1;
+        tmp2 = op2;
+
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            int_result = (tmp1.*func)(tmp2);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            DECIMAL_ASSERT(int_result== INT_MAX, token);
+            check_equalmem(token, tmp1, op1);
+            check_equalmem(token, tmp2, op2);
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    char buf[11];
+    snprintf(buf, 11, "%d", int_result);
+    if (skip == SKIP_NONE || int_result != INT_MAX) {
+        compare_expected(token, buf, expected, expstatus, context);
+    }
+    check_equalmem(token, tmp1, op1);
+    check_equalmem(token, tmp2, op2);
+}
+
+static void
+Bool_DecDec(const Bool_DecimalDecimal func,
+            const std::vector<std::string>& token,
+            const enum skip_cmp skip,
+            const bool scan_equal,
+            const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal tmp1, tmp2;
+    Decimal op1, op2;
+    std::string expected;
+    uint32_t expstatus;
+    size_t n;
+
+    if (scan_equal) {
+        n = scan_op_expected(op1, expected, token, maxcontext);
+        op2 = op1;
+    }
+    else {
+        n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+    }
+    expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    Bool_DecDec_RunSingle(tmp1, tmp2, skip, token, func, op1, op2, expected, expstatus);
+    if (equalmem(op1, op2)) {
+        Bool_DecDec_RunSingle(tmp1, tmp1, skip, token, func, op1, op2, expected, expstatus);
+    }
+}
+
+static mpd_ssize_t
+scan_ssize(const std::string& tok)
+{
+    errno = 0;
+    mpd_ssize_t x = strtossize(tok.c_str(), nullptr, 10);
+    if (errno != 0) {
+        err_raise("invalid conversion to ssize_t");
+    }
+    return x;
+}
+
+/* Test a function with a Decimal and an int64_t operand */
+typedef Decimal (Decimal::*Decimal_DecimalInt64Context)(int64_t, Context&) const;
+
+static void
+Dec_DecInt64_RunSingle(Decimal& result, Decimal& tmp,
+                       const std::vector<std::string>& token,
+                       const Decimal_DecimalInt64Context func,
+                       const Decimal& op,
+                       const int64_t i64,
+                       const std::string& expected,
+                       const uint32_t expstatus)
+{
+    /* Allocation failures */
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+        mpd_init_rand(tmp);
+        tmp = op;
+
+        context.status(0);
+        set_alloc_fail(context, n);
+        try {
+            result = (tmp.*func)(i64, context);
+        }
+        catch (MallocError&) {
+            check_equalmem(token, tmp, op);
+            set_alloc(context);
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+    if (&tmp != &result) {
+        check_equalmem(token, tmp, op);
+    }
+}
+
+static void
+Dec_DecInt64Ctx(const Decimal_DecimalInt64Context func,
+                const std::vector<std::string>& token,
+                const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal result, tmp;
+    Decimal op1, op2;
+    std::string expected;
+
+    const size_t n = scan_op_op_expected(op1, op2, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op1, maxcontext);
+    Triple(token, op2, maxcontext);
+
+    if (op2.isspecial() || op2.exponent() != 0) {
+        return;
+    }
+
+    const int64_t i64 = mpd_get_ssize(op2.getconst(), maxcontext.get());
+    if (maxcontext.status() & MPD_Invalid_operation) {
+        return;
+    }
+
+    Dec_DecInt64_RunSingle(result, tmp, token, func, op1, i64, expected, expstatus);
+    Dec_DecInt64_RunSingle(tmp, tmp, token, func, op1, i64, expected, expstatus);
+}
+
+/* Test decimal::ln10 */
+static void
+ln10(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal result;
+    Decimal op;
+    std::string expected;
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    if (op.isspecial() || op.exponent() != 0) {
+        return;
+    }
+
+    const int64_t i64 = mpd_get_ssize(op.getconst(), maxcontext.get());
+    if (maxcontext.status() & MPD_Invalid_operation) {
+        return;
+    }
+
+    for (uint64_t i = 1; i < UINT64_MAX-1; i++) {
+        const Decimal save_result = result;
+
+        context.status(0);
+        set_alloc_fail(context, i);
+        try {
+            result = Decimal::ln10(i64, context);
+        }
+        catch (MallocError&) {
+            set_alloc(context);
+            check_equalmem(token, result, save_result);
+            continue;
+        }
+
+        set_alloc(context);
+        break;
+    }
+
+    const std::string calc = result.to_sci();
+    compare_expected(token, calc, expected, expstatus, context);
+}
+
+/* Test u64() */
+static void
+u64_DecCtx(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op;
+    uint64_t u64;
+    char calc[23];
+    std::string expected;
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    context.status(0);
+    try {
+        u64 = op.u64();
+    }
+    catch (ValueError&) {
+        DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+        return;
+    }
+
+    snprintf(calc, 23, "%" PRIu64, u64);
+    compare_expected(token, calc, expected, expstatus, context);
+}
+
+/* Test u32() */
+static void
+u32_DecCtx(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op;
+    std::string expected;
+    uint32_t u32;
+    char calc[23];
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    context.status(0);
+    try {
+        u32 = op.u32();
+    }
+    catch (ValueError&) {
+        DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+        return;
+    }
+
+    snprintf(calc, sizeof calc, "%" PRIu32, u32);
+    compare_expected(token, calc, expected, 0, context);
+}
+
+/* Test a function returning an int64_t */
+static void
+i64_DecCtx(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op;
+    std::string expected;
+    int64_t i64;
+    char calc[23];
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    context.status(0);
+    try {
+        i64 = op.i64();
+    }
+    catch (ValueError&) {
+        DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+        return;
+    }
+
+    snprintf(calc, sizeof calc, "%" PRIi64, i64);
+    compare_expected(token, calc, expected, 0, context);
+}
+
+/* Test a function returning an int64_t */
+static void
+i32_DecCtx(const std::vector<std::string>& token, const bool extended)
+{
+    Context maxcontext{readcontext(extended)};
+    Decimal op;
+    std::string expected;
+    int32_t i32;
+    char calc[23];
+
+    const size_t n = scan_op_expected(op, expected, token, maxcontext);
+    const uint32_t expstatus = scan_conditions(token, n);
+    Triple(token, op, maxcontext);
+
+    context.status(0);
+    try {
+        i32 = op.i32();
+    }
+    catch (ValueError&) {
+        DECIMAL_ASSERT(expstatus == MPD_Invalid_operation, token);
+        return;
+    }
+
+    snprintf(calc, sizeof calc, "%" PRIi32, i32);
+    compare_expected(token, calc, expected, 0, context);
+}
+
+static void
+test_copy_constructor(void)
+{
+    const std::vector<std::string> token{"copy_constr"};
+    Decimal a = Decimal(1).shiftl((decimal::MINALLOC*MPD_RDIGITS));
+    Decimal b = Decimal(1).shiftl((2*decimal::MINALLOC*MPD_RDIGITS));
+    Decimal c = 2025;
+    Context ctx;
+
+    /* static ==> dynamic */
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+
+        set_alloc_fail(ctx, n);
+        try {
+            c = a;
+        }
+        catch (MallocError&) {
+            set_alloc(ctx);
+            DECIMAL_ASSERT(c == 2025, token);
+            continue;
+        }
+
+        set_alloc(ctx);
+        break;
+    }
+
+    DECIMAL_ASSERT(c == a, token);
+
+    /* static ==> larger dynamic */
+    for (uint64_t n = 1; n < UINT64_MAX-1; n++) {
+
+        set_alloc_fail(ctx, n);
+        try {
+            c = b;
+        }
+        catch (MallocError&) {
+            set_alloc(ctx);
+            DECIMAL_ASSERT(c == a, token);
+            continue;
+        }
+
+        set_alloc(ctx);
+        break;
+    }
+
+    DECIMAL_ASSERT(c == b, token);
+}
+
+/* process an input stream of test cases */
+static bool skip_bignum = false;
+static std::atomic<uint32_t> bignum_counter{0};
+
+static void
+do_stream(std::istream& in, bool extended=true)
+{
+    std::string line;
+
+    context = Context(testcontext(extended));
+
+    while (std::getline(in, line)) {
+        std::vector<std::string> token = split(line);
+        if (token.size() == 0) {
+            continue;
+        }
+
+        if (skip_bignum) { /* small thread stack */
+            bool cont = false;
+            for (const std::string &s : token) {
+                /* This is a simple heuristic, which works for the test cases
+                   in additional.topTest. */
+                if (s.size() > 4096) {
+                    cont = true;
+                    bignum_counter++;
+                    break;
+                }
+            }
+            if (cont) {
+                continue;
+            }
+        }
+
+        if (startswith(token.at(0), "ExtendedRange")) {
+            if (token.at(1) == "1") {
+                extended = true;
+            }
+            else if (token.at(1) == "0") {
+                extended = false;
+            }
+            else {
+                err_token(token, "value must be 1 or 0");
+            }
+            continue;
+        }
+
+        if (startswith(token.at(0), "Precision")) {
+            if (token.at(1) == "MAX_PREC") {
+                context.prec(MPD_MAX_PREC);
+            }
+            else {
+                mpd_context_t ctx = *context.getconst();
+                const mpd_ssize_t l = scan_ssize(token.at(1));
+                ctx.prec = l;
+                context = Context(ctx);
+            }
+            continue;
+        }
+
+        if (startswith(token.at(0), "MinExponent")) {
+            if (token.at(1) == "MIN_EMIN") {
+                context.emin(MPD_MIN_EMIN);
+            }
+            else {
+                mpd_context_t ctx = *context.getconst();
+                const mpd_ssize_t l = scan_ssize(token.at(1));
+                ctx.emin = l;
+                context = Context(ctx);
+            }
+            continue;
+        }
+
+        if (startswith(token.at(0), "MaxExponent")) {
+            if (token.at(1) == "MAX_EMAX") {
+                context.emax(MPD_MAX_EMAX);
+            }
+            else {
+                mpd_context_t ctx = *context.getconst();
+                const mpd_ssize_t l = scan_ssize(token.at(1));
+                ctx.emax = l;
+                context = Context(ctx);
+            }
+            continue;
+        }
+
+        if (startswith(token.at(0), "Rounding")) {
+            if (eqtoken(token.at(1), "Up")) {
+                context.round(MPD_ROUND_UP);
+            }
+            else if (eqtoken(token.at(1), "Down")) {
+                context.round(MPD_ROUND_DOWN);
+            }
+            else if (eqtoken(token.at(1), "Ceiling")) {
+                context.round(MPD_ROUND_CEILING);
+            }
+            else if (eqtoken(token.at(1), "Floor")) {
+                context.round(MPD_ROUND_FLOOR);
+            }
+            else if (eqtoken(token.at(1), "Half_up")) {
+                context.round(MPD_ROUND_HALF_UP);
+            }
+            else if (eqtoken(token.at(1), "Half_down")) {
+                context.round(MPD_ROUND_HALF_DOWN);
+            }
+            else if (eqtoken(token.at(1), "Half_even")) {
+                context.round(MPD_ROUND_HALF_EVEN);
+            }
+            else if (eqtoken(token.at(1), "05up")) {
+                context.round(MPD_ROUND_05UP);
+            }
+            else {
+                err_token(token, "invalid rounding mode");
+            }
+
+            continue;
+        }
+
+        if (startswith(token.at(0), "Clamp")) {
+            const int l = static_cast<int>(scan_ssize(token.at(1)));
+            context.clamp(l);
+            continue;
+        }
+
+        if (startswith(token.at(0), "Locale")) {
+            if (setlocale(LC_NUMERIC, token.at(1).c_str()) == nullptr) {
+                err_token(token, "invalid or missing locale");
+            }
+            continue;
+        }
+
+        if (startswith(token.at(0), "Version")) {
+            continue;  /* optional directive */
+        }
+
+        if (startswith(token.at(0), "Extended")) {
+            continue;  /* optional directive */
+        }
+
+        mpd_assert_context_ok(context, token);
+
+        /*
+         * Actual tests start here:
+         *   - token.at(0) is the id
+         *   - token.at(1) is the operation type
+         *   - testno can be used for setting a watchpoint in the debugger
+         */
+        const unsigned long testno = get_testno(token);
+        (void)testno;
+
+        if (skip_test(token.at(0))) {
+            continue;  /* id is in the skip list */
+        }
+
+#ifdef MPD_CONFIG_64
+        if (startswith(token.at(0), "cov32")) {
+            continue;  /* skip 32-bit specific coverage tests */
+        }
+#else
+        if (startswith(token.at(0), "cov64")) {
+            continue;  /* skip 64-bit specific coverage tests */
+        }
+#endif
+
+        if (startswith(token.at(0), "pwmx")) {
+            token.at(1) = std::string("powmod");
+        }
+
+        /* Unary functions with std::string result */
+        if (eqtoken(token.at(1), "tosci") || eqtoken(token.at(1), "apply")) {
+            Str_DecCtx(&Decimal::to_sci, token, extended);
+        }
+        else if (eqtoken(token.at(1), "toeng")) {
+            Str_DecCtx(&Decimal::to_eng, token, extended);
+        }
+        else if (eqtoken(token.at(1), "format")) {
+            Fmt(token, extended);
+        }
+
+        /* Unary function with const char * result */
+        else if (eqtoken(token.at(1), "class")) {
+            Class(token, extended);
+        }
+
+        /* Unary functions with Decimal result */
+        else if (eqtoken(token.at(1), "abs")) {
+            Dec_DecCtx(&Decimal::abs, token, extended);
+        }
+        else if (eqtoken(token.at(1), "copy")) {
+            Dec_Dec(&Decimal::copy, token, extended);
+        }
+        else if (eqtoken(token.at(1), "copyabs")) {
+            Dec_Dec(&Decimal::copy_abs, token, extended);
+        }
+        else if (eqtoken(token.at(1), "copynegate")) {
+            Dec_Dec(&Decimal::copy_negate, token, extended);
+        }
+        else if (eqtoken(token.at(1), "exp")) {
+            if (extended) {
+                if (testno != 126) {
+                    /* Almost all test cases in the official tests are
+                       correctly rounded even when context.allcr is not
+                       set. */
+                    context.allcr(0);
+                    Dec_DecCtx(&Decimal::exp, token, extended);
+                    context.allcr(1);
+                }
+            }
+            Dec_DecCtx(&Decimal::exp, token, extended);
+        }
+        else if (eqtoken(token.at(1), "invert")) {
+            Dec_DecCtx(&Decimal::logical_invert, token, extended);
+        }
+        else if (eqtoken(token.at(1), "invroot")) {
+            Dec_DecCtx(&Decimal::invroot, token, extended);
+        }
+        else if (eqtoken(token.at(1), "ln")) {
+            if (extended) {
+                /* All test cases in the official tests are correctly rounded
+                   even when context.allcr is not set. */
+                context.allcr(0);
+                Dec_DecCtx(&Decimal::ln, token, extended);
+                context.allcr(1);
+            }
+            Dec_DecCtx(&Decimal::ln, token, extended);
+        }
+        else if (eqtoken(token.at(1), "log10")) {
+            if (extended) {
+                /* All test cases in the official tests are correctly rounded
+                   even when context.allcr is not set. */
+                context.allcr(0);
+                Dec_DecCtx(&Decimal::log10, token, extended);
+                context.allcr(1);
+            }
+            Dec_DecCtx(&Decimal::log10, token, extended);
+        }
+        else if (eqtoken(token.at(1), "logb")) {
+            Dec_DecCtx(&Decimal::logb, token, extended);
+        }
+        else if (eqtoken(token.at(1), "minus")) {
+            Dec_DecCtx(&Decimal::minus, token, extended);
+        }
+        else if (eqtoken(token.at(1), "nextminus")) {
+            Dec_DecCtx(&Decimal::next_minus, token, extended);
+        }
+        else if (eqtoken(token.at(1), "nextplus")) {
+            Dec_DecCtx(&Decimal::next_plus, token, extended);
+        }
+        else if (eqtoken(token.at(1), "plus")) {
+            Dec_DecCtx(&Decimal::plus, token, extended);
+        }
+        else if (eqtoken(token.at(1), "reduce")) {
+            Dec_DecCtx(&Decimal::reduce, token, extended);
+        }
+        else if (eqtoken(token.at(1), "squareroot")) {
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(16000000);
+            #endif
+            Dec_DecCtx(&Decimal::sqrt, token, extended);
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token.at(1), "quantize_squareroot")) {
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(16000000);
+            #endif
+            Dec_DecCtxWithQuantize(&Decimal::sqrt, token, extended);
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token.at(1), "tointegral")) {
+            Dec_DecCtx(&Decimal::to_integral, token, extended);
+        }
+        else if (eqtoken(token.at(1), "tointegralx")) {
+            Dec_DecCtx(&Decimal::to_integral_exact, token, extended);
+        }
+        else if (eqtoken(token.at(1), "floor")) {
+            Dec_DecCtx(&Decimal::floor, token, extended);
+        }
+        else if (eqtoken(token.at(1), "ceil")) {
+            Dec_DecCtx(&Decimal::ceil, token, extended);
+        }
+        else if (eqtoken(token.at(1), "trunc")) {
+            Dec_DecCtx(&Decimal::trunc, token, extended);
+        }
+
+        /* Binary function returning an int */
+        else if (eqtoken(token.at(1), "samequantum")) {
+            Bool_DecDec(&Decimal::same_quantum, token, SKIP_NONE, false, extended);
+        }
+
+        /* Binary function returning an int, equal operands */
+        else if (eqtoken(token.at(1), "samequantum_eq")) {
+            Bool_DecDec(&Decimal::same_quantum, token, SKIP_NONE, true, extended);
+        }
+
+        /* Binary functions with Decimal result */
+        else if (eqtoken(token.at(1), "add")) {
+            Dec_DecDecCtx(&Decimal::add, token, false, extended);
+            Dec_DecDec(&Decimal::operator+, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "and")) {
+            Dec_DecDecCtx(&Decimal::logical_and, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "copysign")) {
+            Dec_DecDec(&Decimal::copy_sign, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "divide")) {
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(16000000);
+            #endif
+            Dec_DecDecCtx(&Decimal::div, token, false, extended);
+            Dec_DecDec(&Decimal::operator/, token, false, extended);
+            #ifdef MPD_CONFIG_32
+                if (context.prec() == MPD_MAX_PREC) set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token.at(1), "divideint")) {
+            Dec_DecDecCtx(&Decimal::divint, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "max")) {
+            Dec_DecDecCtx(&Decimal::max, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "maxmag") || eqtoken(token.at(1), "max_mag")) {
+            Dec_DecDecCtx(&Decimal::max_mag, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "min")) {
+            Dec_DecDecCtx(&Decimal::min, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "minmag") || eqtoken(token.at(1), "min_mag")) {
+            Dec_DecDecCtx(&Decimal::min_mag, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "multiply")) {
+            Dec_DecDecCtx(&Decimal::mul, token, false, extended);
+            Dec_DecDec(&Decimal::operator*, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "nexttoward")) {
+            Dec_DecDecCtx(&Decimal::next_toward, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "or")) {
+            Dec_DecDecCtx(&Decimal::logical_or, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "power")) {
+            if (extended) {
+                /* All test cases in the official tests are correctly rounded
+                   even when context.allcr is not set. */
+                context.allcr(0);
+                Dec_DecDecCtx(&Decimal::pow, token, false, extended);
+                context.allcr(1);
+            }
+            Dec_DecDecCtx(&Decimal::pow, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "quantize")) {
+            Dec_DecDecCtx(&Decimal::quantize, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "resc")) {
+            Dec_DecInt64Ctx(&Decimal::rescale, token, extended);
+        }
+        else if (eqtoken(token.at(1), "remainder")) {
+            Dec_DecDecCtx(&Decimal::rem, token, false, extended);
+            Dec_DecDec(&Decimal::operator%, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "remaindernear")) {
+            Dec_DecDecCtx(&Decimal::rem_near, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "rotate")) {
+            Dec_DecDecCtx(&Decimal::rotate, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "scaleb")) {
+            Dec_DecDecCtx(&Decimal::scaleb, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "shift")) {
+            Dec_DecDecCtx(&Decimal::shift, token, false, extended);
+            if (extended) {
+                Dec_DecInt64Ctx(&Decimal::shiftn, token, extended);
+            }
+        }
+        else if (eqtoken(token.at(1), "subtract")) {
+            Dec_DecDecCtx(&Decimal::sub, token, false, extended);
+            Dec_DecDec(&Decimal::operator-, token, false, extended);
+        }
+        else if (eqtoken(token.at(1), "xor")) {
+            Dec_DecDecCtx(&Decimal::logical_xor, token, false, extended);
+        }
+
+        /* Binary functions with Decimal result, equal operands */
+        else if (eqtoken(token.at(1), "add_eq")) {
+            Dec_DecDecCtx(&Decimal::add, token, true, extended);
+            Dec_DecDec(&Decimal::operator+, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "and_eq")) {
+            Dec_DecDecCtx(&Decimal::logical_and, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "copysign_eq")) {
+            Dec_DecDec(&Decimal::copy_sign, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "divide_eq")) {
+            Dec_DecDecCtx(&Decimal::div, token, true, extended);
+            Dec_DecDec(&Decimal::operator/, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "divideint_eq")) {
+            Dec_DecDecCtx(&Decimal::divint, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "max_eq")) {
+            Dec_DecDecCtx(&Decimal::max, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "maxmag_eq")) {
+            Dec_DecDecCtx(&Decimal::max_mag, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "min_eq")) {
+            Dec_DecDecCtx(&Decimal::min, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "minmag_eq")) {
+            Dec_DecDecCtx(&Decimal::min_mag, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "multiply_eq")) {
+            Dec_DecDecCtx(&Decimal::mul, token, true, extended);
+            Dec_DecDec(&Decimal::operator*, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "nexttoward_eq")) {
+            Dec_DecDecCtx(&Decimal::next_toward, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "or_eq")) {
+            Dec_DecDecCtx(&Decimal::logical_or, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "power_eq")) {
+            if (extended) {
+                /* see power */
+                context.allcr(0);
+                Dec_DecDecCtx(&Decimal::pow, token, true, extended);
+                context.allcr(1);
+            }
+            Dec_DecDecCtx(&Decimal::pow, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "quantize_eq")) {
+            Dec_DecDecCtx(&Decimal::quantize, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "remainder_eq")) {
+            Dec_DecDecCtx(&Decimal::rem, token, true, extended);
+            Dec_DecDec(&Decimal::operator%, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "remaindernear_eq")) {
+            Dec_DecDecCtx(&Decimal::rem_near, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "rotate_eq")) {
+            Dec_DecDecCtx(&Decimal::rotate, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "scaleb_eq")) {
+            Dec_DecDecCtx(&Decimal::scaleb, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "shift_eq")) {
+            Dec_DecDecCtx(&Decimal::shift, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "subtract_eq")) {
+            Dec_DecDecCtx(&Decimal::sub, token, true, extended);
+            Dec_DecDec(&Decimal::operator-, token, true, extended);
+        }
+        else if (eqtoken(token.at(1), "xor_eq")) {
+            Dec_DecDecCtx(&Decimal::logical_xor, token, true, extended);
+        }
+
+        /* Binary function with Decimal pair result */
+        else if (eqtoken(token.at(1), "divmod")) {
+            DecPair_DecDecCtx(&Decimal::divmod, token, false, extended);
+        }
+        /* Binary function with Decimal pair result, equal operands */
+        else if (eqtoken(token.at(1), "divmod_eq")) {
+            DecPair_DecDecCtx(&Decimal::divmod, token, true, extended);
+        }
+
+        /* Ternary functions with Decimal result */
+        else if (eqtoken(token.at(1), "fma")) {
+            Dec_DecDecDecCtx(&Decimal::fma, OpOpOp, token, extended);
+        }
+        else if (eqtoken(token.at(1), "powmod")) {
+            Dec_DecDecDecCtx(&Decimal::powmod, OpOpOp, token, extended);
+        }
+
+        /* Ternary functions with Decimal result, eq_eq_op */
+        else if (eqtoken(token.at(1), "fma_eq_eq_op")) {
+            Dec_DecDecDecCtx(&Decimal::fma, EqEqOp, token, extended);
+        }
+        else if (eqtoken(token.at(1), "powmod_eq_eq_op")) {
+            Dec_DecDecDecCtx(&Decimal::powmod, EqEqOp, token, extended);
+        }
+
+        /* Ternary functions with Decimal result, eq_op_eq */
+        else if (eqtoken(token.at(1), "fma_eq_op_eq")) {
+            Dec_DecDecDecCtx(&Decimal::fma, EqOpEq, token, extended);
+        }
+        else if (eqtoken(token.at(1), "powmod_eq_op_eq")) {
+            Dec_DecDecDecCtx(&Decimal::powmod, EqOpEq, token, extended);
+        }
+
+        /* Ternary functions with Decimal result, op_eq_eq */
+        else if (eqtoken(token.at(1), "fma_op_eq_eq")) {
+            Dec_DecDecDecCtx(&Decimal::fma, OpEqEq, token, extended);
+        }
+        else if (eqtoken(token.at(1), "powmod_op_eq_eq")) {
+            Dec_DecDecDecCtx(&Decimal::powmod, OpEqEq, token, extended);
+        }
+
+        /* Ternary functions with Decimal result, eq_eq_eq */
+        else if (eqtoken(token.at(1), "fma_eq_eq_eq")) {
+            Dec_DecDecDecCtx(&Decimal::fma, EqEqEq, token, extended);
+        }
+        else if (eqtoken(token.at(1), "powmod_eq_eq_eq")) {
+            Dec_DecDecDecCtx(&Decimal::powmod, EqEqEq, token, extended);
+        }
+
+        /* Special cases for the comparison functions */
+        else if (eqtoken(token.at(1), "compare")) {
+            Dec_DecDecCtx(&Decimal::compare, token, false, extended);
+            Int_DecDec(&Decimal::cmp, token, SKIP_NAN, false, extended);
+        }
+        else if (eqtoken(token.at(1), "comparesig")) {
+            Dec_DecDecCtx(&Decimal::compare_signal, token, false, extended);
+        }
+
+        else if (eqtoken(token.at(1), "comparetotal")) {
+            Dec_DecDec(&Decimal::compare_total, token, false, extended);
+            Int_DecDec(&Decimal::cmp_total, token, SKIP_NONE, false, extended);
+        }
+        else if (eqtoken(token.at(1), "comparetotmag")) {
+            Dec_DecDec(&Decimal::compare_total_mag, token, false, extended);
+            Int_DecDec(&Decimal::cmp_total_mag, token, SKIP_NONE, false, extended);
+        }
+
+        /* Special cases for the comparison functions, equal operands */
+        else if (eqtoken(token.at(1), "compare_eq")) {
+            Dec_DecDecCtx(&Decimal::compare, token, true, extended);
+            Int_DecDec(&Decimal::cmp, token, SKIP_NAN, true, extended);
+        }
+        else if (eqtoken(token.at(1), "comparesig_eq")) {
+            Dec_DecDecCtx(&Decimal::compare_signal, token, true, extended);
+        }
+
+        else if (eqtoken(token.at(1), "comparetotal_eq")) {
+            Dec_DecDec(&Decimal::compare_total, token, true, extended);
+            Int_DecDec(&Decimal::cmp_total, token, SKIP_NAN, true, extended);
+        }
+        else if (eqtoken(token.at(1), "comparetotmag_eq")) {
+            Dec_DecDec(&Decimal::compare_total_mag, token, true, extended);
+            Int_DecDec(&Decimal::cmp_total_mag, token, SKIP_NAN, true, extended);
+        }
+
+        /* Special cases for the shift functions */
+        else if (eqtoken(token.at(1), "shiftleft")) {
+            Dec_DecInt64Ctx(&Decimal::shiftl, token, extended);
+        }
+        else if (eqtoken(token.at(1), "shiftright")) {
+            Dec_DecInt64Ctx(&Decimal::shiftr, token, extended);
+        }
+
+        /* Special case for Decimal::ln10() */
+        else if (eqtoken(token.at(1), "ln10")) {
+            ln10(token, extended);
+        }
+
+        /* Special cases for the get_int functions */
+        else if (eqtoken(token.at(1), "get_u64") || eqtoken(token.at(1), "get_uint64")) {
+            u64_DecCtx(token, extended);
+        }
+        else if (eqtoken(token.at(1), "get_u32") || eqtoken(token.at(1), "get_uint32")) {
+            u32_DecCtx(token, extended);
+        }
+        else if (eqtoken(token.at(1), "get_i64") || eqtoken(token.at(1), "get_int64")) {
+            i64_DecCtx(token, extended);
+        }
+        else if (eqtoken(token.at(1), "get_i32") || eqtoken(token.at(1), "get_int32")) {
+            i32_DecCtx(token, extended);
+        }
+
+        else if (startswith(token.at(0), "bool")) {
+            /* skip: not implemented: bool tests in extra.decTest */
+            continue;
+        }
+
+        else if (eqtoken(token.at(1), "get_uint64_abs") ||
+                 eqtoken(token.at(1), "get_ssize64") ||
+                 eqtoken(token.at(1), "get_uint32_abs") ||
+                 eqtoken(token.at(1), "get_ssize32")) {
+            /* skip: not implemented */
+        }
+
+        else if (eqtoken(token.at(1), "rescale")) {
+           /*
+            * skip: 'rescale' is obsolete in the standard and Decimal::rescale()
+            * is not equivalent to the obsolete version.
+            */
+        }
+        else if (eqtoken(token.at(1), "baseconv")) {
+            /* skip: not implemented */
+        }
+        else {
+            err_token(token, "unknown operation");
+        }
+    }
+}
+
+static int
+exit_status(const std::vector<std::string>& status)
+{
+    for (auto p : status) {
+        if (p != "PASS") {
+            return EXIT_FAILURE;
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+static void
+do_file(const std::string& filename, std::vector<std::string>& status, size_t i, bool threaded)
+{
+    try {
+        if (threaded) {
+            /* Thread local context is initialized on first access. */
+            if (context.prec() != 1) {
+                err_raise("automatic context initialization from template failed");
+            }
+        }
+
+        std::ifstream in{filename};
+        if (!in.is_open()) {
+            err_raise("could not open ", filename);
+        }
+
+        do_stream(in);
+
+        if (in.bad()) {
+            err_raise("iterating over lines failed in ", filename);
+        }
+    } catch (test::Failure& e) {
+        status[i] = e.what();
+    }
+}
+
+/* process a file list */
+static int
+do_files(const std::vector<std::string>& files)
+{
+    const size_t n = files.size();
+    std::vector<std::string> status(n, "PASS");
+
+    for (size_t i = 0; i < n; i++) {
+        std::cout << files[i] << " ... " << std::flush;
+        do_file(files[i], status, i, false);
+        std::cout << status[i] << "\n" << std::flush;
+    }
+
+    std::cout << "\n" << std::flush;
+
+    return exit_status(status);
+}
+
+/* process a file list, using std::thread */
+static int
+do_files_thread(const std::vector<std::string>& files)
+{
+    const size_t n = files.size();
+    std::vector<std::string> status(n, "PASS");
+    std::vector<std::thread> t(n);
+
+    for (size_t i = 0; i < n; i++) {
+        t[i] = std::thread(do_file, files[i], std::ref(status), i, true);
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        t[i].join();
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        std::cout << files[i] << " ... " << status[i] << "\n" << std::flush;
+    }
+
+    std::cout << "\n" << std::flush;
+
+    if (skip_bignum) {
+        std::cout << "NOTE: std::thread stack size < 512K: skipped "
+                  <<  bignum_counter << " bignum test case"
+                  << (bignum_counter == 1 ? "\n\n" : "s\n\n")
+                  << std::flush;
+    }
+
+    return exit_status(status);
+}
+
+#ifdef HAVE_PTHREAD_H
+/*
+ * The pthread section is for systems like AIX, which have a std::thread stack
+ * size that is too small for the bignum tests. std::thread does not allow to
+ * set the stack size.
+ */
+#define THREAD_STACK_SIZE 1048576
+
+struct thread_info {
+    size_t index;
+    pthread_t tid;
+    const std::string *filename;
+    std::vector<std::string> *status;
+};
+
+static bool
+thread_stack_too_small_for_bignum()
+{
+    pthread_attr_t tattr;
+    size_t size;
+    int ret;
+
+    ret = pthread_attr_init(&tattr);
+    if (ret != 0) {
+        err_raise("thread attribute initialization failed");
+    }
+
+    ret = pthread_attr_getstacksize(&tattr, &size);
+    pthread_attr_destroy(&tattr);
+
+    if (ret != 0) {
+        err_raise("getting thread stack size failed");
+    }
+
+    return size < 524288;
+}
+
+static void *
+do_file_pthread(void *arg)
+{
+    struct thread_info *tinfo = static_cast<struct thread_info *>(arg);
+
+    try {
+        if (context.prec() != 1) {
+            err_raise("automatic context initialization from template failed");
+        }
+
+        std::ifstream in{*tinfo->filename};
+        if (!in.is_open()) {
+            err_raise("could not open ", *tinfo->filename);
+        }
+
+        do_stream(in);
+
+        if (in.bad()) {
+            err_raise("iterating over lines failed in ", *tinfo->filename);
+        }
+    } catch (test::Failure& e) {
+        (*tinfo->status)[tinfo->index] = e.what();
+    }
+
+    return nullptr;
+}
+
+/* process a file list, using pthread */
+static int
+do_files_pthread(const std::vector<std::string>& files)
+{
+    const size_t n = files.size();
+    std::vector<std::string> status(n, "PASS");
+    std::vector<struct thread_info> tinfo(n);
+    pthread_attr_t tattr;
+    int ret;
+
+    ret = pthread_attr_init(&tattr);
+    if (ret != 0) {
+        err_raise("thread attribute initialization failed");
+    }
+
+    ret = pthread_attr_setstacksize(&tattr, THREAD_STACK_SIZE);
+    if (ret != 0) {
+        pthread_attr_destroy(&tattr);
+        err_raise("setting thread stack size failed");
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        tinfo[i].index = i;
+        tinfo[i].filename = &files[i];
+        tinfo[i].status = &status;
+
+        ret = pthread_create(&tinfo[i].tid, &tattr, &do_file_pthread, &tinfo[i]);
+        if (ret != 0) {
+            pthread_attr_destroy(&tattr);
+            err_raise("could not create thread");
+        }
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        ret = pthread_join(tinfo[i].tid, nullptr);
+        if (ret != 0) {
+            pthread_attr_destroy(&tattr);
+            err_raise("error in thread execution");
+        }
+    }
+
+    for (size_t i = 0; i < n; i++) {
+        std::cout << files[i] << " ... " << status[i] << "\n" << std::flush;
+    }
+
+    std::cout << "\n" << std::flush;
+
+    pthread_attr_destroy(&tattr);
+
+    return exit_status(status);
+}
+#endif  /* HAVE_PTHREAD_H */
+
+
+static const int32_t int32_cases[] = {
+  INT32_MIN, INT32_MIN+1, INT32_MIN+2,
+  INT32_MAX-2, INT32_MAX-1, INT32_MAX,
+  -10, -5, -1, 0, 5, 10,
+  -999999999, -99999999, -9999999, -999999, -99999, -9999, -999, -99, -9,
+  -1000500001, -100050001, -10050001, -1005001, -105001, -10501, -1501, -151,
+  -1000000001, -100000001, -10000001, -1000001, -100001, -10001, -1001, -101,
+  -1000000000, -100000000, -10000000, -1000000, -100000, -10000, -1000, -100,
+  999999999, 99999999, 9999999, 999999, 99999, 9999, 999, 99, 9,
+  1000500001, 100050001, 10050001, 1005001, 105001, 10501, 1501, 151,
+  1000000001, 100000001, 10000001, 1000001, 100001, 10001, 1001, 101,
+  1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100,
+  -(1<<30),
+  -(1<<29), -(1<<28), -(1<<27), -(1<<26), -(1<<25), -(1<<24), -(1<<23), -(1<<22), -(1<<21), -(1<<20),
+  -(1<<19), -(1<<18), -(1<<17), -(1<<16), -(1<<15), -(1<<14), -(1<<13), -(1<<12), -(1<<11), -(1<<10),
+  -(1<<9),  -(1<<8),  -(1<<7),  -(1<<6),  -(1<<5),  -(1<<4),  -(1<<3),  -(1<<2),  -(1<<1),  -(1<<0),
+   (1<<30),
+   (1<<29), (1<<28), (1<<27), (1<<26), (1<<25), (1<<24), (1<<23), (1<<22), (1<<21), (1<<20),
+   (1<<19), (1<<18), (1<<17), (1<<16), (1<<15), (1<<14), (1<<13), (1<<12), (1<<11), (1<<10),
+   (1<<9),  (1<<8),  (1<<7),  (1<<6),  (1<<5),  (1<<4),  (1<<3),  (1<<2),  (1<<1),  (1<<0)
+};
+
+static const int64_t int64_cases[] = {
+  INT64_MIN, INT64_MIN+1, INT64_MIN+2, -10, -5, -1, 0, 5, 10, INT64_MAX-2, INT64_MAX-1, INT64_MAX,
+  -999999999999999999LL, -99999999999999999LL, -9999999999999999LL, -999999999999999LL, -99999999999999LL, -9999999999999LL,
+  -999999999999LL, -99999999999LL, -9999999999LL, -999999999LL, -99999999LL, -9999999LL, -999999LL, -99999LL, -9999LL, -999LL, -99LL, -9LL,
+  -1000000000000000000LL, -100000000000000000LL, -10000000000000000LL, -1000000000000000LL, -100000000000000LL, -10000000000000LL,
+  -1000000000000LL, -100000000000LL, -10000000000LL, -1000000000LL, -100000000LL, -10000000LL, -1000000LL, -100000LL, -10000LL, -1000LL, -100LL, -10LL,
+  -1000000005000000000LL, -100000005000000000LL, -10000005000000000LL, -1000005000000000LL, -100000005000000LL, -10000005000000LL,
+  -1000005000000LL, -100005000000LL, -10000005000LL, -1000005000LL, -100005000LL, -10005000LL, -1005000LL, -100050LL, -10050LL, -1050LL, -150LL, -15LL,
+  -1000000005000000001LL, -100000005000000001LL, -10000005000000001LL, -1000005000000001LL, -100000005000001LL, -10000005000001LL,
+  -1000005000001LL, -100005000001LL, -10000005001LL, -1000005001LL, -100005001LL, -10005001LL, -1005001LL, -100051LL, -10051LL, -1051LL, -151LL, -15LL,
+  999999999999999999LL, 99999999999999999LL, 9999999999999999LL, 999999999999999LL, 99999999999999LL, 9999999999999LL,
+  999999999999LL, 99999999999LL, 9999999999LL, 999999999LL, 99999999LL, 9999999LL, 999999LL, 99999LL, 9999LL, 999LL, 99LL, 9LL,
+  1000000000000000000LL, 100000000000000000LL, 10000000000000000LL, 1000000000000000LL, 100000000000000LL, 10000000000000LL,
+  1000000000000LL, 100000000000LL, 10000000000LL, 1000000000LL, 100000000LL, 10000000LL, 1000000LL, 100000LL, 10000LL, 1000LL, 100LL, 10LL,
+  1000000005000000000LL, 100000005000000000LL, 10000005000000000LL, 1000005000000000LL, 100000005000000LL, 10000005000000LL,
+  1000005000000LL, 100005000000LL, 10000005000LL, 1000005000LL, 100005000LL, 10005000LL, 1005000LL, 100050LL, 10050LL, 1050LL, 150LL, 15LL,
+  1000000005000000001LL, 100000005000000001LL, 10000005000000001LL, 1000005000000001LL, 100000005000001LL, 10000005000001LL,
+  1000005000001LL, 100005000001LL, 10000005001LL, 1000005001LL, 100005001LL, 10005001LL, 1005001LL, 100051LL, 10051LL, 1051LL, 151LL, 15LL,
+  -(1LL<<62), -(1LL<<61), -(1LL<<60),
+  -(1LL<<59), -(1LL<<58), -(1LL<<57), -(1LL<<56), -(1LL<<55), -(1LL<<54), -(1LL<<53), -(1LL<<52), -(1LL<<51), -(1LL<<50),
+  -(1LL<<39), -(1LL<<38), -(1LL<<37), -(1LL<<36), -(1LL<<35), -(1LL<<34), -(1LL<<33), -(1LL<<32), -(1LL<<31), -(1LL<<30),
+  -(1LL<<29), -(1LL<<28), -(1LL<<27), -(1LL<<26), -(1LL<<25), -(1LL<<24), -(1LL<<23), -(1LL<<22), -(1LL<<21), -(1LL<<20),
+  -(1LL<<19), -(1LL<<18), -(1LL<<17), -(1LL<<16), -(1LL<<15), -(1LL<<14), -(1LL<<13), -(1LL<<12), -(1LL<<11), -(1LL<<10),
+  -(1LL<<9),  -(1LL<<8),  -(1LL<<7),  -(1LL<<6),  -(1LL<<5),  -(1LL<<4),  -(1LL<<3),  -(1LL<<2),  -(1LL<<1),  -(1LL<<0),
+  -(1LL<<62), -(1LL<<61), -(1LL<<60),
+  (1LL<<59), (1LL<<58), (1LL<<57), (1LL<<56), (1LL<<55), (1LL<<54), (1LL<<53), (1LL<<52), (1LL<<51), (1LL<<50),
+  (1LL<<39), (1LL<<38), (1LL<<37), (1LL<<36), (1LL<<35), (1LL<<34), (1LL<<33), (1LL<<32), (1LL<<31), (1LL<<30),
+  (1LL<<29), (1LL<<28), (1LL<<27), (1LL<<26), (1LL<<25), (1LL<<24), (1LL<<23), (1LL<<22), (1LL<<21), (1LL<<20),
+  (1LL<<19), (1LL<<18), (1LL<<17), (1LL<<16), (1LL<<15), (1LL<<14), (1LL<<13), (1LL<<12), (1LL<<11), (1LL<<10),
+  (1LL<<9),  (1LL<<8),  (1LL<<7),  (1LL<<6),  (1LL<<5),  (1LL<<4),  (1LL<<3),  (1LL<<2),  (1LL<<1),  (1LL<<0),
+};
+
+static const char *init_cases[] = {
+  "sNaN", "sNaN19",
+  "sNaN1982612612300000002000000000050000000000000000101111111111111112111111111111111111111111111111111111111111111111"
+  "111111111111111111111111111111111111111111111111111111111111111",
+  "-sNaN", "-sNaN19",
+  "-sNaN198261261230000000200000000005000000000000000010111111111111111211111111111111111111111111111111111111111111111"
+  "1111111111111111111111111111111111111111111111111111111111111111",
+  "NaN", "NaN19",
+  "NaN19826126123000000020000000000500000000000000001011111111111111121111111111111111111111111111111111111111111111111"
+  "11111111111111111111111111111111111111111111111111111111111111",
+  "-NaN", "-NaN19",
+  "-NaN1982612612300000002000000000050000000000000000101111111111111112111111111111111111111111111111111111111111111111"
+  "111111111111111111111111111111111111111111111111111111111111111",
+  "inf", "-inf",
+  "-1", "-0", "0", "1",
+  "1e10", "-1e10",
+  "1.21019218731291112376416152e10",
+  "-1.21019218731291112376416152e10",
+  "0.0000000000000000000000000000000000000000000000000001e-999999",
+  "-0.0000000000000000000000000000000000000000000000000001e-999999"
+};
+
+static void
+test_set_i32(void)
+{
+    const Context savecontext = context;
+    context.status(0);
+    for (const char *s : init_cases) {
+        for (const int32_t& x : int32_cases) {
+            Decimal v{s};
+
+            v = x;
+            assertEqual(context.status(), 0U);
+            assertEqualStr(v, std::to_string(x));
+        }
+    }
+    context = savecontext;
+}
+
+static void
+test_set_i64(void)
+{
+    const Context savecontext = context;
+    context.status(0);
+    for (const char *s : init_cases) {
+        for (const int64_t& x : int64_cases) {
+            Decimal v{s};
+
+            v = x;
+            assertEqual(context.status(), 0U);
+            assertEqualStr(v, std::to_string(x));
+        }
+    }
+    context = savecontext;
+}
+
+
+/* process a single test file */
+static void
+usage(void)
+{
+    std::cerr << "runtest: usage: runtest testfile [--custom] [--alloc] [--thread|--pthread]" << std::endl;
+    exit(EXIT_FAILURE);
+}
+
+static std::vector<std::string>
+collect_files(const std::string& topfile)
+{
+    std::vector<std::string> files;
+    std::string line;
+
+    std::ifstream in{topfile};
+    if (!in.is_open()) {
+        err_exit("could not open file");
+    }
+
+    while (std::getline(in, line)) {
+        std::vector<std::string> token = split(line);
+        if (token.size() == 0) {
+            continue;
+        }
+
+        if (startswith(token.at(0), "Dectest")) {
+            files.push_back(token.at(1));
+            continue;
+        }
+        else {
+            err_exit("parse error");
+        }
+    }
+
+    if (in.bad()) {
+        err_exit("iterating over lines failed");
+    }
+
+    return files;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    std::vector<std::string> args(argv + (argc!=0), argv + argc);
+    std::string filename = "";
+    bool custom_alloc = false;
+    bool check_alloc = false;
+    bool thread = false;
+    bool pthread = false;
+
+    for (auto arg : args) {
+        if (filename == "" && (arg == "-" || !startswith(arg, "--"))) {
+            filename = arg;
+        }
+        else if (!custom_alloc && arg == "--custom") {
+            custom_alloc = true;
+        }
+        else if (!check_alloc && arg == "--alloc") {
+            check_alloc = true;
+        }
+        else if (!thread && arg == "--thread") {
+            thread = true;
+        }
+        else if (!pthread && arg == "--pthread") {
+            pthread = true;
+        }
+        else {
+            usage();
+        }
+    }
+    if (filename == "") {
+        usage();
+    }
+
+    /* std::thread needs 300K stack size for the bignum tests. */
+    #ifdef HAVE_PTHREAD_H
+    if (thread && thread_stack_too_small_for_bignum()) {
+        skip_bignum = true;
+    }
+    #endif
+
+    /* Initialize custom allocation functions */
+    test::init_alloc(custom_alloc, check_alloc);
+
+    /* Initialize the context template */
+    context_template = Context(1, 1, -1);
+
+    /* Initialize main thread context */
+    context = context_template;
+
+    /* Initial tests */
+    test_set_i32();
+    test_set_i64();
+    test_copy_constructor();
+
+
+    /* Read test cases from stdin */
+    if (filename == "-") {
+        try {
+            do_stream(std::cin, /*extended=*/false);
+        }
+        catch (test::Failure& e) {
+            std::cerr << "<stdin> ... " << e.what() << "\n" << std::flush;
+            return EXIT_FAILURE;
+        }
+        std::cout << "<stdin> ... PASS\n\n" << std::flush;
+        return EXIT_SUCCESS;
+    }
+
+    /* Collect test files */
+    std::vector<std::string> files;
+    if (endswith(filename, ".decTest")) {
+        files.push_back(filename);
+    }
+    else if (endswith(filename, ".topTest")) {
+        std::ifstream in{filename};
+        if (!in.is_open()) {
+            err_exit("could not open file");
+        }
+
+        files = collect_files(filename);
+
+        if (in.bad()) {
+            err_exit("iterating over lines failed");
+        }
+    }
+    else {
+        err_exit("unrecognized file extension: expect .decTest or .topTest");
+    }
+
+    /* Run all tests */
+    if (thread) {
+        return do_files_thread(files);
+    }
+    else if (pthread) {
+    #ifdef HAVE_PTHREAD_H
+        return do_files_pthread(files);
+    #else
+        err_exit("pthread not found on this system: use --thread");
+    #endif
+    }
+    else {
+        return do_files(files);
+    }
+}
diff --git a/tests++/test.cc b/tests++/test.cc
new file mode 100644 (file)
index 0000000..3dc549f
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <cstdlib>
+#include <cstdint>
+#include <cinttypes>
+
+#include "mpdecimal.h"
+
+#include "decimal.hh"
+#include "test.hh"
+
+
+/******************************************************************************/
+/*                                Exceptions                                  */
+/******************************************************************************/
+
+namespace test {
+const char *Failure::what() const noexcept { return m.what(); }
+}  // namespace test
+
+
+/******************************************************************************/
+/*                                Functions                                   */
+/******************************************************************************/
+
+namespace test {
+void
+assert_true(const char *file, const int64_t line, const bool p)
+{
+    if (!(p)) {
+        raise(file, line, "assertion failed (expected true, got false)");
+    }
+}
+
+void
+assert_false(const char *file, const int64_t line, const bool p)
+{
+    if (p) {
+        raise(file, line, "assertion failed (expected false, got true)");
+    }
+}
+}  // namespace test
+
+
+/******************************************************************************/
+/*               Primary allocation functions (normal or offset)              */
+/******************************************************************************/
+
+static const size_t OFFSET = 16;
+
+#ifdef MPD_CONFIG_64
+static const size_t alloc_limit = 0x4000000000000ULL;
+#else
+static thread_local size_t alloc_limit = SIZE_MAX;
+#endif
+
+/* malloc with upper limits */
+static void *
+malloc_ceil(size_t size)
+{
+    if (size > alloc_limit) {
+        return nullptr;
+    }
+
+    return malloc(size);
+}
+
+static void *
+calloc_ceil(size_t nmemb, size_t size)
+{
+    if (nmemb > alloc_limit / size) {
+        return nullptr;
+    }
+
+    return calloc(nmemb, size);
+}
+
+static void *
+realloc_ceil(void *ptr, size_t size)
+{
+    if (size > alloc_limit) {
+        return nullptr;
+    }
+
+    return realloc(ptr, size);
+}
+
+static void
+free_ceil(void *ptr)
+{
+    free(ptr);
+}
+
+/* custom malloc with an offset and upper limits */
+static void *
+malloc_offset(size_t size)
+{
+    if (size == 0 || size > SIZE_MAX - OFFSET) {
+        return nullptr;
+    }
+
+    char *ptr = (char *)malloc_ceil(OFFSET + size);
+
+    return ptr ? ptr + OFFSET : nullptr;
+}
+
+static void *
+calloc_offset(size_t nmemb, size_t size)
+{
+    if (nmemb == 0 || size == 0 || size > SIZE_MAX - OFFSET) {
+        return nullptr;
+    }
+
+    char *ptr = (char *)calloc_ceil(nmemb, OFFSET + size);
+
+    return ptr ? ptr + OFFSET : nullptr;
+}
+
+static void *
+realloc_offset(void *ptr, size_t size)
+{
+    if (size == 0 || size > SIZE_MAX - OFFSET) {
+        return nullptr;
+    }
+
+    char *c = (char *)ptr - OFFSET;
+    char *p = (char *)realloc_ceil(c, OFFSET + size);
+
+    return p ? p + OFFSET : nullptr;
+}
+
+static void
+free_offset(void *ptr)
+{
+    free((char *)ptr - OFFSET);
+}
+
+/* active set of primary allocation functions */
+static void *(* test_mallocfunc)(size_t size) = malloc_ceil;
+static void *(* test_callocfunc)(size_t nmemb, size_t size) = calloc_ceil;
+static void *(* test_reallocfunc)(void *ptr, size_t size) = realloc_ceil;
+static void (* test_freefunc)(void *ptr) = free_ceil;
+
+
+/******************************************************************************/
+/*            Secondary allocation functions (count or failure mode)          */
+/******************************************************************************/
+
+static bool enable_check_alloc = false;
+static thread_local uint64_t alloc_fail = UINT64_MAX;
+static thread_local uint64_t alloc_idx = 0;
+
+static void *
+malloc_fail(size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return nullptr;
+    }
+
+    return test_mallocfunc(size);
+}
+
+static void *
+calloc_fail(size_t nmemb, size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return nullptr;
+    }
+
+    return test_callocfunc(nmemb, size);
+}
+
+static void *
+realloc_fail(void *ptr, size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return nullptr;
+    }
+
+    return test_reallocfunc(ptr, size);
+}
+
+namespace test {
+
+void
+init_alloc(bool custom_alloc, bool check_alloc)
+{
+    static bool initialized = false;
+
+    if (initialized) {
+        fputs("mpd_init_alloc: error: cannot initialize twice\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    initialized = true;
+
+    /* initialization for the main thread */
+#ifdef MPD_CONFIG_32
+    alloc_limit = SIZE_MAX;
+#endif
+    alloc_fail = UINT64_MAX;
+    alloc_idx = 0;
+
+    enable_check_alloc = check_alloc;
+
+    if (custom_alloc) {
+        test_mallocfunc = malloc_offset;
+        test_callocfunc = calloc_offset;
+        test_reallocfunc = realloc_offset;
+        test_freefunc = free_offset;
+    }
+
+    mpd_mallocfunc = malloc_fail;
+    mpd_callocfunc = calloc_fail;
+    mpd_reallocfunc = realloc_fail;
+    mpd_free = test_freefunc;
+}
+
+#ifdef MPD_CONFIG_32
+void
+set_alloc_limit(size_t size)
+{
+    alloc_limit = size;
+}
+#endif
+
+void
+set_alloc(decimal::Context &ctx)
+{
+    ctx.traps(MPD_Malloc_error);
+    alloc_idx = 0;
+    alloc_fail = UINT64_MAX;
+}
+
+void
+set_alloc_fail(decimal::Context &ctx, uint64_t n)
+{
+    if (enable_check_alloc) {
+        ctx.traps(MPD_Malloc_error);
+        alloc_idx = 0;
+        alloc_fail = n;
+    }
+}
+
+}  /* namespace test */
diff --git a/tests++/test.hh b/tests++/test.hh
new file mode 100644 (file)
index 0000000..203d54f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef TESTS_HH_
+#define TESTS_HH_
+
+
+#include <exception>
+#include <string>
+#include <sstream>
+#include <vector>
+
+
+namespace test {
+
+
+/******************************************************************************/
+/*                                    Util                                    */
+/******************************************************************************/
+
+template<typename T>
+static inline const std::string
+str(const T& t)
+{
+    std::stringstream ss;
+    ss << t;
+    return ss.str();
+}
+
+static inline const std::string
+stringize()
+{
+    return std::string();
+}
+
+template<typename T, typename... Args>
+static inline const std::string
+stringize(const T& t, Args... args)
+{
+    return str(t) + stringize(args...);
+}
+
+
+/******************************************************************************/
+/*                                 Exceptions                                 */
+/******************************************************************************/
+
+class Failure: public std::exception {
+ private:
+    std::runtime_error m;
+ public:
+    template<typename... Args>
+    explicit Failure(Args... args) : m(stringize(args...)) {}
+    virtual const char* what() const noexcept;
+};
+
+template<typename... Args>
+static void
+raise(const char *file, const int64_t line, Args... args)
+{
+    throw Failure("error: ", args..., "  [", file, ":", line, "]");
+}
+
+
+/******************************************************************************/
+/*                                Test support                                */
+/******************************************************************************/
+
+void assert_true(const char *file, const int64_t line, const bool p);
+void assert_false(const char *file, const int64_t line, const bool p);
+
+template<class T, class U>
+void
+assert_equal(const char *file, const int64_t line, const T& calc, const U& expected)
+{
+    if (calc != expected) {
+        raise(file, line, "values not equal: ", "expected: ", test::str(expected),
+                          " got: ", test::str(calc));
+    }
+}
+
+template<class T, class U>
+void
+assert_equal_str(const char *file, int64_t line, const T& calc, const U& expected)
+{
+    if (str(calc) != str(expected)) {
+        raise(file, line, "string representations not equal: expected: ", test::str(expected),
+                          " got: ", test::str(calc));
+    }
+}
+
+template<typename Exc, typename F>
+void
+assert_raises(const char *file, const int64_t line, const F& f)
+{
+    try {
+        f();
+        raise(file, line, "exception not raised");
+    }
+    catch (Exc& e) {
+        (void)e;
+        return;
+    }
+    catch (std::exception& e) {
+        raise(file, line, "unexpected exception: ", e.what());
+    }
+}
+
+#define assertTrue(p) test::assert_true(__FILE__, __LINE__, p)
+#define assertFalse(p) test::assert_false(__FILE__, __LINE__, p)
+#define assertEqual(calc, expected) test::assert_equal(__FILE__, __LINE__, calc, expected)
+#define assertEqualStr(calc, expected) test::assert_equal_str(__FILE__, __LINE__, calc, expected)
+#define assertRaises(ex, func) test::assert_raises<ex>(__FILE__, __LINE__, func)
+
+#define err_exit(msg) \
+  do {std::cerr << __FILE__ << ":" << __LINE__ << ": error: "; \
+      std::cerr << msg  << std::endl;                          \
+      std::exit(EXIT_FAILURE);                                 \
+  } while (0)
+
+#define err_raise(...) \
+  do { throw test::Failure( "error: ", __VA_ARGS__, "  [", __FILE__, ":", __LINE__, "]"); } while (0)
+
+#define err_token(token, ...) \
+  do { throw test::Failure(token.at(0), ": ", __VA_ARGS__, "  [", __FILE__, ":", __LINE__, "]"); } while (0)
+
+#define DECIMAL_ASSERT(p, token) \
+  do { if (!(p)) { err_token(token, "assertion failure"); } } while (0)
+
+
+/******************************************************************************/
+/*                      API for testing allocation failures                   */
+/******************************************************************************/
+
+void init_alloc(bool custom_alloc, bool check_alloc);
+
+#ifdef MPD_CONFIG_32
+void set_alloc_limit(size_t size);
+#endif
+
+void set_alloc_fail(decimal::Context &ctx, uint64_t n);
+void set_alloc(decimal::Context &ctx);
+
+}  // namespace test
+
+
+#endif  // TESTS_HH_
diff --git a/tests++/vctest.hh b/tests++/vctest.hh
new file mode 100644 (file)
index 0000000..af85b1c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef TESTSXX_VCTEST_H_
+#define TESTSXX_VCTEST_H_
+
+
+/* Visual C fixes */
+#ifdef _MSC_VER
+    /* workaround for broken headers */
+    #undef INT8_MIN
+    #define INT8_MIN ((int8_t)SCHAR_MIN)
+
+    #undef INT8_MAX
+    #define INT8_MAX ((int8_t)SCHAR_MAX)
+
+    /* missing functions */
+    #undef random
+    #define random rand
+    #undef srandom
+    #define srandom srand
+    #undef strncasecmp
+    #define strncasecmp _strnicmp
+    #undef strcasecmp
+    #define strcasecmp _stricmp
+#endif
+
+
+#endif /* TESTSXX_VCTEST_H_ */
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644 (file)
index 0000000..3a33070
--- /dev/null
@@ -0,0 +1,59 @@
+
+# ==============================================================================
+#                        Unix Makefile for libmpdec tests
+# ==============================================================================
+
+SRCDIR = ../libmpdec
+ENABLE_STATIC = @ENABLE_STATIC@
+ENABLE_SHARED = @ENABLE_SHARED@
+
+LIBSTATIC = @LIBSTATIC@
+LIBSHARED = @LIBSHARED@
+LINK_STATIC = @LINK_STATIC@
+LINK_DYNAMIC = @LINK_DYNAMIC@
+
+CC = @CC@
+AR = @AR@
+MPD_GNU99 = @MPD_GNU99@
+
+FILTER_FOR_STATIC = @FILTER_FOR_STATIC@
+
+CONFIGURE_CFLAGS = @CONFIGURE_CFLAGS@
+MPD_CFLAGS_SHARED = $(strip $(filter-out $(CFLAGS),$(CONFIGURE_CFLAGS)) $(CFLAGS) $(MPD_GNU99))
+MPD_CFLAGS = $(strip $(filter-out $(FILTER_FOR_STATIC),$(MPD_CFLAGS_SHARED)))
+
+LINK_LIBSTATIC = $(strip $(LINK_STATIC) $(SRCDIR)/$(LIBSTATIC) $(LINK_DYNAMIC))
+
+
+MPD_TARGETS =
+ifeq ($(ENABLE_STATIC), yes)
+MPD_TARGETS += runtest
+endif
+ifeq ($(ENABLE_SHARED), yes)
+MPD_TARGETS += runtest_shared
+endif
+
+default: $(MPD_TARGETS)
+
+
+# Short test.
+runtest:\
+Makefile runtest.c test.c $(SRCDIR)/$(LIBSTATIC) $(SRCDIR)/mpdecimal.h \
+test.h vctest.h
+       $(CC) -I$(SRCDIR) $(MPD_CFLAGS) -o runtest runtest.c test.c $(LINK_LIBSTATIC) -lm
+
+runtest_shared:\
+Makefile runtest.c test.c $(SRCDIR)/$(LIBSHARED) $(SRCDIR)/mpdecimal.h \
+test.h vctest.h
+       $(CC) -I$(SRCDIR) $(MPD_CFLAGS_SHARED) -o runtest_shared runtest.c test.c -L$(SRCDIR) -lmpdec -lm
+
+
+FORCE:
+
+clean: FORCE
+       rm -f *.o *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock
+       rm -f runtest runtest_shared
+
+distclean: FORCE
+       $(MAKE) clean
+       rm -rf Makefile dectest.zip testdata
diff --git a/tests/Makefile.vc b/tests/Makefile.vc
new file mode 100644 (file)
index 0000000..84fa982
--- /dev/null
@@ -0,0 +1,59 @@
+
+SRCDIR = ..\libmpdec
+LIBSTATIC = libmpdec-4.0.1.lib
+LIBSHARED = libmpdec-4.0.1.dll
+LIBIMPORT = libmpdec-4.0.1.dll.lib
+
+!if "$(DEBUG)" == "1"
+OPT = /MTd /Od /Zi /EHsc
+OPT_SHARED = /MDd /Od /Zi /EHsc
+!else
+OPT = /MT /O2 /GS /EHsc /DNDEBUG
+OPT_SHARED = /MD /O2 /GS /EHsc /DNDEBUG
+!endif
+
+!if "$(CC)" == "clang-cl"
+WARN = /W4 /wd4200 /wd4204 /wd4221 -Wno-undefined-inline /D_CRT_SECURE_NO_WARNINGS
+!else
+WARN = /W4 /wd4200 /wd4204 /wd4221 /D_CRT_SECURE_NO_WARNINGS
+!endif
+
+MPD_CFLAGS = $(WARN) /nologo $(OPT)
+MPD_CFLAGS_SHARED = $(WARN) /nologo $(OPT_SHARED)
+
+
+default: runtest runtest_shared copy_dll
+
+
+runtest:\
+Makefile runtest.c test.c $(SRCDIR)\io.h $(SRCDIR)\mpdecimal.h $(SRCDIR)\mpalloc.h test.h vctest.h \
+$(SRCDIR)\$(LIBSTATIC)
+       $(CC) -I$(SRCDIR) $(MPD_CFLAGS) /Fe:runtest runtest.c test.c $(SRCDIR)\$(LIBSTATIC)
+
+runtest_shared:\
+Makefile runtest.c test.c $(SRCDIR)/io.h $(SRCDIR)/mpdecimal.h $(SRCDIR)/mpalloc.h test.h vctest.h \
+$(SRCDIR)/$(LIBSHARED) $(SRCDIR)/$(LIBIMPORT)
+       $(CC) -I$(SRCDIR) $(MPD_CFLAGS_SHARED) /Fe:runtest_shared runtest.c test.c $(SRCDIR)\$(LIBIMPORT)
+
+FORCE:
+
+copy_dll:
+       copy /y $(SRCDIR)\$(LIBSHARED) .
+
+clean: FORCE
+       -@if exist *.obj del *.obj
+       -@if exist *.dll del *.dll
+       -@if exist *.exp del *.exp
+       -@if exist *.lib del *.lib
+       -@if exist *.ilk del *.ilk
+       -@if exist *.pdb del *.pdb
+       -@if exist *.pgc del *.pgc
+       -@if exist *.pgd del *.pgd
+       -@if exist *.manifest del *.manifest
+       -@if exist *.exe del *.exe
+
+distclean: FORCE
+       nmake clean
+       -@if exist dectest.zip del dectest.zip
+       -@if exist testdata rd /q /s testdata
+       -@if exist Makefile del Makefile
diff --git a/tests/README.txt b/tests/README.txt
new file mode 100644 (file)
index 0000000..5dc0633
--- /dev/null
@@ -0,0 +1,27 @@
+
+
+Download official tests and add the tests to the testdata directory:
+====================================================================
+
+# Unix: If wget is installed, just execute `make check` from
+# the top level directory.
+
+# Windows: See vcbuild directory.
+
+
+#
+# If gettests.sh or gettests.bat fails:
+#
+#   mkdir testdata && cp testdata_dist/* testdata
+#
+# Get http://speleotrove.com/decimal/dectest.zip and extract the archive
+# so that the directory structure is testdata/*.decTest.
+#
+# Change into the top level directory, build the library, change back
+# to the test directory, run:
+#
+#   make && ./runshort.sh
+#
+
+
+
diff --git a/tests/additional.decTest b/tests/additional.decTest
new file mode 100644 (file)
index 0000000..eedaeb9
--- /dev/null
@@ -0,0 +1,31 @@
+
+-- Additional Tests
+
+Dectest: ./testdata/baseconv.decTest
+
+Dectest: ./testdata/binop_eq.decTest
+
+Dectest: ./testdata/divmod.decTest
+Dectest: ./testdata/divmod_eq.decTest
+
+Dectest: ./testdata/fma_eq.decTest
+
+Dectest: ./testdata/format.decTest
+
+Dectest: ./testdata/invroot.decTest
+
+Dectest: ./testdata/largeint.decTest
+
+Dectest: ./testdata/powmod.decTest
+Dectest: ./testdata/powmod_eq.decTest
+
+Dectest: ./testdata/shiftlr.decTest
+
+Dectest: ./testdata/getint.decTest
+
+Dectest: ./testdata/cov.decTest
+Dectest: ./testdata/extra.decTest
+
+Dectest: ./testdata/maxprec.decTest
+
+
diff --git a/tests/gettests.bat b/tests/gettests.bat
new file mode 100755 (executable)
index 0000000..b7afb6c
--- /dev/null
@@ -0,0 +1,16 @@
+@ECHO OFF\r
+\r
+if not exist testdata mkdir testdata\r
+rem copy additional tests\r
+if not exist testdata\baseconv.decTest copy /y testdata_dist\* testdata\r
+\r
+if exist testdata\add.decTest goto OUT\r
+\r
+rem get official tests\r
+if exist dectest.zip goto UNZIP\r
+powershell -Command "wget http://speleotrove.com/decimal/dectest.zip -outfile dectest.zip"\r
+\r
+:UNZIP\r
+powershell -Command "Expand-Archive dectest.zip -DestinationPath testdata"\r
+\r
+:OUT\r
diff --git a/tests/gettests.sh b/tests/gettests.sh
new file mode 100755 (executable)
index 0000000..53ccab3
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if [ ! -d testdata ]; then
+    mkdir testdata
+fi
+
+if [ x"$1" != x"--local" -a ! -f testdata/add.decTest ]; then
+    # The official tests are freely downloadable, but copyrighted.
+    WGET=`which wget`
+    if [ ! -f "$WGET" ]; then
+        printf "\n*** gettests.sh: error: could not find wget. In order to run the tests, \n"
+        printf "    download http://speleotrove.com/decimal/dectest.zip and extract the \n"
+        printf "    archive into tests/testdata.\n\n"
+        exit 1
+    fi
+    printf "\nGetting official tests ... \n\n"
+    $WGET -nv http://speleotrove.com/decimal/dectest.zip 2>&1 &&
+    cd testdata &&
+    unzip -qq ../dectest.zip &&
+    cd ../
+fi
+
+if [ ! -f testdata/baseconv.decTest ]; then
+    cp testdata_dist/* testdata
+fi
diff --git a/tests/official.decTest b/tests/official.decTest
new file mode 100644 (file)
index 0000000..c5fce0a
--- /dev/null
@@ -0,0 +1,163 @@
+
+-- All tests from the dectest directory. Tests that are skipped are
+-- commented out.
+
+Dectest: ./testdata/abs.decTest
+Dectest: ./testdata/add.decTest
+Dectest: ./testdata/and.decTest
+Dectest: ./testdata/base.decTest
+Dectest: ./testdata/clamp.decTest
+Dectest: ./testdata/class.decTest
+Dectest: ./testdata/compare.decTest
+Dectest: ./testdata/comparetotal.decTest
+Dectest: ./testdata/comparetotmag.decTest
+Dectest: ./testdata/copyabs.decTest
+Dectest: ./testdata/copy.decTest
+Dectest: ./testdata/copynegate.decTest
+Dectest: ./testdata/copysign.decTest
+Dectest: ./testdata/ddAbs.decTest
+Dectest: ./testdata/ddAdd.decTest
+Dectest: ./testdata/ddAnd.decTest
+Dectest: ./testdata/ddBase.decTest
+-- Dectest: ./testdata/ddCanonical.decTest
+Dectest: ./testdata/ddClass.decTest
+Dectest: ./testdata/ddCompare.decTest
+Dectest: ./testdata/ddCompareSig.decTest
+Dectest: ./testdata/ddCompareTotal.decTest
+Dectest: ./testdata/ddCompareTotalMag.decTest
+Dectest: ./testdata/ddCopyAbs.decTest
+Dectest: ./testdata/ddCopy.decTest
+Dectest: ./testdata/ddCopyNegate.decTest
+Dectest: ./testdata/ddCopySign.decTest
+Dectest: ./testdata/ddDivide.decTest
+Dectest: ./testdata/ddDivideInt.decTest
+-- Dectest: ./testdata/ddEncode.decTest
+Dectest: ./testdata/ddFMA.decTest
+Dectest: ./testdata/ddInvert.decTest
+Dectest: ./testdata/ddLogB.decTest
+Dectest: ./testdata/ddMax.decTest
+Dectest: ./testdata/ddMaxMag.decTest
+Dectest: ./testdata/ddMin.decTest
+Dectest: ./testdata/ddMinMag.decTest
+Dectest: ./testdata/ddMinus.decTest
+Dectest: ./testdata/ddMultiply.decTest
+Dectest: ./testdata/ddNextMinus.decTest
+Dectest: ./testdata/ddNextPlus.decTest
+Dectest: ./testdata/ddNextToward.decTest
+Dectest: ./testdata/ddOr.decTest
+Dectest: ./testdata/ddPlus.decTest
+Dectest: ./testdata/ddQuantize.decTest
+Dectest: ./testdata/ddReduce.decTest
+Dectest: ./testdata/ddRemainder.decTest
+Dectest: ./testdata/ddRemainderNear.decTest
+Dectest: ./testdata/ddRotate.decTest
+Dectest: ./testdata/ddSameQuantum.decTest
+Dectest: ./testdata/ddScaleB.decTest
+Dectest: ./testdata/ddShift.decTest
+Dectest: ./testdata/ddSubtract.decTest
+Dectest: ./testdata/ddToIntegral.decTest
+Dectest: ./testdata/ddXor.decTest
+-- Dectest: ./testdata/decDouble.decTest
+-- Dectest: ./testdata/decQuad.decTest
+-- Dectest: ./testdata/decSingle.decTest
+Dectest: ./testdata/divide.decTest
+Dectest: ./testdata/divideint.decTest
+Dectest: ./testdata/dqAbs.decTest
+Dectest: ./testdata/dqAdd.decTest
+Dectest: ./testdata/dqAnd.decTest
+Dectest: ./testdata/dqBase.decTest
+-- Dectest: ./testdata/dqCanonical.decTest
+Dectest: ./testdata/dqClass.decTest
+Dectest: ./testdata/dqCompare.decTest
+Dectest: ./testdata/dqCompareSig.decTest
+Dectest: ./testdata/dqCompareTotal.decTest
+Dectest: ./testdata/dqCompareTotalMag.decTest
+Dectest: ./testdata/dqCopyAbs.decTest
+Dectest: ./testdata/dqCopy.decTest
+Dectest: ./testdata/dqCopyNegate.decTest
+Dectest: ./testdata/dqCopySign.decTest
+Dectest: ./testdata/dqDivide.decTest
+Dectest: ./testdata/dqDivideInt.decTest
+-- Dectest: ./testdata/dqEncode.decTest
+Dectest: ./testdata/dqFMA.decTest
+Dectest: ./testdata/dqInvert.decTest
+Dectest: ./testdata/dqLogB.decTest
+Dectest: ./testdata/dqMax.decTest
+Dectest: ./testdata/dqMaxMag.decTest
+Dectest: ./testdata/dqMin.decTest
+Dectest: ./testdata/dqMinMag.decTest
+Dectest: ./testdata/dqMinus.decTest
+Dectest: ./testdata/dqMultiply.decTest
+Dectest: ./testdata/dqNextMinus.decTest
+Dectest: ./testdata/dqNextPlus.decTest
+Dectest: ./testdata/dqNextToward.decTest
+Dectest: ./testdata/dqOr.decTest
+Dectest: ./testdata/dqPlus.decTest
+Dectest: ./testdata/dqQuantize.decTest
+Dectest: ./testdata/dqReduce.decTest
+Dectest: ./testdata/dqRemainder.decTest
+Dectest: ./testdata/dqRemainderNear.decTest
+Dectest: ./testdata/dqRotate.decTest
+Dectest: ./testdata/dqSameQuantum.decTest
+Dectest: ./testdata/dqScaleB.decTest
+Dectest: ./testdata/dqShift.decTest
+Dectest: ./testdata/dqSubtract.decTest
+Dectest: ./testdata/dqToIntegral.decTest
+Dectest: ./testdata/dqXor.decTest
+Dectest: ./testdata/dsBase.decTest
+-- Dectest: ./testdata/dsEncode.decTest
+Dectest: ./testdata/exp.decTest
+Dectest: ./testdata/fma.decTest
+Dectest: ./testdata/inexact.decTest
+Dectest: ./testdata/invert.decTest
+Dectest: ./testdata/ln.decTest
+Dectest: ./testdata/log10.decTest
+Dectest: ./testdata/logb.decTest
+Dectest: ./testdata/max.decTest
+Dectest: ./testdata/maxmag.decTest
+Dectest: ./testdata/min.decTest
+Dectest: ./testdata/minmag.decTest
+Dectest: ./testdata/minus.decTest
+Dectest: ./testdata/multiply.decTest
+Dectest: ./testdata/nextminus.decTest
+Dectest: ./testdata/nextplus.decTest
+Dectest: ./testdata/nexttoward.decTest
+Dectest: ./testdata/or.decTest
+Dectest: ./testdata/plus.decTest
+Dectest: ./testdata/power.decTest
+Dectest: ./testdata/powersqrt.decTest
+Dectest: ./testdata/quantize.decTest
+Dectest: ./testdata/randombound32.decTest
+Dectest: ./testdata/randoms.decTest
+Dectest: ./testdata/reduce.decTest
+Dectest: ./testdata/remainder.decTest
+Dectest: ./testdata/remaindernear.decTest
+Dectest: ./testdata/rescale.decTest
+Dectest: ./testdata/rotate.decTest
+Dectest: ./testdata/rounding.decTest
+Dectest: ./testdata/samequantum.decTest
+Dectest: ./testdata/scaleb.decTest
+Dectest: ./testdata/shift.decTest
+Dectest: ./testdata/squareroot.decTest
+Dectest: ./testdata/subtract.decTest
+-- Dectest: ./testdata/testall.decTest
+Dectest: ./testdata/tointegral.decTest
+Dectest: ./testdata/tointegralx.decTest
+-- Dectest: ./testdata/trim.decTest
+Dectest: ./testdata/xor.decTest
+
+
+-- Summary of functions that are not implemented or do not need testing:
+
+-- Dectest: ./testdata/ddCanonical.decTest  ==>  same as copy()
+-- Dectest: ./testdata/ddEncode.decTest
+-- Dectest: ./testdata/decDouble.decTest
+-- Dectest: ./testdata/decQuad.decTest
+-- Dectest: ./testdata/decSingle.decTest
+-- Dectest: ./testdata/dqCanonical.decTest  ==> same as copy()
+-- Dectest: ./testdata/dqEncode.decTest
+-- Dectest: ./testdata/dsEncode.decTest
+-- Dectest: ./testdata/testall.decTest
+-- Dectest: ./testdata/trim.decTest
+
+
diff --git a/tests/runshort.sh b/tests/runshort.sh
new file mode 100755 (executable)
index 0000000..a45e638
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# malloc() on OS X does not conform to the C standard.
+SYSTEM=`uname -s`
+case $SYSTEM in
+    darwin*|Darwin*)
+        export MallocLogFile=/dev/null
+        export MallocDebugReport=crash
+        ;;
+    *)
+        ;;
+esac
+
+# Download the official test cases (text files).
+./gettests.sh "$1" || exit 1
+
+if [ ! -f ./runtest -a ! -f ./runtest_shared ]; then
+    printf "\nERROR: ./runtest and ./runtest_shared not found\n\n\n"; exit 1;
+fi
+
+if [ -f ./runtest ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                         libmpdec: static library\n"
+    printf "# ========================================================================\n\n"
+
+    if [ x"$1" != x"--local" ]; then
+        printf "Running official tests ...\n\n"
+        ./runtest official.decTest || { printf "\nFAIL\n\n\n"; exit 1; }
+    fi
+
+    printf "Running additional tests ...\n\n"
+    ./runtest additional.decTest || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
+
+if [ -f ./runtest_shared ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                         libmpdec: shared library\n"
+    printf "# ========================================================================\n\n"
+
+    PORTABLE_PWD=`pwd`
+    LD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH"
+    DYLD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$DYLD_LIBRARY_PATH"
+    LD_64_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_64_LIBRARY_PATH"
+    LD_32_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_32_LIBRARY_PATH"
+    LD_LIBRARY_PATH_64="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH_64"
+    LD_LIBRARY_PATH_32="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH_32"
+    PATH="$LD_LIBRARY_PATH:$PATH"
+    export LD_LIBRARY_PATH
+    export DYLD_LIBRARY_PATH
+    export LD_64_LIBRARY_PATH
+    export LD_32_LIBRARY_PATH
+    export LD_LIBRARY_PATH_64
+    export LD_LIBRARY_PATH_32
+
+    if [ x"$1" != x"--local" ]; then
+        printf "Running official tests ...\n\n"
+        ./runtest_shared official.decTest || { printf "\nFAIL\n\n\n"; exit 1; }
+    fi
+
+    printf "Running additional tests ...\n\n"
+    ./runtest_shared additional.decTest || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
diff --git a/tests/runshort_alloc.sh b/tests/runshort_alloc.sh
new file mode 100755 (executable)
index 0000000..c307402
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+# malloc() on OS X does not conform to the C standard.
+SYSTEM=`uname -s`
+case $SYSTEM in
+    darwin*|Darwin*)
+        export MallocLogFile=/dev/null
+        export MallocDebugReport=crash
+        ;;
+    *)
+        ;;
+esac
+
+# Download the official test cases (text files).
+./gettests.sh || exit 1
+
+if [ ! -f ./runtest -a ! -f ./runtest_shared ]; then
+    printf "\nERROR: ./runtest and ./runtest_shared not found\n\n\n"; exit 1;
+fi
+
+if [ -f ./runtest ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                         libmpdec: static library\n"
+    printf "# ========================================================================\n\n"
+
+    printf "Running official tests with allocation failures ...\n\n"
+    ./runtest official.decTest --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running additional tests with allocation failures ...\n\n"
+    ./runtest additional.decTest --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
+
+if [ -f ./runtest_shared ]; then
+    printf "\n# ========================================================================\n"
+    printf "#                         libmpdec: shared library\n"
+    printf "# ========================================================================\n\n"
+
+    PORTABLE_PWD=`pwd`
+    LD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH"
+    DYLD_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$DYLD_LIBRARY_PATH"
+    LD_64_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_64_LIBRARY_PATH"
+    LD_32_LIBRARY_PATH="$PORTABLE_PWD/../libmpdec:$LD_32_LIBRARY_PATH"
+    LD_LIBRARY_PATH_64="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH_64"
+    LD_LIBRARY_PATH_32="$PORTABLE_PWD/../libmpdec:$LD_LIBRARY_PATH_32"
+    PATH="$LD_LIBRARY_PATH:$PATH"
+    export LD_LIBRARY_PATH
+    export DYLD_LIBRARY_PATH
+    export LD_64_LIBRARY_PATH
+    export LD_32_LIBRARY_PATH
+    export LD_LIBRARY_PATH_64
+    export LD_LIBRARY_PATH_32
+
+    printf "Running official tests with allocation failures ...\n\n"
+    ./runtest_shared official.decTest --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+
+    printf "Running additional tests with allocation failures ...\n\n"
+    ./runtest_shared additional.decTest --alloc || { printf "\nFAIL\n\n\n"; exit 1; }
+fi
diff --git a/tests/runtest.c b/tests/runtest.c
new file mode 100644 (file)
index 0000000..a44655d
--- /dev/null
@@ -0,0 +1,5649 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#if defined(__GNUC__) || defined(__COMPCERT__)
+  #define _GNU_SOURCE
+#endif
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "mpdecimal.h"
+#include "test.h"
+#include "vctest.h"
+
+
+#define MAXLINE 400000
+#define MAXTOKEN 32
+
+#ifndef _MSC_VER
+  #include <inttypes.h>
+  #define ASSERT(p) if (!(p)) {abort();}
+#else
+  #define ASSERT(p) if (!(p)) {mpd_err_fatal("assertion failed");}
+#endif
+
+
+static int extended = 1;
+static int global_failure = 0;
+static int file_failure = 0;
+
+static mpd_ssize_t
+strtossize(const char *s, char **end, int base)
+{
+    int64_t retval;
+
+    errno = 0;
+    retval = _mpd_strtossize(s, end, base);
+    if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) {
+        errno = ERANGE;
+    }
+    if (errno == ERANGE) {
+        return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX;
+    }
+
+    return (mpd_ssize_t)retval;
+}
+
+static void
+mpd_init_rand(mpd_t *x)
+{
+    long r = random() % 100;
+    uint8_t sign = random()%2;
+
+    if (r >= 80) {
+        mpd_minalloc(x);
+    }
+    else if (r >= 60) {
+        mpd_minalloc(x);
+        mpd_set_flags(x, sign);
+    }
+    else if (r >= 40) {
+        mpd_setspecial(x, sign, MPD_NAN);
+    }
+    else if (r >= 20) {
+        mpd_setspecial(x, sign, MPD_SNAN);
+    }
+    else {
+        mpd_setspecial(x, sign, MPD_INF);
+    }
+}
+
+/* These ranges are needed for the official test suite
+ * and are generally not problematic at all. */
+#if defined(MPD_CONFIG_64)
+  #define MPD_READ_MAX_PREC 1070000000000000000LL
+#elif defined(MPD_CONFIG_32)
+  #define MPD_READ_MAX_PREC 1070000000
+#else
+  #error "config not defined"
+#endif
+
+static void
+mpd_readcontext(mpd_context_t *ctx)
+{
+    if (extended) {
+        ctx->prec=MPD_READ_MAX_PREC;
+        ctx->emax=MPD_READ_MAX_PREC;
+        ctx->emin=-MPD_READ_MAX_PREC;
+    }
+    else {
+        ctx->prec=MPD_MAX_PREC;
+        ctx->emax=MPD_MAX_EMAX;
+        ctx->emin=MPD_MIN_EMIN;
+    }
+
+    ctx->round=MPD_ROUND_HALF_UP;
+    ctx->traps=MPD_Traps;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=0;
+    ctx->allcr=1;
+}
+
+static void
+mpd_testcontext(mpd_context_t *ctx)
+{
+    if (extended) {
+    #if defined(MPD_CONFIG_64)
+        ctx->prec=MPD_MAX_PREC;
+        ctx->emax=MPD_MAX_EMAX;
+        ctx->emin=MPD_MIN_EMIN;
+    #elif defined(MPD_CONFIG_32)
+        /* These ranges are needed for the official test suite. */
+        ctx->prec=999999999;
+        ctx->emax=999999999;
+        ctx->emin=-999999999;
+    #else
+      #error "config not defined"
+    #endif
+    }
+    else {
+        ctx->prec=MPD_MAX_PREC;
+        ctx->emax=MPD_MAX_EMAX;
+        ctx->emin=MPD_MIN_EMIN;
+    }
+
+    ctx->round=MPD_ROUND_HALF_UP;
+    ctx->traps=MPD_Traps;
+    ctx->status=0;
+    ctx->newtrap=0;
+    ctx->clamp=0;
+    ctx->allcr=1;
+}
+
+static void
+mpd_assert_context_ok(const mpd_context_t *ctx)
+{
+    ASSERT(0 < ctx->prec && ctx->prec <= MPD_READ_MAX_PREC);
+    ASSERT(0 <= ctx->emax && ctx->emax <= MPD_READ_MAX_PREC);
+    ASSERT(-MPD_READ_MAX_PREC <= ctx->emin && ctx->emin <= 0);
+    ASSERT(0 <= ctx->round && ctx->round < MPD_ROUND_GUARD);
+    ASSERT(ctx->traps <= MPD_Max_status);
+    ASSERT(ctx->status <= MPD_Max_status);
+    ASSERT(ctx->clamp == 0 || ctx->clamp == 1);
+    ASSERT(ctx->allcr == 0 || ctx->allcr == 1);
+}
+
+/* Known differences that are within the spec. */
+struct result_diff {
+    const char *id;
+    const char *calc;
+    const char *expected;
+};
+
+struct status_diff {
+    const char *id;
+    uint32_t calc;
+    uint32_t expected;
+};
+
+static struct result_diff ulp_cases[] = {
+    /* Cases where the result is allowed to differ by less than one ULP.
+     * Only needed if ctx->allcr is 0. */
+    { "expx013", "1.001000", "1.001001" },
+    { "expx020", "1.000000", "1.000001" },
+    { "expx109", "0.999999910000004049999878", "0.999999910000004049999879" },
+    { "expx1036", "1.005088", "1.005087" },
+    { "expx350", "1.0000000", "1.0000001" },
+    { "expx351", "1.0000000", "1.0000001" },
+    { "expx352", "1.0000000", "1.0000001" },
+    {NULL, NULL, NULL}
+};
+
+static struct status_diff status_cases[] = {
+    /* With a reduced working precision in mpd_qpow() the status matches. */
+    { "pwsx803", MPD_Inexact|MPD_Rounded|MPD_Subnormal|MPD_Underflow, MPD_Inexact|MPD_Rounded },
+    {NULL, 0, 0}
+};
+
+static const char *skipit[] = {
+    /* NULL reference, decimal16, decimal32, or decimal128 */
+    "absx900",
+    "addx9990",
+    "addx9991",
+    "clam090",
+    "clam091",
+    "clam092",
+    "clam093",
+    "clam094",
+    "clam095",
+    "clam096",
+    "clam097",
+    "clam098",
+    "clam099",
+    "clam189",
+    "clam190",
+    "clam191",
+    "clam192",
+    "clam193",
+    "clam194",
+    "clam195",
+    "clam196",
+    "clam197",
+    "clam198",
+    "clam199",
+    "comx990",
+    "comx991",
+    "cotx9990",
+    "cotx9991",
+    "ctmx9990",
+    "ctmx9991",
+    "ddabs900",
+    "ddadd9990",
+    "ddadd9991",
+    "ddcom9990",
+    "ddcom9991",
+    "ddcot9990",
+    "ddcot9991",
+    "ddctm9990",
+    "ddctm9991",
+    "dddiv9998",
+    "dddiv9999",
+    "dddvi900",
+    "dddvi901",
+    "ddfma2990",
+    "ddfma2991",
+    "ddfma39990",
+    "ddfma39991",
+    "ddlogb900",
+    "ddmax900",
+    "ddmax901",
+    "ddmxg900",
+    "ddmxg901",
+    "ddmin900",
+    "ddmin901",
+    "ddmng900",
+    "ddmng901",
+    "ddmul9990",
+    "ddmul9991",
+    "ddnextm900",
+    "ddnextm900",
+    "ddnextp900",
+    "ddnextp900",
+    "ddnextt900",
+    "ddnextt901",
+    "ddqua998",
+    "ddqua999",
+    "ddred900",
+    "ddrem1000",
+    "ddrem1001",
+    "ddrmn1000",
+    "ddrmn1001",
+    "ddsub9990",
+    "ddsub9991",
+    "ddintx074",
+    "ddintx094",
+    "divx9998",
+    "divx9999",
+    "dvix900",
+    "dvix901",
+    "dqabs900",
+    "dqadd9990",
+    "dqadd9991",
+    "dqcom990",
+    "dqcom991",
+    "dqcot9990",
+    "dqcot9991",
+    "dqctm9990",
+    "dqctm9991",
+    "dqdiv9998",
+    "dqdiv9999",
+    "dqdvi900",
+    "dqdvi901",
+    "dqfma2990",
+    "dqfma2991",
+    "dqadd39990",
+    "dqadd39991",
+    "dqlogb900",
+    "dqmax900",
+    "dqmax901",
+    "dqmxg900",
+    "dqmxg901",
+    "dqmin900",
+    "dqmin901",
+    "dqmng900",
+    "dqmng901",
+    "dqmul9990",
+    "dqmul9991",
+    "dqnextm900",
+    "dqnextp900",
+    "dqnextt900",
+    "dqnextt901",
+    "dqqua998",
+    "dqqua999",
+    "dqred900",
+    "dqrem1000",
+    "dqrem1001",
+    "dqrmn1000",
+    "dqrmn1001",
+    "dqsub9990",
+    "dqsub9991",
+    "dqintx074",
+    "dqintx094",
+    "expx900",
+    "fmax2990",
+    "fmax2991",
+    "fmax39990",
+    "fmax39991",
+    "lnx900",
+    "logx900",
+    "logbx900",
+    "maxx900",
+    "maxx901",
+    "mxgx900",
+    "mxgx901",
+    "mnm900",
+    "mnm901",
+    "mng900",
+    "mng901",
+    "minx900",
+    "mulx990",
+    "mulx991",
+    "nextm900",
+    "nextp900",
+    "nextt900",
+    "nextt901",
+    "plu900",
+    "powx900",
+    "powx901",
+    "pwsx900",
+    "quax1022",
+    "quax1023",
+    "quax1024",
+    "quax1025",
+    "quax1026",
+    "quax1027",
+    "quax1028",
+    "quax1029",
+    "quax0a2",
+    "quax0a3",
+    "quax998",
+    "quax999",
+    "redx900",
+    "remx1000",
+    "remx1001",
+    "rmnx900",
+    "rmnx901",
+    "sqtx9900",
+    "subx9990",
+    "subx9991",
+    /* operand range violations, invalid context */
+    "expx901",
+    "expx902",
+    "expx903",
+    "expx905",
+    "lnx901",
+    "lnx902",
+    "lnx903",
+    "lnx905",
+    "logx901",
+    "logx902",
+    "logx903",
+    "logx905",
+    "powx1183",
+    "powx1184",
+    "powx4001",
+    "powx4002",
+    "powx4003",
+    "powx4005",
+    "powx4008",
+    "powx4010",
+    "powx4012",
+    "powx4014",
+    "scbx164",
+    "scbx165",
+    "scbx166",
+#if defined(MPD_CONFIG_32) && MPD_MINALLOC_MAX <= 4
+    /* Under the allocation failure tests, the result is numerically correct
+       (1 == 1.00000) but without zero padding. This is by design, since in
+       case of MPD_Malloc_error mpd_qsqrt() retries the operation with a lower
+       context precision and allows all exact results.
+
+       The MPD_MINALLOC_MAX < 64 feature is is officially unsupported but works
+       (if the little-endian mpd_ln10_data arrays are adjusted).
+    */
+    "sqtx9045",
+#endif
+    /* skipped for decNumber, too */
+    "powx4302",
+    "powx4303",
+    "powx4303",
+    "powx4342",
+    "powx4343",
+    "pwsx805",
+    /* disagreement for three arg power */
+    "pwmx325",
+    "pwmx326",
+    NULL
+};
+
+static inline int
+startswith(const char *token, const char *s)
+{
+    return token != NULL && strncasecmp(token, s, strlen(s)) == 0;
+}
+
+static inline int
+eqtoken(const char *token, const char *s)
+{
+    return token != NULL && strcasecmp(token, s) == 0;
+}
+
+static int
+check_skip(char *id)
+{
+    int i;
+
+    for (i = 0; skipit[i] != NULL; i++) {
+        if (eqtoken(id, skipit[i])) {
+#if defined(RT_VERBOSITY) && RT_VERBOSITY == 2
+            fprintf(stderr, "SKIP: %s\n", id);
+#endif
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static char *
+nexttoken(char *cp)
+{
+    static char *start = NULL;
+    static char *end = NULL;
+
+    if (cp == NULL) {
+        assert(end != NULL);
+        cp = end;
+    }
+
+    for (; *cp != '\0'; cp++) {
+        if (isspace((unsigned char)*cp)) {
+            /* empty */
+        }
+        else if (*cp == '"') {
+            start = end = cp+1;
+            for (; *end != '\0'; end++) {
+                if (*end == '"' && *(end+1) == '"')
+                    end += 1;
+                else if (*end == '"')
+                    break;
+            }
+            if (*end == '\0')
+                return NULL;
+            *end++ = '\0';
+            return start;
+        }
+        else if (*cp == '\'') {
+            start = end = cp+1;
+            for (; *end != '\0'; end++) {
+                if (*end == '\'' && *(end+1) == '\'')
+                    end += 1;
+                else if (*end == '\'')
+                    break;
+            }
+            if (*end == '\0')
+                return NULL;
+            *end++ = '\0';
+            return start;
+        }
+        else {
+            start = end = cp;
+            for (; *end != '\0'; end++)
+                if (isspace((unsigned char)*end))
+                    break;
+            if (*end == '\0')
+                return NULL;
+            *end++ = '\0';
+            return start;
+        }
+    }
+
+    return NULL;
+}
+
+/* split a line into tokens */
+static int
+split(char *token[], char line[])
+{
+    char *cp;
+    size_t len;
+    int n = 0;
+
+    cp = nexttoken(line);
+    while (n < MAXTOKEN && cp != NULL) {
+        len = strlen(cp);
+        if ((token[n] = malloc(len+1)) == NULL) {
+            mpd_err_fatal("out of memory");
+        }
+        strcpy(token[n], cp);
+        cp = nexttoken(NULL);
+        n++;
+    }
+    token[n] = NULL;
+
+    return n;
+}
+
+static void
+freetoken(char **token)
+{
+    while (*token != NULL) {
+        free(*token++);
+    }
+}
+
+/* returns all expected conditions in a status flag */
+static uint32_t
+scan_conditions(char **token)
+{
+    char *condition = *token;
+    uint32_t status = 0;
+
+    while (condition != NULL) {
+
+        if (startswith(condition, "--"))
+            break;
+        else if (eqtoken(condition, "Clamped"))
+            status |= MPD_Clamped;
+        else if (eqtoken(condition, "Conversion_syntax"))
+            status |= MPD_Conversion_syntax;
+        else if (eqtoken(condition, "Division_by_zero"))
+            status |= MPD_Division_by_zero;
+        else if (eqtoken(condition, "Division_impossible"))
+            status |= MPD_Division_impossible;
+        else if (eqtoken(condition, "Division_undefined"))
+            status |= MPD_Division_undefined;
+        else if (eqtoken(condition, "Fpu_error"))
+            status |= MPD_Fpu_error;
+        else if (eqtoken(condition, "Inexact"))
+            status |= MPD_Inexact;
+        else if (eqtoken(condition, "Invalid_context"))
+            status |= MPD_Invalid_context;
+        else if (eqtoken(condition, "Invalid_operation"))
+            status |= MPD_Invalid_operation;
+        else if (eqtoken(condition, "Malloc_error"))
+            status |= MPD_Malloc_error;
+        else if (eqtoken(condition, "Not_implemented"))
+            status |= MPD_Not_implemented;
+        else if (eqtoken(condition, "Overflow"))
+            status |= MPD_Overflow;
+        else if (eqtoken(condition, "Rounded"))
+            status |= MPD_Rounded;
+        else if (eqtoken(condition, "Subnormal"))
+            status |= MPD_Subnormal;
+        else if (eqtoken(condition, "Underflow"))
+            status |= MPD_Underflow;
+        else
+            mpd_err_fatal("unknown status: %s", condition);
+
+        condition = *(++token);
+    }
+
+    return status;
+}
+
+static void
+compare_expected(const char *calc, const char *expected, uint32_t expected_status,
+                 char *id, mpd_context_t *ctx)
+{
+    char ctxstatus[MPD_MAX_FLAG_STRING];
+    char expstatus[MPD_MAX_FLAG_STRING];
+
+
+#ifndef RT_VERBOSITY
+    /* Do not print known pseudo-failures. */
+    int i;
+
+    /* known ULP diffs */
+    if (ctx->allcr == 0) {
+        for (i = 0; ulp_cases[i].id != NULL; i++) {
+            if (eqtoken(id, ulp_cases[i].id) &&
+                strcmp(expected, ulp_cases[i].expected) == 0 &&
+                strcmp(calc, ulp_cases[i].calc) == 0) {
+                return;
+            }
+        }
+    }
+
+    /* known status diffs */
+    for (i = 0; status_cases[i].id != NULL; i++) {
+        if (eqtoken(id, status_cases[i].id) &&
+            expected_status == status_cases[i].expected &&
+            ctx->status == status_cases[i].calc) {
+            return;
+        }
+    }
+#endif
+
+    if (strcmp(calc, expected) != 0) {
+        if (file_failure == 0) {
+            fputs("\n\n", stderr);
+        }
+        fprintf(stderr, "FAIL: %s  calc: %s  expected: %s\n",
+                        id, calc, expected);
+        global_failure = file_failure = 1;
+    }
+    if (ctx->status != expected_status) {
+        if (file_failure == 0) {
+            fputs("\n\n", stderr);
+        }
+        mpd_snprint_flags(ctxstatus, MPD_MAX_FLAG_STRING, ctx->status);
+        mpd_snprint_flags(expstatus, MPD_MAX_FLAG_STRING, expected_status);
+        fprintf(stderr, "FAIL: %s: status:  calc: %s  expected: %s\n",
+                id, ctxstatus, expstatus);
+        global_failure = file_failure = 1;
+    }
+}
+
+static int
+equalmem(const mpd_t *a, const mpd_t *b)
+{
+    mpd_ssize_t i;
+
+    if (a->flags != b->flags) return 0;
+    if (a->exp != b->exp) return 0;
+    if (a->len != b->len) return 0;
+    if (a->digits != b->digits) return 0;
+    for (i = 0; i < a->len; i++)
+        if (a->data[i] != b->data[i])
+            return 0;
+    return 1;
+}
+
+static void
+check_equalmem(const mpd_t *a, const mpd_t *b, const char *id)
+{
+    if (!equalmem(a, b)) {
+        fprintf(stderr, "FAIL: const arg changed: %s\n", id);
+    }
+}
+
+static unsigned long
+get_testno(char *token)
+{
+    char *number;
+
+    number = strpbrk(token, "0123456789");
+    ASSERT(number != NULL);
+    return strtoul(number, NULL, 10);
+}
+
+/* scan a single operand and the expected result */
+static int
+scan_1op_result(mpd_t *op1, char **result, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* discard "->" */
+    if (token[3] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+
+    /* expected result */
+    if (token[4] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+    *result = token[4];
+
+    return 5;
+}
+
+/* scan a single operand and two results */
+static int
+scan_1op_2results(mpd_t *op1, char **result1, char **result2, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* discard "->" */
+    if (token[3] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+
+    /* expected result1 */
+    if (token[4] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+    *result1 = token[4];
+
+    /* expected result2 */
+    if (token[5] == NULL) {
+        mpd_err_fatal("parse error at id %s", token[0]);
+    }
+    *result2 = token[5];
+
+    return 6;
+}
+
+/* scan decimal operand, string operand and the expected result */
+static int
+scan_1op_str_result(mpd_t *op1, char **op2, char **result, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* operand 2 */
+    if (token[3] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *op2 = token[3];
+
+    /* discard "->" */
+    if (token[4] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+
+    /* expected result */
+    if (token[5] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *result = token[5];
+
+    return 6;
+}
+
+/* scan two operands and the expected result */
+static int
+scan_2ops_result(mpd_t *op1, mpd_t *op2, char **result, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* operand 2 */
+    if (token[3] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op2, token[3], ctx);
+
+    /* discard "->" */
+    if (token[4] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+
+    /* expected result */
+    if (token[5] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *result = token[5];
+
+    return 6;
+}
+
+/* scan two operands and two results */
+static int
+scan_2ops_2results(mpd_t *op1, mpd_t *op2, char **result1, char **result2, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* operand 2 */
+    if (token[3] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op2, token[3], ctx);
+
+    /* discard "->" */
+    if (token[4] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+
+    /* expected result1 */
+    if (token[5] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *result1 = token[5];
+
+    /* expected result2 */
+    if (token[6] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *result2 = token[6];
+
+    return 7;
+}
+
+/* scan three operands and the expected result */
+static int
+scan_3ops_result(mpd_t *op1, mpd_t *op2, mpd_t *op3, char **result, char *token[], mpd_context_t *ctx)
+{
+    /* operand 1 */
+    if (token[2] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op1, token[2], ctx);
+
+    /* operand 2 */
+    if (token[3] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op2, token[3], ctx);
+
+    /* operand 2 */
+    if (token[4] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    mpd_set_string(op3, token[4], ctx);
+
+    /* discard "->" */
+    if (token[5] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+
+    /* expected result */
+    if (token[6] == NULL) {
+        mpd_err_fatal("%s", token[0]);
+    }
+    *result = token[6];
+
+    return 7;
+}
+
+static mpd_t *op, *op1, *op2, *op3;
+static mpd_t *tmp, *tmp1, *tmp2, *tmp3;
+static mpd_t *result, *result1, *result2;
+
+/* Test triple conversion */
+static void
+_TripleTest(const mpd_t *a, mpd_context_t *ctx, const char *testno)
+{
+    mpd_uint128_triple_t triple;
+    uint32_t status = 0;
+    int ret = 0;
+
+#ifdef MPD_CONFIG_32
+    /*
+     * 32-bit: as_triple() expects well-formed decimals. Skip test cases
+     * that use the extended exponent, which is safe in the tests but not
+     * in production.
+     */
+     if (a->exp < MPD_MIN_ETINY || a->exp > MPD_MAX_EMAX) {
+         return;
+     }
+#endif
+
+    triple = mpd_as_uint128_triple(a);
+    switch (triple.tag) {
+    case MPD_TRIPLE_QNAN: case MPD_TRIPLE_SNAN:
+        ASSERT(triple.exp == 0)
+        break;
+    case MPD_TRIPLE_INF:
+        ASSERT(triple.hi == 0 && triple.lo == 0 && triple.exp == 0)
+        break;
+    case MPD_TRIPLE_NORMAL:
+        break;
+    case MPD_TRIPLE_ERROR:
+        ASSERT(triple.sign == 0 && triple.hi == 0 && triple.lo == 0 && triple.exp == 0)
+        break;
+    }
+
+    /* Allocation failures in from_triple() */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(result);
+
+        mpd_set_alloc_fail(ctx);
+        status = 0;
+        ret = mpd_from_uint128_triple(result, &triple, &status);
+        mpd_set_alloc(ctx);
+
+        if (!(status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(ret == -1)
+        ASSERT(mpd_isnan(result))
+    }
+
+    if (triple.tag != MPD_TRIPLE_ERROR) {
+        ASSERT(ret == 0)
+        ASSERT(status == 0)
+        check_equalmem(result, a, testno);
+    }
+    else {
+        ASSERT(ret == -1)
+        ASSERT(status == MPD_Conversion_syntax)
+        ASSERT(mpd_isnan(result))
+    }
+}
+
+/* Test both versions of mpd_to_sci. Do not use this if alloc_fail is set,
+   since MPD_Malloc_error will only be triggered for one of the functions. */
+static char *
+_mpd_to_sci(const mpd_t *dec, int fmt)
+{
+    char *r, *s;
+    mpd_ssize_t size;
+
+    r = mpd_to_sci(dec, fmt);
+    size = mpd_to_sci_size(&s, dec, fmt);
+
+    if (r == NULL) {
+        ASSERT(size == -1)
+        ASSERT(s == NULL)
+    }
+    else {
+        ASSERT(strcmp(r, s) == 0)
+        ASSERT(size == (mpd_ssize_t)strlen(s))
+    }
+
+    mpd_free(s);
+    return r;
+}
+
+/*
+ * Test a function returning pointer to char, accepting:
+ *     op1, context
+ *
+ * This function is used for "toSci", "toEng" and "apply"
+ * and does not use a maxctx for the conversion of the operand.
+ */
+static void
+_cp_MpdCtx(char **token, char *(*func)(const mpd_t *, int), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected, *expected_fail = NULL;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+
+    mpd_context_t *workctx = ctx;
+    workctx->status = 0;
+    n = scan_1op_result(op, &expected, token, workctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+    /* Allocation failures for mpd_set_string */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        workctx->status = 0;
+
+        mpd_set_alloc_fail(workctx);
+        (void)scan_1op_result(tmp, &expected_fail, token, workctx);
+        mpd_set_alloc(workctx);
+
+        if (!(workctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    ASSERT(strcmp(expected, expected_fail) == 0)
+    ASSERT(mpd_cmp_total(tmp, op) == 0)
+
+
+    /* make a copy of the operand */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, workctx);
+
+    calc = func(tmp, 1);
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], workctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, workctx);
+
+        mpd_set_alloc_fail(workctx);
+        calc = func(tmp, 1);
+        mpd_set_alloc(workctx);
+
+        if (calc != NULL) {
+            break;
+        }
+    }
+    compare_expected(calc, expected, expstatus, token[0], workctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+}
+
+/* Test mpd_to_sci_size() and mpd_to_eng_size(). */
+static void
+sci_eng_size(char **token, mpd_ssize_t (*func)(char **, const mpd_t *, int), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected, *expected_fail = NULL;
+    uint32_t expstatus;
+    mpd_ssize_t size;
+    int n;
+
+    mpd_readcontext(&maxctx);
+
+    mpd_context_t *workctx = ctx;
+    workctx->status = 0;
+    n = scan_1op_result(op, &expected, token, workctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+    /* Allocation failures for mpd_set_string */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        workctx->status = 0;
+
+        mpd_set_alloc_fail(workctx);
+        (void)scan_1op_result(tmp, &expected_fail, token, workctx);
+        mpd_set_alloc(workctx);
+
+        if (!(workctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    ASSERT(strcmp(expected, expected_fail) == 0)
+    ASSERT(mpd_cmp_total(tmp, op) == 0)
+
+
+    /* make a copy of the operand */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, workctx);
+
+    size = func(&calc, tmp, 1);
+    ASSERT(size == (mpd_ssize_t)strlen(calc))
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], workctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, workctx);
+
+        mpd_set_alloc_fail(workctx);
+        size = func(&calc, tmp, 1);
+        mpd_set_alloc(workctx);
+
+        if (calc != NULL) {
+            ASSERT(size == (mpd_ssize_t)strlen(calc))
+            break;
+        }
+    }
+    compare_expected(calc, expected, expstatus, token[0], workctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+}
+
+/* Quick and dirty: parse hex escape sequences */
+static char *
+parse_escapes(const char *s)
+{
+    char hex[5];
+    char *res, *cp;
+    unsigned int u;
+    int n;
+
+    cp = res = malloc(strlen(s)+1);
+    if (res == NULL) {
+        return NULL;
+    }
+
+    hex[0] = '0';
+    hex[1] = '\0';
+    while (*s) {
+        if (*s == '\\' && *(s+1) == 'x') {
+            for (n = 1; n < 4; n++) {
+                if (!s[n]) {
+                    free(res);
+                    return NULL;
+                }
+                hex[n] = s[n];
+            }
+            hex[n] = '\0';
+            sscanf(hex, "%x%n", &u, &n);
+            *cp++ = (char)u;
+            s += n;
+        }
+        else {
+            *cp++ = *s++;
+        }
+    }
+
+    *cp = '\0';
+    return res;
+}
+
+/*
+ * Test a function returning pointer to char, accepting:
+ *     op1, fmt, context
+ *
+ * This function is used for "mpd_format".
+ */
+static void
+_cp_MpdFmtCtx(char **token, char *(*func)(const mpd_t *, const char *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *fmt;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_str_result(op1, &fmt, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+
+    fmt = parse_escapes(fmt);
+    expected = parse_escapes(expected);
+
+    expstatus = scan_conditions(token+n);
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op1, ctx);
+    ctx->status = 0;
+
+    calc = func(tmp, fmt, ctx);
+
+    /* compare the calculated result to the expected result */
+    if (calc == NULL) {
+        compare_expected("NULL", expected, expstatus, token[0], ctx);
+    }
+    else {
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+    }
+    check_equalmem(tmp, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op1, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        calc = func(tmp, fmt, ctx);
+        mpd_set_alloc(ctx);
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(calc == NULL)
+    }
+    if (calc == NULL) {
+        compare_expected("NULL", expected, expstatus, token[0], ctx);
+    }
+    else {
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+    }
+    check_equalmem(tmp, op1, token[0]);
+
+    free(fmt);
+    free(expected);
+}
+
+/*
+ * Test a function returning pointer to const char, accepting:
+ *     op1, context
+ */
+static void
+_ccp_MpdCtx(char **token, const char *(*func)(const mpd_t *, const mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    const char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    calc = func(tmp, ctx);
+
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        calc = func(tmp, ctx);
+        mpd_set_alloc(ctx);
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(calc == NULL)
+    }
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+
+/* Test a unary function */
+static void
+_Res_Op_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n, incr;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* result and tmp are distinct decimals */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result, tmp, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* result equals operand */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp, tmp, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/* Test a unary function, quantize the operand before applying the actual function */
+static void
+_Res_Op_CtxWithQuantize(char *token[], void (*func)(mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n, incr;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op, op1, &expected, token, &maxctx);
+    mpd_quantize(op, op, op1, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+    _TripleTest(op1, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* result and tmp are distinct decimals */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result, tmp, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* result equals operand */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp, tmp, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+static void
+resolve_status_hack(uint32_t *expstatus, const uint32_t status)
+{
+    /* hack #1 to resolve disagreement with results generated by decimal.py */
+    if ((*expstatus & MPD_Invalid_operation) &&
+        (status & MPD_Division_impossible)) {
+        *expstatus = MPD_Division_impossible;
+    }
+
+    /* hack #2 to resolve disagreement with results generated by decimal.py */
+    if ((*expstatus & MPD_Invalid_operation) &&
+        (status & MPD_Division_undefined)) {
+        *expstatus = MPD_Division_undefined;
+    }
+}
+
+/* Test a binary function */
+static void
+_Res_Binop_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *),
+               mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n, incr;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+    /* three distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp1, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+}
+
+/* Test a binary function where op1 == op2. */
+static void
+_Res_EqualBinop_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *),
+                    mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* equal operands, distinct result */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* all parameters equal */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    func(tmp, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/* Test a binary function with a binary result */
+static void
+_Binres_Binop_Ctx(char *token[], void (*func)(mpd_t *, mpd_t*, const mpd_t *, const mpd_t *, mpd_context_t *),
+                  mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected1, *expected2;
+    uint32_t expstatus;
+    int n, incr;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_2results(op1, op2, &expected1, &expected2, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* four distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result1);
+    mpd_init_rand(result2);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result1, result2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    resolve_status_hack(&expstatus, ctx->status);
+
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result1);
+        mpd_minalloc(result2);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result1, result2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result1))
+        ASSERT(mpd_isnan(result2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result1 == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result2);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp1, result2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail <= INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result2);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, result2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+        ASSERT(mpd_isnan(result2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result2 == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result1);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result1, tmp1, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result1);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result1, tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result1))
+        ASSERT(mpd_isnan(tmp1))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result1 == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result2);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp2, result2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result2);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, result2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+        ASSERT(mpd_isnan(result2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+
+
+    /* result2 == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result1);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result1, tmp2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result1);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result1, tmp2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result1))
+        ASSERT(mpd_isnan(tmp2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp1, op1, token[0]);
+
+
+    /* result1 == tmp1, result2 == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp1, tmp2, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+        ASSERT(mpd_isnan(tmp2))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+
+    /* result1 == tmp2, result2 == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp2, tmp1, tmp1, tmp2, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+        ASSERT(mpd_isnan(tmp1))
+
+        if (alloc_fail > 50) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/* Test a binary function with a binary result; equal operands */
+static void
+_Binres_EqualBinop_Ctx(char *token[], void (*func)(mpd_t *, mpd_t*, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected1, *expected2;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_2results(op, &expected1, &expected2, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* distinct results */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result1);
+    mpd_init_rand(result2);
+    ctx->status = 0;
+
+    func(result1, result2, tmp, tmp, ctx);
+
+    resolve_status_hack(&expstatus, ctx->status);
+
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result1);
+        mpd_minalloc(result2);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result1, result2, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result1))
+        ASSERT(mpd_isnan(result2))
+    }
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* result1 == tmp */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result2);
+    ctx->status = 0;
+
+    func(tmp, result2, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result2);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp, result2, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+        ASSERT(mpd_isnan(result2))
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(result2, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+
+    /* result2 == tmp */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result1);
+    ctx->status = 0;
+
+    func(result1, tmp, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result1);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result1, tmp, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result1))
+        ASSERT(mpd_isnan(tmp))
+    }
+    calc = _mpd_to_sci(result1, 1);
+    compare_expected(calc, expected1, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected2, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/* Test a ternary function */
+static void
+_Res_Ternop_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n, incr;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_3ops_result(op1, op2, op3, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+    _TripleTest(op3, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* four distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_init_rand(tmp3);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_copy(tmp3, op3, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(result, tmp1, tmp2, tmp3, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_init_rand(tmp3);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_copy(tmp3, op3, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, tmp2, tmp3, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_init_rand(tmp3);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_copy(tmp3, op3, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp1, tmp1, tmp2, tmp3, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_init_rand(tmp3);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_copy(tmp3, op3, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, tmp2, tmp3, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_init_rand(tmp3);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_copy(tmp3, op3, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp2, tmp1, tmp2, tmp3, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_init_rand(tmp3);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_copy(tmp3, op3, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp2, tmp3, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp3, op3, token[0]);
+
+
+    /* result == tmp3 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_init_rand(tmp3);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_copy(tmp3, op3, ctx);
+    ctx->status = 0;
+
+    mpd_set_alloc_count(ctx);
+    func(tmp3, tmp1, tmp2, tmp3, ctx);
+    mpd_set_alloc(ctx);
+
+    calc = _mpd_to_sci(tmp3, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    incr = 1;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail += incr) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_init_rand(tmp3);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_copy(tmp3, op3, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp3, tmp1, tmp2, tmp3, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp3))
+
+        if (alloc_fail > 100) {
+            incr = (int)(alloc_count*0.02) + 1;
+        }
+    }
+    calc = _mpd_to_sci(tmp3, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+}
+
+/* Test a ternary function, first and second operand equal */
+static void
+_Res_EqEqOp_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* distinct result */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp1, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp1, tmp1, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp2, tmp1, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+}
+
+/* Test a ternary function, first and third operand equal */
+static void
+_Res_EqOpEq_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* distinct result */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp1, tmp2, tmp1, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, tmp2, tmp1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp1, tmp1, tmp2, tmp1, ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, tmp2, tmp1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp2, tmp1, tmp2, tmp1, ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp2, tmp1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+}
+
+/* Test a ternary function, second and third operand equal */
+static void
+_Res_OpEqEq_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* distinct result */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp1, tmp2, tmp2, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, tmp2, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp2, tmp1, tmp2, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp2, tmp1, tmp2, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    func(tmp1, tmp1, tmp2, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, tmp2, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp2, op2, token[0]);
+}
+
+/* Test a ternary function, first, second and third operand equal */
+static void
+_Res_EqEqEq_Ctx(char *token[], void (*func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* distinct result */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* result == tmp */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    func(tmp, tmp, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp, tmp, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/*
+ * Test a binary function that returns an additional integer result.
+ * Used for the comparison functions.
+ */
+static void
+_Int_Res_Binop_Ctx(char *token[], int (*func)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    int int_result;
+    char buf[11];
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* three distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    int_result = func(result, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(result, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp1, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp1, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp2, op2, token[0]);
+
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp2, tmp1, tmp2, ctx);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp2, tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+}
+
+/*
+ * Test a binary function that returns an additional integer result.
+ * Equal operands.
+ * Used for the comparison functions.
+ */
+static void
+_Int_Res_EqualBinop_Ctx(char *token[], int (*func)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    int int_result;
+    char buf[11];
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* equal operands */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    int_result = func(result, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(result, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* all parameters equal */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp, tmp, tmp, ctx);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp, tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+}
+
+/*
+ * Test a binary function that returns an additional integer result.
+ * Function does not take a context argument.
+ * Used for the comparison functions.
+ */
+static void
+_Int_Res_Binop(char *token[], int (*func)(mpd_t *, const mpd_t *, const mpd_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    int int_result;
+    char buf[11];
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* three distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    int_result = func(result, tmp1, tmp2);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(result, tmp1, tmp2);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp1, tmp1, tmp2);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp1, tmp1, tmp2);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp2, op2, token[0]);
+
+
+    /* result == tmp2 */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp2, tmp1, tmp2);
+
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp2, tmp1, tmp2);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp2))
+    }
+    calc = _mpd_to_sci(tmp2, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+}
+
+/*
+ * Test a binary function that returns an additional integer result.
+ * Function does not take a context argument.
+ * Equal operands.
+ * Used for the comparison functions.
+ */
+static void
+_Int_Res_EqualBinop(char *token[], int (*func)(mpd_t *, const mpd_t *, const mpd_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    int int_result;
+    char buf[11];
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* equal operands */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    int_result = func(result, tmp, tmp);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(result, tmp, tmp);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+
+
+    /* all parameters equal */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp, tmp, tmp);
+
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp, tmp, tmp);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp))
+    }
+    calc = _mpd_to_sci(tmp, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    snprintf(buf, 11, "%d", int_result);
+    if (int_result != INT_MAX) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+}
+
+/*
+ * Test a binary function that returns only an integer result.
+ * Used for the cmp functions.
+ */
+enum {SKIP_NONE, SKIP_NAN, SKIP_NONINT};
+static void
+_Int_Binop_Ctx(int skip, char *token[], int (*func)(const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int int_result;
+    char buf[11];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* two distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp1, tmp2, ctx);
+
+    snprintf(buf, 11, "%d", int_result);
+    if (!(skip && int_result == INT_MAX)) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp1, tmp2, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(int_result== INT_MAX)
+    }
+    snprintf(buf, 11, "%d", int_result);
+    if (!(skip && int_result == INT_MAX)) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+}
+
+/*
+ * Test a binary function that returns only an integer result.
+ * Equal operands.
+ * Used for the cmp functions.
+ */
+static void
+_Int_EqualBinop_Ctx(int skip, char *token[], int (*func)(const mpd_t *, const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int int_result;
+    char buf[11];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* equal operands */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp, tmp, ctx);
+
+    snprintf(buf, 11, "%d", int_result);
+    if (!(skip && int_result == INT_MAX)) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp, tmp, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(int_result== INT_MAX)
+    }
+    snprintf(buf, 11, "%d", int_result);
+    if (!(skip && int_result == INT_MAX)) { /* NaN cases are skipped for the int_retval */
+        compare_expected(buf, expected, expstatus, token[0], ctx);
+    }
+    check_equalmem(tmp, op, token[0]);
+}
+
+/*
+ * Test a binary function that returns an int.
+ * The function does not take a context argument.
+ */
+static void
+_Int_Binop(char *token[], int (*func)(const mpd_t *, const mpd_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int int_result;
+    char buf[11];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* two distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_init_rand(tmp2);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_copy(tmp2, op2, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp1, tmp2);
+
+    snprintf(buf, 11, "%d", int_result);
+    compare_expected(buf, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_init_rand(tmp2);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_copy(tmp2, op2, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp1, tmp2);
+        mpd_set_alloc(ctx);
+
+        if (int_result != INT_MAX) {
+            break;
+        }
+    }
+    snprintf(buf, 11, "%d", int_result);
+    compare_expected(buf, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp1, op1, token[0]);
+    check_equalmem(tmp2, op2, token[0]);
+}
+
+/*
+ * Test a binary function that returns an int.
+ * Equal operands.
+ * The function does not take a context argument.
+ */
+static void
+_Int_EqualBinop(char *token[], int (*func)(const mpd_t *, const mpd_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int int_result;
+    char buf[11];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* equal operands */
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    int_result = func(tmp, tmp);
+
+    snprintf(buf, 11, "%d", int_result);
+    compare_expected(buf, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp);
+        mpd_copy(tmp, op, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        int_result = func(tmp, tmp);
+        mpd_set_alloc(ctx);
+
+        if (int_result != INT_MAX) {
+            break;
+        }
+    }
+    snprintf(buf, 11, "%d", int_result);
+    compare_expected(buf, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+
+static mpd_ssize_t
+scan_ssize(char *token[])
+{
+    errno = 0;
+    if (token[1] == NULL) {
+        errno = 1;
+        return MPD_SSIZE_MAX;
+    }
+    return strtossize(token[1], NULL, 10);
+}
+
+static void
+_mpd_shiftl(mpd_t *res, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx)
+{
+    ASSERT(!mpd_isspecial(a));
+    mpd_shiftl(res, a, n, ctx);
+}
+
+static void
+_mpd_shiftr(mpd_t *res, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx)
+{
+    ASSERT(!mpd_isspecial(a));
+    (void)mpd_shiftr(res, a, n, ctx);
+}
+
+/*
+ * Test a function with an mpd_t and an mpd_ssize_t operand.
+ * Used for the shift functions.
+ */
+static void
+_Res_Op_Lsize_Ctx(int skip, char *token[], void (*func)(mpd_t *, const mpd_t *, mpd_ssize_t, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    mpd_ssize_t ssize;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_2ops_result(op1, op2, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+    _TripleTest(op2, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* only integers are allowed for ssize */
+    if (skip && (mpd_isspecial(op2) || op2->exp != 0)) {
+        /* fprintf(stderr, "SKIP: %s\n", token[0]); */
+        return;
+    }
+    ssize = mpd_get_ssize(op2, &maxctx);
+    if (maxctx.status&MPD_Invalid_operation) {
+        return;
+    }
+
+    /* two distinct decimals */
+    mpd_init_rand(tmp1);
+    mpd_copy(tmp1, op1, ctx);
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    func(result, tmp1, ssize, ctx);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_copy(tmp1, op1, ctx);
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(result, tmp1, ssize, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    check_equalmem(tmp1, op1, token[0]);
+
+
+    /* result == tmp1 */
+    mpd_init_rand(tmp1);
+    mpd_copy(tmp1, op1, ctx);
+    ctx->status = 0;
+
+    func(tmp1, tmp1, ssize, ctx);
+
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_init_rand(tmp1);
+        mpd_copy(tmp1, op1, ctx);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        func(tmp1, tmp1, ssize, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(tmp1))
+    }
+    calc = _mpd_to_sci(tmp1, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+/* test mpd_qln10() */
+static void
+_test_mpd_qln10(int skip, char *token[], mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    char *calc;
+    char *expected;
+    uint32_t expstatus;
+    mpd_ssize_t ssize;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op1, &expected, token, &maxctx);
+    _TripleTest(op1, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+
+    /* only integers are allowed for ssize */
+    if (skip && (mpd_isspecial(op1) || op1->exp != 0)) {
+        /* fprintf(stderr, "SKIP: %s\n", token[0]); */
+        return;
+    }
+    ssize = mpd_get_ssize(op1, &maxctx);
+    if (maxctx.status&MPD_Invalid_operation) {
+        return;
+    }
+
+    mpd_init_rand(result);
+    ctx->status = 0;
+
+    mpd_qln10(result, ssize, &ctx->status);
+
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+
+    /* Allocation failures */
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_qln10(result, ssize, &ctx->status);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+}
+
+static void
+_Baseconv(char *token[], mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    uint32_t base;
+    uint16_t *data16;
+    uint32_t *data32;
+    size_t len16, len32;
+    size_t expected_len16, expected_len32;
+    char *expected;
+    uint32_t expstatus;
+    char *calc;
+    int n = 0;
+    int i, iter = 0;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    n = scan_1op_result(op1, &expected, token, &maxctx);
+    ASSERT(mpd_isinteger(op1))
+    _TripleTest(op1, ctx, token[0]);
+
+    /* scan expected conditions */
+    expstatus = scan_conditions(token+n);
+
+    /*
+     * base := (1<<15)
+     * data16 := NULL
+     * Allocation and deallocation on error by mpd_export_u16().
+     */
+    base = (1<<15);
+    data16 = NULL;
+    expected_len16 = mpd_export_u16(&data16, 0, base, op1, ctx);
+    if (expected_len16 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data16);
+
+    /*
+     * base := (1<<15)
+     * data16 := NULL
+     * Test allocation failures.
+     */
+    base = (1<<15);
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        data16 = NULL;
+        expected_len16 = mpd_export_u16(&data16, 0, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        /* If data16 == NULL, it is deallocated on failure. */
+        ASSERT(expected_len16 == SIZE_MAX)
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data16);
+
+    /*
+     * base := (1<<15)
+     * len(data16) := 1
+     * Simulate result from sizeinbase() that is too small.
+     */
+    base = (1<<15);
+    data16 = mpd_alloc(1, sizeof *data16);
+    expected_len16 = mpd_export_u16(&data16, 1, base, op1, ctx);
+    if (expected_len16 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data16);
+
+    /*
+     * base := (1<<15)
+     * len(data16) == 1
+     * Test allocation failures.
+     */
+    base = (1<<15);
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        data16 = mpd_alloc(1, sizeof *data16);
+        mpd_set_alloc_fail(ctx);
+        expected_len16 = mpd_export_u16(&data16, 1, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        /* Caller must free the memory that was passed in. */
+        mpd_free(data16);
+        ASSERT(expected_len16 == SIZE_MAX)
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data16);
+
+
+    /*
+     * base := (1<<16)
+     * len(data16) := mpd_sizeinbase()
+     */
+    base = (1U<<16);
+    len16 = mpd_sizeinbase(op1, base);
+    data16 = mpd_alloc((mpd_size_t)len16, sizeof *data16);
+    len16 = mpd_export_u16(&data16, len16, base, op1, ctx);
+    if (len16 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u16(result, data16, len16, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data16);
+
+
+    /*
+     * base := 9999
+     * len(data16) := mpd_sizeinbase()
+     */
+    base = 9999;
+    len16 = mpd_sizeinbase(op1, base);
+    data16 = mpd_alloc((mpd_size_t)len16, sizeof *data16);
+    expected_len16 = mpd_export_u16(&data16, len16, base, op1, ctx);
+    if (expected_len16 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data16);
+
+
+    /*
+     * base := [2..16]
+     * len(data16) := mpd_sizeinbase()
+     */
+    iter = 16;
+    for (i = 2; i <= iter; i++) {
+
+        base = (uint32_t)i;
+        len16 = mpd_sizeinbase(op1, base);
+        data16 = mpd_alloc((mpd_size_t)len16, sizeof *data16);
+        len16 = mpd_export_u16(&data16, len16, base, op1, ctx);
+        if (len16 == SIZE_MAX) {
+            mpd_err_fatal("export_to_base failed");
+        }
+
+        mpd_import_u16(result, data16, len16, MPD_POS, base, ctx);
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+
+        mpd_free(calc);
+        mpd_free(data16);
+    }
+
+    /*
+     * base := random(UINT_16_MAX)
+     * len(data16) := mpd_sizeinbase()
+     */
+    iter = 5;
+    for (i = 0; i < iter; i++) {
+
+        base = random() % UINT16_MAX;
+        if (base < 2) base = 2;
+
+        len16 = mpd_sizeinbase(op1, base);
+        data16 = mpd_alloc((mpd_size_t)len16, sizeof *data16);
+        len16 = mpd_export_u16(&data16, len16, base, op1, ctx);
+        if (len16 == SIZE_MAX) {
+            mpd_err_fatal("export_to_base failed");
+        }
+
+        mpd_import_u16(result, data16, len16, MPD_POS, base, ctx);
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+
+        mpd_free(calc);
+        mpd_free(data16);
+
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            ctx->status = 0;
+
+            data16 = mpd_alloc(1, sizeof *data16);
+            mpd_set_alloc_fail(ctx);
+            expected_len16 = mpd_export_u16(&data16, 1, base, op1, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            /* Caller must free the memory that was passed in. */
+            mpd_free(data16);
+            ASSERT(expected_len16 == SIZE_MAX)
+        }
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            mpd_minalloc(result);
+            ctx->status = 0;
+
+            mpd_set_alloc_fail(ctx);
+            mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(mpd_isnan(result))
+        }
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+        mpd_free(data16);
+
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            ctx->status = 0;
+
+            data16 = mpd_alloc(1, sizeof *data16);
+            mpd_set_alloc_fail(ctx);
+            expected_len16 = mpd_export_u16(&data16, 1, base, op1, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            /* Caller must free the memory that was passed in. */
+            mpd_free(data16);
+            ASSERT(expected_len16 == SIZE_MAX)
+        }
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            mpd_minalloc(result);
+            ctx->status = 0;
+
+            mpd_set_alloc_fail(ctx);
+            mpd_import_u16(result, data16, expected_len16, MPD_POS, base, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(mpd_isnan(result))
+        }
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+        mpd_free(data16);
+    }
+
+    /* ================================================================ */
+    /*                         uint32_t bases                           */
+    /* ================================================================ */
+
+    /*
+     * base := (1<<30)
+     * data32 := NULL
+     */
+    base = (1<<30);
+    data32 = NULL;
+    expected_len32 = mpd_export_u32(&data32, 0, base, op1, ctx);
+    if (expected_len32 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data32);
+
+    /*
+     * base := (1<<30)
+     * data32 := NULL
+     * Test allocation failures. */
+    base = (1<<30);
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        data32 = NULL;
+        expected_len32 = mpd_export_u32(&data32, 0, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(expected_len32 == SIZE_MAX)
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data32);
+
+
+    /*
+     * base := (1<<30)
+     * len(data32) := 1
+     */
+    base = (1<<30);
+    data32 = mpd_alloc(1, sizeof *data32);
+    expected_len32 = mpd_export_u32(&data32, 1, base, op1, ctx);
+    if (expected_len32 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data32);
+
+    /*
+     * base := (1<<30)
+     * len(data32) := 1
+     * Test allocation failures.
+     */
+    base = (1<<30);
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        data32 = mpd_alloc(1, sizeof *data32);
+        mpd_set_alloc_fail(ctx);
+        expected_len32 = mpd_export_u32(&data32, 1, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(expected_len32 == SIZE_MAX)
+        mpd_free(data32);
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data32);
+
+
+    /*
+     * base := 10**9
+     * len(data32) := mpd_sizeinbase()
+     */
+    base = 1000000000;
+    len32 = mpd_sizeinbase(op1, base);
+    data32 = mpd_alloc((mpd_size_t)len32, sizeof *data32);
+    expected_len32 = mpd_export_u32(&data32, len32, base, op1, ctx);
+    if (len32 == SIZE_MAX) {
+        mpd_err_fatal("export_to_base failed");
+    }
+
+    mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+
+    mpd_free(calc);
+    mpd_free(data32);
+
+    /*
+     * base := 10**9
+     * len(data32) := mpd_sizeinbase()
+     * Test allocation failures.
+     */
+    base = 1000000000;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        data32 = NULL;
+        expected_len32 = mpd_export_u32(&data32, 0, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(expected_len32 == SIZE_MAX)
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data32);
+
+    /*
+     * base := 10**9
+     * len(data32) := 1
+     * Test allocation failures.
+     */
+    base = 1000000000;
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        ctx->status = 0;
+
+        data32 = mpd_alloc(1, sizeof *data32);
+        mpd_set_alloc_fail(ctx);
+        expected_len32 = mpd_export_u32(&data32, 1, base, op1, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(expected_len32 == SIZE_MAX)
+        mpd_free(data32);
+    }
+    for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+        mpd_minalloc(result);
+        ctx->status = 0;
+
+        mpd_set_alloc_fail(ctx);
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        mpd_set_alloc(ctx);
+
+        if (!(ctx->status&MPD_Malloc_error)) {
+            break;
+        }
+        ASSERT(mpd_isnan(result))
+    }
+    calc = _mpd_to_sci(result, 1);
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    mpd_free(calc);
+    mpd_free(data32);
+
+    for (i = 2; i <= 16; i++) {
+
+        base = (uint32_t)i;
+        len32 = mpd_sizeinbase(op1, base);
+        data32 = mpd_alloc((mpd_size_t)len32, sizeof *data32);
+        expected_len32 = mpd_export_u32(&data32, len32, base, op1, ctx);
+        if (len32 == SIZE_MAX) {
+            mpd_err_fatal("export_to_base failed");
+        }
+
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+
+        mpd_free(calc);
+        mpd_free(data32);
+    }
+
+    for (i = 0; i < 5; i++) {
+
+        base = random() % UINT32_MAX;
+        if (base < 2) base = 2;
+
+        len32 = mpd_sizeinbase(op1, base);
+        data32 = mpd_alloc((mpd_size_t)len32, sizeof *data32);
+        expected_len32 = mpd_export_u32(&data32, len32, base, op1, ctx);
+        if (len32 == SIZE_MAX) {
+            mpd_err_fatal("export_to_base failed");
+        }
+
+        mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+
+        mpd_free(calc);
+        mpd_free(data32);
+
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            ctx->status = 0;
+
+            data32 = mpd_alloc(1, sizeof *data32);
+            mpd_set_alloc_fail(ctx);
+            expected_len32 = mpd_export_u32(&data32, 1, base, op1, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(expected_len32 == SIZE_MAX)
+            mpd_free(data32);
+        }
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            mpd_minalloc(result);
+            ctx->status = 0;
+
+            mpd_set_alloc_fail(ctx);
+            mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(mpd_isnan(result))
+        }
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+        mpd_free(data32);
+
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            ctx->status = 0;
+
+            data32 = mpd_alloc(1, sizeof *data32);
+            mpd_set_alloc_fail(ctx);
+            expected_len32 = mpd_export_u32(&data32, 1, base, op1, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(expected_len32 == SIZE_MAX)
+            mpd_free(data32);
+        }
+        for (alloc_fail = 1; alloc_fail < INT_MAX; alloc_fail++) {
+            mpd_minalloc(result);
+            ctx->status = 0;
+
+            mpd_set_alloc_fail(ctx);
+            mpd_import_u32(result, data32, expected_len32, MPD_POS, base, ctx);
+            mpd_set_alloc(ctx);
+
+            if (!(ctx->status&MPD_Malloc_error)) {
+                break;
+            }
+            ASSERT(mpd_isnan(result))
+        }
+        calc = _mpd_to_sci(result, 1);
+        compare_expected(calc, expected, expstatus, token[0], ctx);
+        mpd_free(calc);
+        mpd_free(data32);
+    }
+}
+
+/*
+ * Test a function returning a uint64_t, accepting:
+ *     op, context
+ *
+ * This function is used for:
+ *   - mpd_get_uint (64 bit)
+ *   - mpd_abs_uint (64 bit)
+ *   - mpd_get_u64
+ */
+#ifndef MPD_LEGACY_COMPILER
+static void
+_u64_MpdCtx(char **token, uint64_t (*func)(const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    uint64_t calc_uint;
+    char calc[23];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    expstatus = scan_conditions(token+n);
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    calc_uint = func(tmp, ctx);
+    snprintf(calc, 23, "%" PRIu64, calc_uint);
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+#endif
+
+/*
+ * Test a function returning a uint64_t, accepting:
+ *     op, context
+ *
+ * This function is used for:
+ *   - mpd_get_uint (64 bit)
+ *   - mpd_abs_uint (64 bit)
+ *   - mpd_get_u64
+ */
+static void
+_u32_MpdCtx(char **token, uint32_t (*func)(const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    uint32_t calc_uint;
+    char calc[23];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    expstatus = scan_conditions(token+n);
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    calc_uint = func(tmp, ctx);
+    snprintf(calc, 23, "%" PRIu32, calc_uint);
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+
+/*
+ * Test a function returning an mpd_ssize_t, accepting:
+ *     op, fmt, context
+ *
+ * This function is used for:
+ *   - mpd_get_ssize
+ *   - mpd_get_i64
+ *   - mpd_get_i32
+ */
+#ifndef MPD_LEGACY_COMPILER
+static void
+_i64_MpdCtx(char **token, int64_t (*func)(const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int64_t calc_ssize;
+    char calc[23];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    expstatus = scan_conditions(token+n);
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    calc_ssize = func(tmp, ctx);
+    snprintf(calc, 23, "%" PRIi64, calc_ssize);
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+#endif
+
+/*
+ * Test a function returning an mpd_ssize_t, accepting:
+ *     op, fmt, context
+ *
+ * This function is used for:
+ *   - mpd_get_ssize
+ *   - mpd_get_i64
+ *   - mpd_get_i32
+ */
+static void
+_i32_MpdCtx(char **token, int32_t (*func)(const mpd_t *, mpd_context_t *), mpd_context_t *ctx)
+{
+    mpd_context_t maxctx;
+    int32_t calc_ssize;
+    char calc[23];
+    char *expected;
+    uint32_t expstatus;
+    int n;
+
+    mpd_readcontext(&maxctx);
+    maxctx.traps = MPD_Malloc_error;
+
+    /* conversion should be done as if there were no limits */
+    n = scan_1op_result(op, &expected, token, &maxctx);
+    _TripleTest(op, ctx, token[0]);
+
+    expstatus = scan_conditions(token+n);
+
+    mpd_init_rand(tmp);
+    mpd_copy(tmp, op, ctx);
+    ctx->status = 0;
+
+    calc_ssize = func(tmp, ctx);
+    snprintf(calc, 23, "%" PRIi32, calc_ssize);
+
+    /* compare the calculated result to the expected result */
+    compare_expected(calc, expected, expstatus, token[0], ctx);
+    check_equalmem(tmp, op, token[0]);
+}
+
+static void
+triple_cov(void)
+{
+    mpd_uint128_triple_t triple = { MPD_TRIPLE_QNAN, 2, 0, 0, 0 };
+    uint32_t status = 0;
+
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.sign = 0;
+    triple.exp = 1;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_INF;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_NORMAL;
+    triple.sign = 2;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_NORMAL;
+    triple.sign = 0;
+    triple.exp = INT64_MAX;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_NORMAL;
+    triple.sign = 0;
+    triple.exp = INT64_MIN;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_NORMAL;
+    triple.sign = 0;
+    triple.exp = MPD_SSIZE_MAX;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = MPD_TRIPLE_NORMAL;
+    triple.sign = 0;
+    triple.exp = MPD_SSIZE_MIN;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+
+    triple.tag = (enum mpd_triple_class)10;
+    triple.sign = 0;
+    status = 0;
+    mpd_from_uint128_triple(op, &triple, &status);
+    ASSERT(status == MPD_Conversion_syntax)
+    ASSERT(mpd_isqnan(op))
+}
+
+
+/* process a file */
+static void
+doit(const char *filename)
+{
+    FILE *file;
+    mpd_context_t ctx;
+    char *line;
+    char *tmpline;
+    char *token[MAXTOKEN+1];
+    uint32_t testno;
+    mpd_ssize_t l;
+
+
+    mpd_testcontext(&ctx);
+
+    ctx.traps = MPD_Malloc_error;
+
+    file_failure = 0;
+    if (strcmp(filename, "-") == 0) {
+        file = stdin;
+    }
+    else {
+        if ((file = fopen(filename, "r")) == NULL) {
+            mpd_err_fatal("could not open %s", filename);
+        }
+        if (!startswith(filename, "official") && !startswith(filename, "additional")) {
+            printf("%s ...", filename);
+            fflush(stdout);
+        }
+    }
+
+    if ((line = mpd_alloc(MAXLINE+1, sizeof *line)) == NULL) {
+        mpd_err_fatal("out of memory");
+    }
+    if ((tmpline = mpd_alloc(MAXLINE+1, sizeof *line)) == NULL) {
+        mpd_err_fatal("out of memory");
+    }
+
+
+    while (fgets(line, MAXLINE+1, file) != NULL) {
+
+        /* split a line into tokens */
+        strcpy(tmpline, line);
+
+        for (int i =0; i < MAXTOKEN+1; i++) {
+            token[i] = NULL;
+        }
+
+        if (split(token, tmpline) == 0) {
+            goto cleanup;
+        }
+
+
+        /* comments */
+        if (startswith(token[0], "--")) {
+            goto cleanup;
+        }
+        /* end comments */
+
+        /* skip bool* tests in extra.decTest */
+        if (startswith(token[0], "bool")) {
+            goto cleanup;
+        }
+        /* end skips */
+
+
+        /* directives */
+        if (startswith(token[0], "ExtendedRange")) {
+            ASSERT(token[1] != NULL);
+            if (strcmp(token[1], "1") == 0) {
+                extended = 1;
+            }
+            else if (strcmp(token[1], "0") == 0) {
+                extended = 0;
+            }
+            else {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "Precision")) {
+            ASSERT(token[1] != NULL);
+            if (strcmp(token[1], "MAX_PREC") == 0) {
+                l = MPD_MAX_PREC;
+            }
+            else {
+                l = scan_ssize(token);
+                if (errno != 0) {
+                    mpd_err_fatal("%s: %s", filename, line);
+                }
+            }
+            ctx.prec = l;
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "Rounding")) {
+            ASSERT(token[1] != NULL);
+            if (eqtoken(token[1], "Ceiling"))
+                ctx.round = MPD_ROUND_CEILING;
+            else if (eqtoken(token[1], "Up"))
+                ctx.round = MPD_ROUND_UP;
+            else if (eqtoken(token[1], "Half_up"))
+                ctx.round = MPD_ROUND_HALF_UP;
+            else if (eqtoken(token[1], "Half_even"))
+                ctx.round = MPD_ROUND_HALF_EVEN;
+            else if (eqtoken(token[1], "Half_down"))
+                ctx.round = MPD_ROUND_HALF_DOWN;
+            else if (eqtoken(token[1], "Down"))
+                ctx.round = MPD_ROUND_DOWN;
+            else if (eqtoken(token[1], "Floor"))
+                ctx.round = MPD_ROUND_FLOOR;
+            else if (eqtoken(token[1], "05up"))
+                ctx.round = MPD_ROUND_05UP;
+            else
+                mpd_err_fatal("%s: %s", filename, line);
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "MaxExponent")) {
+            ASSERT(token[1] != NULL);
+            if (strcmp(token[1], "MAX_EMAX") == 0) {
+                l = MPD_MAX_EMAX;
+            }
+            else {
+                l = scan_ssize(token);
+                if (errno != 0) {
+                    mpd_err_fatal("%s: %s", filename, line);
+                }
+            }
+            ctx.emax = l;
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "MinExponent")) {
+            ASSERT(token[1] != NULL);
+            if (strcmp(token[1], "MIN_EMIN") == 0) {
+                l = MPD_MIN_EMIN;
+            }
+            else {
+                l = scan_ssize(token);
+                if (errno != 0) {
+                    mpd_err_fatal("%s: %s", filename, line);
+                }
+            }
+            ctx.emin = l;
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "Dectest")) {
+            ASSERT(token[1] != NULL);
+            if (token[1] == NULL) {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            doit(token[1]);
+            goto cleanup;
+        }
+        /* end directives */
+
+        /* optional directives */
+        if (startswith(token[0], "Version")) {
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "Extended")) {
+            goto cleanup;
+        }
+
+        if (startswith(token[0], "Clamp")) {
+            ASSERT(token[1] != NULL);
+            l = scan_ssize(token);
+            if (errno != 0) {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            if (!mpd_qsetclamp(&ctx, (int)l)) {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            goto cleanup;
+        }
+        if (startswith(token[0], "Locale")) {
+            ASSERT(token[1] != NULL);
+            if (token[1] == NULL) {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            if (extended) {
+                printf("locale: %s\n", token[1]);
+                fflush(stdout);
+            }
+            if (setlocale(LC_NUMERIC, token[1]) == NULL) {
+                mpd_err_fatal("%s: %s", filename, line);
+            }
+            goto cleanup;
+        }
+
+        mpd_assert_context_ok(&ctx);
+        /* end optional directives */
+
+
+        /*
+         * Actual tests start here:
+         *   - token[0] is the id
+         *   - token[1] is the operation type
+         *   - testno can be used for setting a watchpoint in the debugger
+         */
+        testno = (uint32_t)get_testno(token[0]);
+        (void)testno;
+
+        /* The id is in the skip list */
+        if (check_skip(token[0])) {
+            goto cleanup;
+        }
+#ifdef MPD_CONFIG_64
+        /* Skip 32-bit specific coverage tests. */
+        if (startswith(token[0], "cov32")) {
+            goto cleanup;
+        }
+#else
+        /* Skip 64-bit specific coverage tests. */
+        if (startswith(token[0], "cov64")) {
+            goto cleanup;
+        }
+#endif
+        /* Translate operation type for powmod */
+        if (startswith(token[0], "pwmx")) {
+            free(token[1]);
+            token[1] = malloc(sizeof("powmod"));
+            strcpy(token[1], "powmod");
+        }
+        /* end skips */
+
+
+        /* Unary functions with char * result */
+        if (eqtoken(token[1], "tosci") || eqtoken(token[1], "apply")) {
+            _cp_MpdCtx(token, mpd_to_sci, &ctx);
+            sci_eng_size(token, mpd_to_sci_size, &ctx);
+        }
+        else if (eqtoken(token[1], "toeng")) {
+            _cp_MpdCtx(token, mpd_to_eng, &ctx);
+            sci_eng_size(token, mpd_to_eng_size, &ctx);
+        }
+        else if (eqtoken(token[1], "format")) {
+            _cp_MpdFmtCtx(token, mpd_format, &ctx);
+        }
+        /* Unary function with const char * result */
+        else if (eqtoken(token[1], "class")) {
+            _ccp_MpdCtx(token, mpd_class, &ctx);
+        }
+
+        /* Unary functions with mpd_t * result */
+        else if (eqtoken(token[1], "abs")) {
+            _Res_Op_Ctx(token, mpd_abs, &ctx);
+        }
+        else if (eqtoken(token[1], "copy")) {
+            _Res_Op_Ctx(token, mpd_copy, &ctx);
+        }
+        else if (eqtoken(token[1], "copyabs")) {
+            _Res_Op_Ctx(token, mpd_copy_abs, &ctx);
+        }
+        else if (eqtoken(token[1], "copynegate")) {
+            _Res_Op_Ctx(token, mpd_copy_negate, &ctx);
+        }
+        else if (eqtoken(token[1], "exp")) {
+            if (extended) {
+                if (testno != 126) {
+                    ctx.allcr = 0;
+                    /* exp: err < 1ulp, but not correctly rounded */
+                    _Res_Op_Ctx(token, mpd_exp, &ctx);
+                    ctx.allcr = 1;
+                }
+            }
+            _Res_Op_Ctx(token, mpd_exp, &ctx);
+        }
+        else if (eqtoken(token[1], "invert")) {
+            _Res_Op_Ctx(token, mpd_invert, &ctx);
+        }
+        else if (eqtoken(token[1], "invroot")) {
+            _Res_Op_Ctx(token, mpd_invroot, &ctx);
+        }
+        else if (eqtoken(token[1], "ln")) {
+            if (extended) {
+                ctx.allcr = 0;
+                _Res_Op_Ctx(token, mpd_ln, &ctx);
+                ctx.allcr = 1;
+            }
+            _Res_Op_Ctx(token, mpd_ln, &ctx);
+        }
+        else if (eqtoken(token[1], "log10")) {
+            if (extended) {
+                ctx.allcr = 0;
+                _Res_Op_Ctx(token, mpd_log10, &ctx);
+                ctx.allcr = 1;
+            }
+            _Res_Op_Ctx(token, mpd_log10, &ctx);
+        }
+        else if (eqtoken(token[1], "logb")) {
+            _Res_Op_Ctx(token, mpd_logb, &ctx);
+        }
+        else if (eqtoken(token[1], "minus")) {
+            _Res_Op_Ctx(token, mpd_minus, &ctx);
+        }
+        else if (eqtoken(token[1], "nextminus")) {
+            _Res_Op_Ctx(token, mpd_next_minus, &ctx);
+        }
+        else if (eqtoken(token[1], "nextplus")) {
+            _Res_Op_Ctx(token, mpd_next_plus, &ctx);
+        }
+        else if (eqtoken(token[1], "plus")) {
+            _Res_Op_Ctx(token, mpd_plus, &ctx);
+        }
+        else if (eqtoken(token[1], "reduce")) {
+            _Res_Op_Ctx(token, mpd_reduce, &ctx);
+        }
+        else if (eqtoken(token[1], "squareroot")) {
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(16000000);
+            #endif
+            _Res_Op_Ctx(token, mpd_sqrt, &ctx);
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token[1], "quantize_squareroot")) {
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(16000000);
+            #endif
+            _Res_Op_CtxWithQuantize(token, mpd_sqrt, &ctx);
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token[1], "tointegral")) {
+            _Res_Op_Ctx(token, mpd_round_to_int, &ctx);
+        }
+        else if (eqtoken(token[1], "tointegralx")) {
+            _Res_Op_Ctx(token, mpd_round_to_intx, &ctx);
+        }
+        else if (eqtoken(token[1], "floor")) {
+            _Res_Op_Ctx(token, mpd_floor, &ctx);
+        }
+        else if (eqtoken(token[1], "ceil")) {
+            _Res_Op_Ctx(token, mpd_ceil, &ctx);
+        }
+        else if (eqtoken(token[1], "trunc")) {
+            _Res_Op_Ctx(token, mpd_trunc, &ctx);
+        }
+
+
+        /* Binary function returning an int */
+        else if (eqtoken(token[1], "samequantum")) {
+            _Int_Binop(token, mpd_same_quantum, &ctx);
+        }
+        /* Binary function returning an int, equal operands */
+        else if (eqtoken(token[1], "samequantum_eq")) {
+            _Int_EqualBinop(token, mpd_same_quantum, &ctx);
+        }
+
+        /* Binary functions with mpd_t * result */
+        else if (eqtoken(token[1], "add")) {
+            _Res_Binop_Ctx(token, mpd_add, &ctx);
+        }
+        else if (eqtoken(token[1], "and")) {
+            _Res_Binop_Ctx(token, mpd_and, &ctx);
+        }
+        else if (eqtoken(token[1], "copysign")) {
+            _Res_Binop_Ctx(token, mpd_copy_sign, &ctx);
+        }
+        else if (eqtoken(token[1], "divide")) {
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(16000000);
+            #endif
+            _Res_Binop_Ctx(token, mpd_div, &ctx);
+            #ifdef MPD_CONFIG_32
+                if (ctx.prec == MPD_MAX_PREC) mpd_set_alloc_limit(SIZE_MAX);
+            #endif
+        }
+        else if (eqtoken(token[1], "divideint")) {
+            _Res_Binop_Ctx(token, mpd_divint, &ctx);
+        }
+        else if (eqtoken(token[1], "max")) {
+            _Res_Binop_Ctx(token, mpd_max, &ctx);
+        }
+        else if (eqtoken(token[1], "maxmag") || eqtoken(token[1], "max_mag")) {
+            _Res_Binop_Ctx(token, mpd_max_mag, &ctx);
+        }
+        else if (eqtoken(token[1], "min")) {
+            _Res_Binop_Ctx(token, mpd_min, &ctx);
+        }
+        else if (eqtoken(token[1], "minmag") || eqtoken(token[1], "min_mag")) {
+            _Res_Binop_Ctx(token, mpd_min_mag, &ctx);
+        }
+        else if (eqtoken(token[1], "multiply")) {
+            _Res_Binop_Ctx(token, mpd_mul, &ctx);
+        }
+        else if (eqtoken(token[1], "nexttoward")) {
+            _Res_Binop_Ctx(token, mpd_next_toward, &ctx);
+        }
+        else if (eqtoken(token[1], "or")) {
+            _Res_Binop_Ctx(token, mpd_or, &ctx);
+        }
+        else if (eqtoken(token[1], "power")) {
+            if (extended) {
+                ctx.allcr = 0;
+                _Res_Binop_Ctx(token, mpd_pow, &ctx);
+                ctx.allcr = 1;
+            }
+            _Res_Binop_Ctx(token, mpd_pow, &ctx);
+        }
+        else if (eqtoken(token[1], "quantize")) {
+            _Res_Binop_Ctx(token, mpd_quantize, &ctx);
+        }
+        else if (eqtoken(token[1], "resc")) {
+            _Res_Op_Lsize_Ctx(SKIP_NONINT, token, mpd_rescale, &ctx);
+        }
+        else if (eqtoken(token[1], "remainder")) {
+            _Res_Binop_Ctx(token, mpd_rem, &ctx);
+        }
+        else if (eqtoken(token[1], "remaindernear")) {
+            _Res_Binop_Ctx(token, mpd_rem_near, &ctx);
+        }
+        else if (eqtoken(token[1], "rotate")) {
+            _Res_Binop_Ctx(token, mpd_rotate, &ctx);
+        }
+        else if (eqtoken(token[1], "scaleb")) {
+            _Res_Binop_Ctx(token, mpd_scaleb, &ctx);
+        }
+        else if (eqtoken(token[1], "shift")) {
+            _Res_Binop_Ctx(token, mpd_shift, &ctx);
+            if (extended) {
+                _Res_Op_Lsize_Ctx(SKIP_NONINT, token, mpd_shiftn, &ctx);
+            }
+        }
+        else if (eqtoken(token[1], "subtract")) {
+            _Res_Binop_Ctx(token, mpd_sub, &ctx);
+        }
+        else if (eqtoken(token[1], "xor")) {
+            _Res_Binop_Ctx(token, mpd_xor, &ctx);
+        }
+
+        /* Binary functions with mpd_t result, equal operands */
+        else if (eqtoken(token[1], "add_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_add, &ctx);
+        }
+        else if (eqtoken(token[1], "and_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_and, &ctx);
+        }
+        else if (eqtoken(token[1], "copysign_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_copy_sign, &ctx);
+        }
+        else if (eqtoken(token[1], "divide_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_div, &ctx);
+        }
+        else if (eqtoken(token[1], "divideint_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_divint, &ctx);
+        }
+        else if (eqtoken(token[1], "max_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_max, &ctx);
+        }
+        else if (eqtoken(token[1], "maxmag_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_max_mag, &ctx);
+        }
+        else if (eqtoken(token[1], "min_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_min, &ctx);
+        }
+        else if (eqtoken(token[1], "minmag_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_min_mag, &ctx);
+        }
+        else if (eqtoken(token[1], "multiply_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_mul, &ctx);
+        }
+        else if (eqtoken(token[1], "nexttoward_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_next_toward, &ctx);
+        }
+        else if (eqtoken(token[1], "or_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_or, &ctx);
+        }
+        else if (eqtoken(token[1], "power_eq")) {
+            if (extended) {
+                ctx.allcr = 0;
+                _Res_EqualBinop_Ctx(token, mpd_pow, &ctx);
+                ctx.allcr = 1;
+            }
+            _Res_EqualBinop_Ctx(token, mpd_pow, &ctx);
+        }
+        else if (eqtoken(token[1], "quantize_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_quantize, &ctx);
+        }
+        else if (eqtoken(token[1], "remainder_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_rem, &ctx);
+        }
+        else if (eqtoken(token[1], "remaindernear_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_rem_near, &ctx);
+        }
+        else if (eqtoken(token[1], "rotate_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_rotate, &ctx);
+        }
+        else if (eqtoken(token[1], "scaleb_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_scaleb, &ctx);
+        }
+        else if (eqtoken(token[1], "shift_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_shift, &ctx);
+        }
+        else if (eqtoken(token[1], "subtract_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_sub, &ctx);
+        }
+        else if (eqtoken(token[1], "xor_eq")) {
+            _Res_EqualBinop_Ctx(token, mpd_xor, &ctx);
+        }
+
+        /* Binary function with binary result */
+        else if (eqtoken(token[1], "divmod")) {
+            _Binres_Binop_Ctx(token, mpd_divmod, &ctx);
+        }
+
+        /* Binary function with binary result, equal operands */
+        else if (eqtoken(token[1], "divmod_eq")) {
+            _Binres_EqualBinop_Ctx(token, mpd_divmod, &ctx);
+        }
+
+
+        /* Ternary functions with mpd_t result */
+        else if (eqtoken(token[1], "fma")) {
+            _Res_Ternop_Ctx(token, mpd_fma, &ctx);
+        }
+        else if (eqtoken(token[1], "powmod")) {
+            _Res_Ternop_Ctx(token, mpd_powmod, &ctx);
+        }
+
+        /* Ternary functions with mpd_t result, eq_eq_op */
+        else if (eqtoken(token[1], "fma_eq_eq_op")) {
+            _Res_EqEqOp_Ctx(token, mpd_fma, &ctx);
+        }
+        else if (eqtoken(token[1], "powmod_eq_eq_op")) {
+            _Res_EqEqOp_Ctx(token, mpd_powmod, &ctx);
+        }
+
+        /* Ternary functions with mpd_t result, eq_op_eq */
+        else if (eqtoken(token[1], "fma_eq_op_eq")) {
+            _Res_EqOpEq_Ctx(token, mpd_fma, &ctx);
+        }
+        else if (eqtoken(token[1], "powmod_eq_op_eq")) {
+            _Res_EqOpEq_Ctx(token, mpd_powmod, &ctx);
+        }
+
+        /* Ternary functions with mpd_t result, op_eq_eq */
+        else if (eqtoken(token[1], "fma_op_eq_eq")) {
+            _Res_OpEqEq_Ctx(token, mpd_fma, &ctx);
+        }
+        else if (eqtoken(token[1], "powmod_op_eq_eq")) {
+            _Res_OpEqEq_Ctx(token, mpd_powmod, &ctx);
+        }
+
+        /* Ternary functions with mpd_t result, eq_eq_eq */
+        else if (eqtoken(token[1], "fma_eq_eq_eq")) {
+            _Res_EqEqEq_Ctx(token, mpd_fma, &ctx);
+        }
+        else if (eqtoken(token[1], "powmod_eq_eq_eq")) {
+            _Res_EqEqEq_Ctx(token, mpd_powmod, &ctx);
+        }
+
+        /* Special cases for the comparison functions */
+        else if (eqtoken(token[1], "compare")) {
+            _Int_Res_Binop_Ctx(token, mpd_compare, &ctx);
+            _Int_Binop_Ctx(SKIP_NAN, token, mpd_cmp, &ctx);
+        }
+        else if (eqtoken(token[1], "comparesig")) {
+            _Int_Res_Binop_Ctx(token, mpd_compare_signal, &ctx);
+        }
+        else if (eqtoken(token[1], "comparetotal")) {
+            _Int_Res_Binop(token, mpd_compare_total, &ctx);
+            _Int_Binop(token, mpd_cmp_total, &ctx);
+        }
+        else if (eqtoken(token[1], "comparetotmag")) {
+            _Int_Res_Binop(token, mpd_compare_total_mag, &ctx);
+            _Int_Binop(token, mpd_cmp_total_mag, &ctx);
+        }
+
+        /* Special cases for the comparison functions, equal operands */
+        else if (eqtoken(token[1], "compare_eq")) {
+            _Int_Res_EqualBinop_Ctx(token, mpd_compare, &ctx);
+            _Int_EqualBinop_Ctx(SKIP_NAN, token, mpd_cmp, &ctx);
+        }
+        else if (eqtoken(token[1], "comparesig_eq")) {
+            _Int_Res_EqualBinop_Ctx(token, mpd_compare_signal, &ctx);
+        }
+        else if (eqtoken(token[1], "comparetotal_eq")) {
+            _Int_Res_EqualBinop(token, mpd_compare_total, &ctx);
+            _Int_EqualBinop(token, mpd_cmp_total, &ctx);
+        }
+        else if (eqtoken(token[1], "comparetotmag_eq")) {
+            _Int_Res_EqualBinop(token, mpd_compare_total_mag, &ctx);
+            _Int_EqualBinop(token, mpd_cmp_total_mag, &ctx);
+        }
+
+        /* Special cases for the shift functions */
+        else if (eqtoken(token[1], "shiftleft")) {
+            _Res_Op_Lsize_Ctx(SKIP_NONINT, token, _mpd_shiftl, &ctx);
+        }
+        else if (eqtoken(token[1], "shiftright")) {
+            _Res_Op_Lsize_Ctx(SKIP_NONINT, token, _mpd_shiftr, &ctx);
+        }
+
+        /* Special case for mpd_qln10() */
+        else if (eqtoken(token[1], "ln10")) {
+            _test_mpd_qln10(SKIP_NONINT, token, &ctx);
+        }
+
+        /* Special case for the base conversion functions */
+        else if (eqtoken(token[1], "baseconv")) {
+            _Baseconv(token, &ctx);
+        }
+
+        /* Special cases for the get_int functions */
+#ifdef MPD_CONFIG_64
+        else if (eqtoken(token[1], "get_uint64")) {
+            _u64_MpdCtx(token, mpd_get_uint, &ctx);
+        }
+        else if (eqtoken(token[1], "get_uint64_abs")) {
+            _u64_MpdCtx(token, mpd_abs_uint, &ctx);
+        }
+        else if (eqtoken(token[1], "get_ssize64")) {
+            _i64_MpdCtx(token, mpd_get_ssize, &ctx);
+        }
+#else
+        else if (eqtoken(token[1], "get_uint32")) {
+            _u32_MpdCtx(token, mpd_get_uint, &ctx);
+        }
+        else if (eqtoken(token[1], "get_uint32_abs")) {
+            _u32_MpdCtx(token, mpd_abs_uint, &ctx);
+        }
+        else if (eqtoken(token[1], "get_ssize32")) {
+            _i32_MpdCtx(token, mpd_get_ssize, &ctx);
+        }
+
+#endif
+
+#ifndef MPD_LEGACY_COMPILER
+        else if (eqtoken(token[1], "get_u64")) {
+            _u64_MpdCtx(token, mpd_get_u64, &ctx);
+        }
+        else if (eqtoken(token[1], "get_i64")) {
+            _i64_MpdCtx(token, mpd_get_i64, &ctx);
+        }
+#endif
+
+        else if (eqtoken(token[1], "get_u32")) {
+            _u32_MpdCtx(token, mpd_get_u32, &ctx);
+        }
+        else if (eqtoken(token[1], "get_i32")) {
+            _i32_MpdCtx(token, mpd_get_i32, &ctx);
+        }
+
+        else if (startswith(token[1], "get_")) {
+            /* empty */
+        }
+
+        else if (eqtoken(token[1], "rescale")) {
+            /* empty */
+        }
+
+        /* unknown operation */
+        else {
+            mpd_err_fatal("%s: unknown operation: %s", filename, line);
+        }
+        /* end tests */
+
+        cleanup:
+        freetoken(token);
+    }
+
+    mpd_free(line);
+    mpd_free(tmpline);
+    if (file != stdin) {
+        fclose(file);
+    }
+
+    if (!startswith(filename, "official") &&
+        !startswith(filename, "additional") &&
+        !file_failure) {
+        printf(" PASS\n");
+    }
+    else {
+        printf("\n");
+    }
+    fflush(stdout);
+}
+
+static void
+traphandler(mpd_context_t *ctx)
+{
+    if (ctx->newtrap & MPD_Malloc_error) {
+        fprintf(stderr, "\n\n\
+runtest: out of memory:\n\
+    - bignum tests require 200MB heap and 300KB stack.\n\
+    - normal tests require 10MB heap and 50KB stack.\n\n");
+    }
+    else {
+        fprintf(stderr, "\n\nruntest: unexpected error: relevant traps: %u\n\n",
+                        ctx->newtrap);
+    }
+
+    exit(1);
+}
+
+static void
+usage(void)
+{
+    fputs("runtest: usage: runtest testfile [--custom] [--alloc]\n", stderr);
+    exit(EXIT_FAILURE);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    const char *filename = NULL;
+    bool custom_alloc = false;
+    bool check_alloc = false;
+
+    for (int i = 1; i < argc; i++) {
+        if (!filename && (strcmp(argv[i], "-") == 0 || !startswith(argv[i], "--"))) {
+            filename = argv[i];
+        }
+        else if (!custom_alloc && strcmp(argv[i], "--custom") == 0) {
+            custom_alloc = true;
+        }
+        else if (!check_alloc && strcmp(argv[i], "--alloc") == 0) {
+            check_alloc = true;
+        }
+        else {
+            usage();
+        }
+    }
+    if (filename == NULL) {
+        usage();
+    }
+
+    /* Test version */
+    if (strcmp(mpd_version(), "4.0.1") != (0)) {
+        fputs("runtest: error: mpd_version() != 4.0.1\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    if (strcmp(MPD_VERSION, "4.0.1") != (0)) {
+        fputs("runtest: error: MPD_VERSION != 4.0.1\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+
+#ifdef _MSC_VER
+  #pragma warning(push)
+  #pragma warning(disable : 4127)
+#endif
+    if (MPD_MAJOR_VERSION != (4)) {
+        fputs("runtest: error: MPD_MAJOR_VERSION != 4\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    if (MPD_MINOR_VERSION != (0)) {
+        fputs("runtest: error: MPD_MINOR_VERSION != 0\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    if (MPD_MICRO_VERSION != (1)) {
+        fputs("runtest: error: MPD_MICRO_VERSION != 1\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    if (MPD_VERSION_HEX != (0x4000100)) {
+        fputs("runtest: error: MPD_VERSION_HEX != 0x4000100\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+#ifdef _MSC_VER
+  #pragma warning(pop)
+#endif
+
+    extended = strcmp(filename, "-") != 0;
+
+    /* Initialize random number generator */
+    srandom((unsigned int)time(NULL));
+
+    /* Initialize custom allocation functions */
+    mpd_init_alloc(custom_alloc, check_alloc);
+
+    /* Initialize MPD_MINALLOC (optional, default is 2) */
+    MPD_MINALLOC = 2;
+
+    /* Initialize trap handler */
+    mpd_traphandler = traphandler;
+
+    op = mpd_qnew();
+    op1 = mpd_qnew();
+    op2 = mpd_qnew();
+    op3 = mpd_qnew();
+    tmp = mpd_qnew();
+    tmp1 = mpd_qnew();
+    tmp2 = mpd_qnew();
+    tmp3 = mpd_qnew();
+    result = mpd_qnew();
+    result1 = mpd_qnew();
+    result2 = mpd_qnew();
+
+    triple_cov();
+    doit(filename);
+
+    mpd_del(op);
+    mpd_del(op1);
+    mpd_del(op2);
+    mpd_del(op3);
+    mpd_del(tmp);
+    mpd_del(tmp1);
+    mpd_del(tmp2);
+    mpd_del(tmp3);
+    mpd_del(result);
+    mpd_del(result1);
+    mpd_del(result2);
+
+
+    return global_failure;
+}
diff --git a/tests/test.c b/tests/test.c
new file mode 100644 (file)
index 0000000..e2d23bc
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "mpdecimal.h"
+#include "test.h"
+
+
+/******************************************************************************/
+/*               Primary allocation functions (normal or offset)              */
+/******************************************************************************/
+
+static const size_t OFFSET = 16;
+
+#ifdef MPD_CONFIG_64
+static const size_t alloc_limit = 0x4000000000000ULL;
+#else
+static size_t alloc_limit = SIZE_MAX;
+#endif
+
+/* malloc with upper limits */
+static void *
+malloc_ceil(size_t size)
+{
+    if (size > alloc_limit) {
+        return NULL;
+    }
+
+    return malloc(size);
+}
+
+static void *
+calloc_ceil(size_t nmemb, size_t size)
+{
+    if (nmemb > alloc_limit / size) {
+        return NULL;
+    }
+
+    return calloc(nmemb, size);
+}
+
+static void *
+realloc_ceil(void *ptr, size_t size)
+{
+    if (size > alloc_limit) {
+        return NULL;
+    }
+
+    return realloc(ptr, size);
+}
+
+static void
+free_ceil(void *ptr)
+{
+    free(ptr);
+}
+
+/* custom malloc with an offset and upper limits */
+static void *
+malloc_offset(size_t size)
+{
+    if (size == 0 || size > SIZE_MAX - OFFSET) {
+        return NULL;
+    }
+
+    char *ptr = malloc_ceil(OFFSET + size);
+
+    return ptr ? ptr + OFFSET : NULL;
+}
+
+static void *
+calloc_offset(size_t nmemb, size_t size)
+{
+    if (nmemb == 0 || size == 0 || size > SIZE_MAX - OFFSET) {
+        return NULL;
+    }
+
+    char *ptr = calloc_ceil(nmemb, OFFSET + size);
+
+    return ptr ? ptr + OFFSET : NULL;
+}
+
+static void *
+realloc_offset(void *ptr, size_t size)
+{
+    if (size == 0 || size > SIZE_MAX - OFFSET) {
+        return NULL;
+    }
+
+    char *c = (char *)ptr - OFFSET;
+    char *p = realloc_ceil(c, OFFSET + size);
+
+    return p ? p + OFFSET : NULL;
+}
+
+static void
+free_offset(void *ptr)
+{
+    free((char *)ptr - OFFSET);
+}
+
+/* active set of primary allocation functions */
+static void *(* test_mallocfunc)(size_t size) = malloc_ceil;
+static void *(* test_callocfunc)(size_t nmemb, size_t size) = calloc_ceil;
+static void *(* test_reallocfunc)(void *ptr, size_t size) = realloc_ceil;
+static void (* test_freefunc)(void *ptr) = free_ceil;
+
+
+/******************************************************************************/
+/*            Secondary allocation functions (count or failure mode)          */
+/******************************************************************************/
+
+static bool enable_check_alloc = false;
+int alloc_count;
+int alloc_fail;
+int alloc_idx;
+
+static void *
+malloc_count(size_t size)
+{
+    ++alloc_count;
+    return test_mallocfunc(size);
+}
+
+static void *
+calloc_count(size_t nmemb, size_t size)
+{
+    ++alloc_count;
+    return test_callocfunc(nmemb, size);
+}
+
+static void *
+realloc_count(void *ptr, size_t size)
+{
+    ++alloc_count;
+    return test_reallocfunc(ptr, size);
+}
+
+static void *
+malloc_fail(size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return NULL;
+    }
+
+    return test_mallocfunc(size);
+}
+
+static void *
+calloc_fail(size_t nmemb, size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return NULL;
+    }
+
+    return test_callocfunc(nmemb, size);
+}
+
+static void *
+realloc_fail(void *ptr, size_t size)
+{
+    if (++alloc_idx >= alloc_fail) {
+        return NULL;
+    }
+
+    return test_reallocfunc(ptr, size);
+}
+
+
+/******************************************************************************/
+/*                                 Public API                                 */
+/******************************************************************************/
+
+/* choose primary allocation functions at program start */
+void
+mpd_init_alloc(bool custom_alloc, bool check_alloc)
+{
+    static bool initialized = false;
+
+    if (initialized) {
+        fputs("mpd_init_alloc: error: cannot initialize twice\n", stderr);
+        exit(EXIT_FAILURE);
+    }
+    initialized = true;
+
+    enable_check_alloc = check_alloc;
+
+    if (custom_alloc) {
+        test_mallocfunc = malloc_offset;
+        test_callocfunc = calloc_offset;
+        test_reallocfunc = realloc_offset;
+        test_freefunc = free_offset;
+    }
+
+    mpd_mallocfunc = test_mallocfunc;
+    mpd_callocfunc = test_callocfunc;
+    mpd_reallocfunc = test_reallocfunc;
+    mpd_free = test_freefunc;
+}
+
+#ifdef MPD_CONFIG_32
+void
+mpd_set_alloc_limit(size_t size)
+{
+    alloc_limit = size;
+}
+#endif
+
+void
+mpd_set_alloc(mpd_context_t *ctx)
+{
+    mpd_mallocfunc = test_mallocfunc;
+    mpd_callocfunc = test_callocfunc;
+    mpd_reallocfunc = test_reallocfunc;
+    mpd_free = test_freefunc;
+
+    ctx->traps = MPD_Malloc_error;
+}
+
+void
+mpd_set_alloc_count(mpd_context_t *ctx)
+{
+    mpd_mallocfunc = malloc_count;
+    mpd_callocfunc = calloc_count;
+    mpd_reallocfunc = realloc_count;
+    mpd_free = test_freefunc;
+
+    ctx->traps = MPD_Malloc_error;
+    alloc_count = 0;
+}
+
+void
+mpd_set_alloc_fail(mpd_context_t *ctx)
+{
+    if (enable_check_alloc) {
+        mpd_mallocfunc = malloc_fail;
+        mpd_callocfunc = calloc_fail;
+        mpd_reallocfunc = realloc_fail;
+        mpd_free = test_freefunc;
+
+        ctx->traps = 0;
+        alloc_idx = 0;
+    }
+}
diff --git a/tests/test.h b/tests/test.h
new file mode 100644 (file)
index 0000000..a2bc1f9
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef TESTS_H_
+#define TESTS_H_
+
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "mpdecimal.h"
+
+
+extern int alloc_count;
+extern int alloc_fail;
+extern int alloc_idx;
+
+void mpd_init_alloc(bool custom_alloc, bool check_alloc);
+
+#ifdef MPD_CONFIG_32
+void mpd_set_alloc_limit(size_t size);
+#endif
+
+void mpd_set_alloc(mpd_context_t *ctx);
+void mpd_set_alloc_count(mpd_context_t *ctx);
+void mpd_set_alloc_fail(mpd_context_t *ctx);
+
+
+#endif /* TESTS_H_ */
diff --git a/tests/testdata_dist/baseconv.decTest b/tests/testdata_dist/baseconv.decTest
new file mode 100644 (file)
index 0000000..7d0d18c
--- /dev/null
@@ -0,0 +1,53 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+Precision: 425000000
+Maxexponent: 425000000
+Minexponent: -425000000
+
+bconv0  baseconv  0  ->  0
+bconv1  baseconv  1  ->  1
+bconv2  baseconv  2  ->  2
+bconv3  baseconv  3  ->  3
+bconv4  baseconv  4  ->  4
+bconv5  baseconv  5  ->  5
+bconv6  baseconv  6  ->  6
+bconv7  baseconv  7  ->  7
+bconv8  baseconv  8  ->  8
+bconv9  baseconv  9  ->  9
+bconv10  baseconv  10  ->  10
+
+bconv0  baseconv  1  ->  1
+bconv1  baseconv  10  ->  10
+bconv2  baseconv  100  ->  100
+bconv3  baseconv  1000  ->  1000
+bconv4  baseconv  10000  ->  10000
+bconv5  baseconv  100000  ->  100000
+bconv6  baseconv  1000000  ->  1000000
+bconv7  baseconv  10000000  ->  10000000
+bconv8  baseconv  100000000  ->  100000000
+bconv9  baseconv  1000000000  ->  1000000000
+bconv10  baseconv  10000000000  ->  10000000000
+
+bconv90  baseconv  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv91  baseconv  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv92  baseconv  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv93  baseconv  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv94  baseconv  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv95  baseconv  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv96  baseconv  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv97  baseconv  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv98  baseconv  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+bconv99  baseconv  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  ->  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+bconv0  baseconv  1  ->  1
+bconv1  baseconv  2  ->  2
+bconv2  baseconv  4  ->  4
+bconv3  baseconv  8  ->  8
+bconv4  baseconv  16  ->  16
+bconv5  baseconv  32  ->  32
+bconv6  baseconv  64  ->  64
+bconv7  baseconv  128  ->  128
+bconv8  baseconv  256  ->  256
+bconv9  baseconv  512  ->  512
diff --git a/tests/testdata_dist/binop_eq.decTest b/tests/testdata_dist/binop_eq.decTest
new file mode 100644 (file)
index 0000000..b2c4c93
--- /dev/null
@@ -0,0 +1,120 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+minExponent: -425000000
+maxExponent: 425000000
+
+precision: 27
+add_eq1  add_eq  +926069853  ->  1852139706  
+precision: 104
+add_eq2  add_eq  -Infinity  ->  -Infinity  
+
+precision: 100
+compare_eq1  compare_eq  +5916794372888113130055019620156129327439018422817  ->  0  
+precision: 199
+compare_eq2  compare_eq  -6385564075788557361489053622233363254132221134310928544266570524036736301587945295E-378058779  ->  0  
+
+precision: 255
+comparesig_eq1  comparesig_eq  37997421400698793468295234499258938258888893937595659697398091686412465480  ->  0  
+precision: 103
+comparesig_eq2  comparesig_eq  +Inf  ->  0  
+
+precision: 112
+comparetotal_eq0  comparetotal_eq  +5246899448694934.76746583E7239466  ->  0  
+precision: 79
+comparetotal_eq1  comparetotal_eq  -461866196766787289755310460813711109452546567.23424500338922  ->  0  
+precision: 40
+comparetotal_eq2  comparetotal_eq  40559967.8048E-198120324  ->  0  
+
+precision: 111
+comparetotmag_eq1  comparetotmag_eq  -.9680035  ->  0  
+precision: 280
+comparetotmag_eq2  comparetotmag_eq  -2978664146700401661050144467205502091778945285794943.5248293726490459289168834361170650892056840210562147124621253082638290597E+7322343  ->  0  
+
+precision: 241
+copysign_eq0  copysign_eq  +912849910816424783962776495925326137570904169012007696206741038734E-113244462  ->  9.12849910816424783962776495925326137570904169012007696206741038734E-113244397  
+precision: 120
+copysign_eq2  copysign_eq  -Inf  ->  -Infinity  
+
+precision: 101
+divide_eq0  divide_eq  -Infinity  ->  NaN  Invalid_operation 
+precision: 245
+divide_eq1  divide_eq  Inf  ->  NaN  Invalid_operation 
+precision: 110
+divide_eq2  divide_eq  -.1476808094725729904095250393485863450763071588976156608004151488204454583149499119086353485308969531e-190874140  ->  1  
+
+precision: 146
+divideint_eq1  divideint_eq  -1256574716053067901997983102369440625154437304394877782  ->  1  
+precision: 48
+divideint_eq2  divideint_eq  -.548918  ->  1  
+
+precision: 107
+max_eq1  max_eq  -958729752001  ->  -958729752001  
+
+precision: 125
+maxmag_eq1  maxmag_eq  .867049357676692568304662108070292464722909902766555830155  ->  0.867049357676692568304662108070292464722909902766555830155  
+precision: 135
+maxmag_eq2  maxmag_eq  NaN6070908532835254314072874  ->  NaN6070908532835254314072874  
+
+precision: 16
+min_eq0  min_eq  7097E334037297  ->  7.097E+334037300  
+precision: 41
+min_eq1  min_eq  .6441974486  ->  0.6441974486  
+
+precision: 90
+minmag_eq0  minmag_eq  +619334386544815.606216e+357209456  ->  6.19334386544815606216E+357209470  
+precision: 161
+minmag_eq1  minmag_eq  -25.  ->  -25  
+
+precision: 267
+multiply_eq0  multiply_eq  +16419229917556917718044035  ->  269591111085596147004142137735461565267426199081225  
+precision: 21
+multiply_eq1  multiply_eq  +5.27711105277744423e-348158313  ->  0E-425000020  Underflow Rounded Subnormal Clamped Inexact 
+
+precision: 34
+nexttoward_eq0  nexttoward_eq  -78134744367691536194708531  ->  -78134744367691536194708531  
+precision: 200
+nexttoward_eq2  nexttoward_eq  -.8537008685979470753640770866065301180915124288689  ->  -0.8537008685979470753640770866065301180915124288689  
+
+precision: 36
+power_eq0  power_eq  -362429158631567479322670636751195  ->  -0E-425000035  Underflow Rounded Subnormal Clamped Inexact 
+precision: 182
+power_eq1  power_eq  Inf  ->  Infinity  
+precision: 24
+power_eq2  power_eq  336328251898.680440  ->  Infinity  Overflow Rounded Inexact 
+
+precision: 91
+quantize_eq0  quantize_eq  290548.8E-1423999  ->  2.905488E-1423994  
+precision: 44
+quantize_eq1  quantize_eq  +448658.9  ->  448658.9  
+precision: 172
+quantize_eq2  quantize_eq  -817350403193055  ->  -817350403193055  
+
+precision: 110
+remainder_eq0  remainder_eq  -Inf  ->  NaN  Invalid_operation 
+precision: 57
+remainder_eq2  remainder_eq  -975534984681769723475932500908516678693056e-17495830  ->  -0E-17495830  
+
+precision: 222
+remaindernear_eq0  remaindernear_eq  +606302560422303238839300023784949387340467879977016801015961108154411606509462813597451715049853691310868412854381471077355940382201615032  ->  0  
+precision: 70
+remaindernear_eq1  remaindernear_eq  -NaN  ->  -NaN  
+precision: 125
+remaindernear_eq2  remaindernear_eq  -2353922789310.187  ->  -0.000  
+
+precision: 33
+shift_eq0  shift_eq  +64  ->  NaN  Invalid_operation 
+precision: 64
+shift_eq2  shift_eq  .1064629116843833794591007955659963903297906158674535586  ->  NaN  Invalid_operation 
+
+precision: 259
+subtract_eq0  subtract_eq  -.784626254375705635359421742211628372073214972658015192255951E-322501478  ->  0E-322501538  
+precision: 43
+subtract_eq1  subtract_eq  +89690.7376661374  ->  0E-10  
+precision: 233
+subtract_eq2  subtract_eq  +Infinity  ->  NaN  Invalid_operation 
+
+precision: 116
+samequantum_eq2  samequantum_eq  +.956099822146866314870e+96185702  ->  1  
diff --git a/tests/testdata_dist/cov.decTest b/tests/testdata_dist/cov.decTest
new file mode 100644 (file)
index 0000000..1ba0ce8
--- /dev/null
@@ -0,0 +1,277 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+precision: 28
+maxexponent: 999999999
+minexponent: -999999999
+clamp: 0
+
+covx5000 power 0 123456789123456789123456789 -> 0
+covx5001 power 1 123456789123456789123456789 -> 1
+covx5004 power 1.0000000000000 123456789123456789123456789 -> 1.000000000000000000000000000 Rounded
+covx5008 power 1.000000001 12345676891234567891 -> Infinity  Rounded Inexact Overflow
+covx5010 power 1.000000001 -12345676891234567891 -> 0E-1000000026  Rounded Underflow Inexact Clamped Subnormal
+
+rounding: down
+covx5011 power 2.2 123456789123456789123456788 -> 9.999999999999999999999999999E+999999999 Rounded Inexact Overflow
+
+rounding: down
+covx5012 power 2.2 2921000000.891239129 -> 9.999999999999999999999999999E+999999999 Rounded Inexact Overflow
+
+rounding: half_even
+precision: 5000
+covx5025 exp 1e20 -> Infinity  Rounded Inexact Overflow
+
+rounding: half_even
+precision: 5000
+covx5026 exp -1e20 -> 0E-1000004998  Rounded Subnormal Clamped Underflow Inexact
+
+rounding: down
+precision: 4
+maxexponent: 4
+minexponent: -4
+covx5027 ln 1.000000000000000000001 -> 0E-7 Subnormal Inexact Clamped Underflow Rounded
+covx5028 ln 1e999999999 -> Infinity Inexact Overflow Rounded
+
+rounding: half_even
+precision: 1
+maxexponent: 1
+minexponent: -1
+covx5029 log10 1.000000000000000000001 -> 0.0 Subnormal Inexact Clamped Underflow Rounded
+
+rounding: half_even
+covx5030 log10 12345e100 -> Infinity Inexact Overflow Rounded
+
+rounding: half_up
+precision: 28
+maxexponent: 999999999
+minexponent: -999999999
+covx5031 log10 177.82794100389228012254211972400046751027254035861481049206983723942104337400734839183756638300367799 -> 2.250000000000000000000000001  Inexact Rounded
+
+rounding: half_even
+precision: 10000
+covx5034 rotate 1234 12345678901234567890 -> NaN Invalid_operation
+
+rounding: half_up
+precision: 24
+maxexponent: 999999999
+minexponent: -999999999
+covx5048 remaindernear  999999999999999999999999.5   1  -> NaN Division_impossible
+covx5051 remaindernear  999999999999999999999998.5   1  -> 0.5
+
+rounding: half_even
+precision: 28
+maxexponent: 99
+minexponent: -99
+covx5056 apply 9999999999999999999999999999E-127 -> 1.000000000000000000000000000E-99  Underflow Subnormal Rounded Inexact
+covx5057 quantize 9999999999999999999999999999e71 1e72 -> 1.000000000000000000000000000E+99 Rounded Inexact
+
+rounding: half_even
+precision: 58
+maxexponent: 99
+minexponent: -99
+covx5058 apply 999999999999999999999999999999999999999999999999E-159 -> 1.000000000000000000000000000000000000000000000E-111  Underflow Subnormal Rounded Inexact
+covx5059 quantize 999999999999999999999999999999999999999999999999e51 1e52 -> 1.00000000000000000000000000000000000000000000000E+99 Rounded Inexact
+
+covx5060 tointegralx 999999999999999999999999999999999999999999999.9 -> 1000000000000000000000000000000000000000000000  Inexact Rounded
+covx5061 tointegralx 9999999999999999999999999999999999999999999999999999999999999999999999999999.9 -> 10000000000000000000000000000000000000000000000000000000000000000000000000000  Inexact Rounded
+
+rounding: half_even
+precision: 2000
+maxexponent: 999999999
+minexponent: -999999999
+
+covx5071 quantize 9999999999999999999999999999999999999999999999999999999999 1e1 -> 1.000000000000000000000000000000000000000000000000000000000E+58  Rounded Inexact
+covx5072 and  100000010010011000000000000000000000000101001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001000000000000 1081000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000010101000000000000000000000000000000000000000000000000000000000000000000000000000000100001010010100000100101010000101000100000100101000100100000010001010010000000000000000000000000000000000010101001010000000000000000000000000000000000000000001 ->  NaN Invalid_operation
+covx5073 invert  1081000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000010101000000000000000000000000000000000000000000000000000000000000000000000000000000100001010010100000100101010000101000100000100101000100100000010001010010000000000000000000000000000000000010101001010000000000000000000000000000000000000000001 ->  NaN Invalid_operation
+covx5074 or  100000010010011000000000000000000000000101001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001000000000000 1081000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000010101000000000000000000000000000000000000000000000000000000000000000000000000000000100001010010100000100101010000101000100000100101000100100000010001010010000000000000000000000000000000000010101001010000000000000000000000000000000000000000001 ->  NaN Invalid_operation
+covx5075 xor  100000010010011000000000000000000000000101001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001000000000000 1081000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000010101000000000000000000000000000000000000000000000000000000000000000000000000000000100001010010100000100101010000101000100000100101000100100000010001010010000000000000000000000000000000000010101001010000000000000000000000000000000000000000001 ->  NaN Invalid_operation
+
+rounding: half_even
+precision: 28
+covx5076 shift  123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789  -20   -> 91234567
+
+rounding: half_even
+precision: 80
+covx5077 reduce 9892345673.0123456780000000000000000000000000000000000000000000000000000000000000000000000 -> 9892345673.012345678  Rounded
+
+rounding: down
+precision: 28
+covx5078 resc Infinity 425000000 -> Infinity
+covx50780 resc NaN 425000000 -> NaN
+covx50782 resc sNaN 425000000 -> sNaN
+covx50783 resc 0 425000000 -> 0E+425000000
+
+cov64x50784 resc 1e999999999999999999 -1000 -> NaN Invalid_operation
+cov32x50785 resc 1e425000000 -1000 -> NaN Invalid_operation
+
+rounding: half_even
+precision: 58
+maxexponent: 99
+minexponent: -99
+covx5079 resc 999999999e200 100 -> 9.999999990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+208
+
+rounding: half_even
+covx5080 resc 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999e100 120 -> 1.0000000000000000000000000000000000000000000000000000000000000000000000E+190  Inexact Rounded
+
+rounding: half_even
+precision: 58
+maxexponent: 999
+minexponent: -999
+covx5081 resc 1E-1000 -1005 -> 1.00000E-1000 Subnormal
+
+-- extreme cases for NaN payloads
+maxexponent: 1
+minexponent: -1
+precision: 1
+clamp: 1
+covx5082 plus NaN123 -> NaN
+covx5083 plus NaN0 -> NaN
+
+-- decapitating leads to NaN0 => NaN
+maxexponent: 999
+minexponent: -999
+precision: 6
+covx5084 plus NaN123000000 -> NaN
+covx5085 plus NaN12300000 -> NaN
+covx5086 plus NaN1230000 -> NaN30000
+
+clamp: 0
+covx5087 plus NaN123000000 -> NaN
+covx5088 plus NaN12300000 -> NaN300000
+covx5089 plus NaN1230000 -> NaN230000
+
+
+-- floor, ceil, trunc
+rounding: half_even
+precision: 2000
+maxexponent: 999
+minexponent: -999
+clamp: 0
+
+floor10000 floor 0 -> 0
+floor10001 floor 0.0 -> 0
+floor10002 floor -0.0 -> -0
+floor10003 floor 1e-200 -> 0
+floor10004 floor -1e-200 -> -1
+floor10005 floor 0.5 -> 0
+floor10006 floor -0.5 -> -1
+floor10007 floor 0.999999999999 -> 0
+floor10008 floor -0.99999999999 -> -1
+floor10009 floor -1.0 -> -1
+floor10010 floor 1.0 -> 1
+floor10011 floor 9.9 -> 9
+floor10012 floor -9.9 -> -10
+floor10013 floor 9.333e+80 -> 9.333E+80
+floor10014 floor -9.333e+80 -> -9.333E+80
+floor10015 floor 100.0 -> 100
+floor10016 floor -100.0 -> -100
+ceil10017 ceil 0 -> 0
+ceil10018 ceil 0.0 -> 0
+ceil10019 ceil -0.0 -> -0
+ceil10020 ceil 1e-200 -> 1
+ceil10021 ceil -1e-200 -> -0
+ceil10022 ceil 0.5 -> 1
+ceil10023 ceil -0.5 -> -0
+ceil10024 ceil 0.999999999999 -> 1
+ceil10025 ceil -0.99999999999 -> -0
+ceil10026 ceil -1.0 -> -1
+ceil10027 ceil 1.0 -> 1
+ceil10028 ceil 9.9 -> 10
+ceil10029 ceil -9.9 -> -9
+ceil10030 ceil 9.333e+80 -> 9.333E+80
+ceil10031 ceil -9.333e+80 -> -9.333E+80
+ceil10032 ceil 100.0 -> 100
+ceil10033 ceil -100.0 -> -100
+trunc10034 trunc 0 -> 0
+trunc10035 trunc 0.0 -> 0
+trunc10036 trunc -0.0 -> -0
+trunc10037 trunc 1e-200 -> 0
+trunc10038 trunc -1e-200 -> -0
+trunc10039 trunc 0.5 -> 0
+trunc10040 trunc -0.5 -> -0
+trunc10041 trunc 0.999999999999 -> 0
+trunc10042 trunc -0.99999999999 -> -0
+trunc10043 trunc -1.0 -> -1
+trunc10044 trunc 1.0 -> 1
+trunc10045 trunc 9.9 -> 9
+trunc10046 trunc -9.9 -> -9
+trunc10047 trunc 9.333e+80 -> 9.333E+80
+trunc10048 trunc -9.333e+80 -> -9.333E+80
+trunc10049 trunc 100.0 -> 100
+trunc10050 trunc -100.0 -> -100
+
+-- negative etop: trigger allocation failure in mpd_qshiftl.
+maxexponent: 50
+minexponent: -50
+precision: 100
+clamp: 1
+etop10051 apply 1e+50 -> 100000000000000000000000000000000000000000000000000.0000000000000000000000000000000000000000000000000 Clamped
+
+-- test mpd_qln10().
+
+lnX10052 ln10 1 -> 2 Rounded Inexact
+lnX10053 ln10 51 -> 2.30258509299404568401799145468436420760110148862877 Rounded Inexact
+lnX10054 ln10 101 -> 2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983 Rounded Inexact
+
+precision: 4
+rounding: floor
+maxExponent: 4
+minExponent: -4
+clamp: 0
+
+covx10101 divide 99960 -8.4E+2 -> -119.0
+
+-- ExtendedRange==0 disables the allcr=0 tests.
+
+ExtendedRange: 0
+
+Precision: 119
+MaxExponent: 425000000
+MinExponent: -425000000
+Rounding: Half_even
+Clamp: 0
+gen14398 exp '-978598892.4783936221181690860' -> '1.0000000000000000000E-425000099' Inexact Rounded Subnormal Underflow
+
+precision: 21
+MaxExponent: 2100
+MinExponent: -2100
+
+expx10001: exp -4835.428695287495936437782054837164835962313126120423249669988592031902480322440208495594130688156427 -> 1.00000000000000000000E-2100 Rounded Inexact Subnormal Underflow
+
+precision: 21
+rounding: Up
+MaxExponent: 2100
+MinExponent: -55
+clamp: 0
+
+lnx10001 ln 1.0000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000025 -> 1.00000000000000000000E-55 Rounded Inexact Subnormal Underflow
+
+precision: 43
+rounding: Up
+MaxExponent: 784
+MinExponent: -101
+clamp: 1
+
+lnx10002 ln 1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 -> 1.000000000000000000000000000000000000000000E-101 Rounded Inexact
+
+precision: 7
+rounding: Up
+MaxExponent: 96
+MinExponent: -95
+clamp: 1
+
+lnx10003 ln 1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005 -> 5E-101 Rounded Inexact Subnormal Underflow
+
+precision: 21
+rounding: Up
+MaxExponent: 2100
+MinExponent: -55
+clamp: 0
+
+log10x10001 log10 1.000000000000000000000000000000000000000000000000000000230258509299404568401799145468436420760110149 -> 1.00000000000000000000E-55 Rounded Inexact
+
+-- Restore ExtendedRange because it is a global variable in the C tests.
+ExtendedRange: 1
diff --git a/tests/testdata_dist/divmod.decTest b/tests/testdata_dist/divmod.decTest
new file mode 100644 (file)
index 0000000..f21e445
--- /dev/null
@@ -0,0 +1,19 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+precision: 4
+divmod0  divmod  +.19e-911565689  3  ->  0  1.9E-911565690  
+precision: 134
+divmod1  divmod  -7511169757701755204403067994764250868266023726415875410280423917144182133410083809891399408586918959074  4246476766758966627011583811854662354887063790038443  ->  -1768800389183453909698778567576755142065118498146502  -2797376381641405358930320502134014338018800092982688  
+precision: 262
+divmod2  divmod  -Inf  +7.905442548849169790507598032704018880614786823147752534821712985326338770302489617E486798639  ->  -Infinity  NaN  Invalid_operation 
+precision: 216
+divmod3  divmod  -757719995173294752327540483701116946913961037738625667311193429926112129713646725612959034118065582310312494071362898125823373077706316701639971503.7061421  62623122235568293491576846461787522859478144460815517209962234575328066320595  ->  -12099684080314501435119346718279324158329411218353503927245988982340212  -27825671910110331254919071614638382146665442212850800774501568154918287705363.7061421  
+precision: 264
+divmod60  divmod  .29710504707442803271433301062233400148972305067963266041512977334667175052716876430358025717559782571789385135307807427e+408570266  316018911136590957402752480309236024133069474131769799.16590  ->  NaN  NaN  Invalid_operation
+
diff --git a/tests/testdata_dist/divmod_eq.decTest b/tests/testdata_dist/divmod_eq.decTest
new file mode 100644 (file)
index 0000000..d5d6fc6
--- /dev/null
@@ -0,0 +1,19 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+precision: 287
+divmod_eq0  divmod_eq  53464363333374808283597687012581545556435043430877416069601812915742085729084642883631769946846145248101886580777706430843020189140428608578077608617960084403336009490995063314174483207370023308095611980240  ->  1  0  
+precision: 150
+divmod_eq1  divmod_eq  +60282411403474423437251944856221834213664804070443619110124159546725081821627.9520731524443948144720661450464724117304412956005906597494085920338030465  ->  1  0E-73  
+precision: 85
+divmod_eq2  divmod_eq  -63467629447618961271624908961051190267743022525700e648470929  ->  1  -0E+648470929  
+precision: 108
+divmod_eq3  divmod_eq  -762879944390665.371815875601  ->  1  -0E-12  
+precision: 288
+divmod_eq4  divmod_eq  +997594334066627951033394333915508375011722549244112196997015462663582982961809024613180076099282803800482019887107098847480355044678421823378359002130300510590127911433084709732427595232911169827362243916e+685827935  ->  1  0E+685827935  
+
diff --git a/tests/testdata_dist/extra.decTest b/tests/testdata_dist/extra.decTest
new file mode 100644 (file)
index 0000000..f44477b
--- /dev/null
@@ -0,0 +1,10 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision: 28
+maxexponent: 384
+minexponent: -384
+rounding: half_even
+
+mulx11337 multiply 88888444448888880000000000000000000000000000000000 0.0000000000000000000000000000000000000005 -> 44444222224.44444000000000000 Rounded
diff --git a/tests/testdata_dist/fma_eq.decTest b/tests/testdata_dist/fma_eq.decTest
new file mode 100644 (file)
index 0000000..97095f9
--- /dev/null
@@ -0,0 +1,19 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+precision: 90
+fma_eq_eq_op0  fma_eq_eq_op  1171338855698368789056737337147116733764903254  6334341367673830006086235053199346622349828839  ->  1.37203471486876402097062945058787905483129562161521709249566934082907767436352197432961736E+90 Rounded Inexact 
+precision: 159
+fma_eq_eq_op1  fma_eq_eq_op  8846971720773377832118709116655976973169530112542418661239912443  6256181023698902965000946039667715131691025387162696107046936851  ->  78268908628163862019856367747135852723774224319783179476206001612701777270473207771109192335032068044416291512398851733353165100 
+precision: 255
+fma_eq_eq_op2  fma_eq_eq_op  43160782777719481063272448014  49812293344537606155996723694  ->  1862853169985486563497651896052778055443061751840329267890 
+precision: 93
+fma_eq_eq_op3  fma_eq_eq_op  2032071158752304061247812627062271522999803982027661360583876846812153089899539485250840  6360518457329636924305818275371080716215939771089431758009813661626572665849888335794576  ->  4.12931319423293173438185246065285707617873245447024136224145210538137905488220974932370465884E+174 Rounded Inexact 
+precision: 227
+fma_eq_eq_op4  fma_eq_eq_op  95019985613539095380134663979529421356656050721802478521977200303668310888  94197867895449893953505329706678321477778056838953868383808842326244093635  ->  9028797665997176656298151698503832425497797348392415467972558968046789558677401017404152498353487199247588110290399071760384180693759079897263442179 
+
diff --git a/tests/testdata_dist/format.decTest b/tests/testdata_dist/format.decTest
new file mode 100644 (file)
index 0000000..e8f1d85
--- /dev/null
@@ -0,0 +1,133 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+xfmt1  format  .12345  '\xe6\xae\x8d<50.23'  ->  '0.12345\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d\xe6\xae\x8d'
+xfmt4  format  -2815980E0  ','  ->  '-2,815,980'
+
+-- alignment of specials
+xfmt12201  format  sNaN '+10.10'  ->  '     +sNaN'
+xfmt12202  format  Inf  ' 10.10'  ->  '  Infinity'
+xfmt12203  format  Inf  ' 10.10'  ->  '  Infinity'
+
+-- zero padding of specials
+xfmt12204  format  NaN  '010'  ->  '       NaN'
+
+-- zero padding conflicts with alignment specifier
+xfmt12205  format  999  '<010'  ->   NULL  Invalid_operation
+
+-- zero minimum width
+xfmt12206  format  999  '00.10'  ->   NULL  Invalid_operation
+
+-- excessive minimum width
+xfmt12207  format  999  '18446744073709551616.10'  ->   NULL  Invalid_operation
+
+-- invalid fraction digits
+xfmt12207  format  999  '100.-10'  ->   NULL  Invalid_operation
+
+-- excessive number of fraction digits
+xfmt12207  format  999  '100.18446744073709551616'  ->   NULL  Invalid_operation
+
+-- trailing garbage
+xfmt12208  format  999  '10x'  ->   NULL  Invalid_operation
+
+-- excess precision after rescale
+xfmt12209  format  999999999e20  '.7e'  ->  1.0000000e+29 
+
+-- illegal UTF-8 sequences (see http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt)
+xfmt12211  format  Inf  '\xfe=10.10'              ->  NULL  Invalid_operation
+xfmt12212  format  Inf  '\xff=10.10'              ->  NULL  Invalid_operation
+xfmt12213  format  Inf  '\xfe\xfe\xff\xff=10.10'  ->  NULL  Invalid_operation
+xfmt12214  format  Inf  '\xc0\xaf=10.10'          ->  NULL  Invalid_operation
+xfmt12215  format  Inf  '\xe0\x80\xaf=10.10'      ->  NULL  Invalid_operation
+xfmt12216  format  Inf  '\xf0\x80\x80\xaf<10.10'  ->  NULL  Invalid_operation
+xfmt12217  format  Inf  '\xc1\xbf>10.10'          ->  NULL  Invalid_operation
+xfmt12218  format  Inf  '\xe0\x9f\xbf^10.10'      ->  NULL  Invalid_operation
+xfmt12219  format  Inf  '\xf0\x8f\xbf\xbf=10.10'  ->  NULL  Invalid_operation
+xfmt12220  format  Inf  '\xed\xa0\x80=10.10'      ->  NULL  Invalid_operation
+xfmt12221  format  Inf  '\xf4\x90\x80\x80=10.10'  ->  NULL  Invalid_operation
+
+-- more illegal UTF-8 sequences
+xfmt12222  format  Inf  '\xf1\xf1\xf1\xf1=10.10'  ->  NULL  Invalid_operation
+
+
+-- power of 10 boundaries
+xfmt12224  format  99999  'g'    ->  99999
+xfmt12225  format  99999  '.6g'  ->  99999
+xfmt12226  format  99999  '.5g'  ->  99999
+xfmt12227  format  99999  '.4g'  ->  1.000e+5
+xfmt12228  format  99999  '.3g'  ->  1.00e+5
+xfmt12229  format  99999  '.2g'  ->  1.0e+5
+xfmt12230  format  99999  '.1g'  ->  1e+5
+xfmt12231  format  99999  '.0g'  ->  1e+5
+
+xfmt12232  format  99999  'e'    ->  9.9999e+4
+xfmt12233  format  99999  '.6e'  ->  9.999900e+4
+xfmt12234  format  99999  '.5e'  ->  9.99990e+4
+xfmt12235  format  99999  '.4e'  ->  9.9999e+4
+xfmt12236  format  99999  '.3e'  ->  1.000e+5
+xfmt12237  format  99999  '.2e'  ->  1.00e+5
+xfmt12238  format  99999  '.1e'  ->  1.0e+5
+xfmt12239  format  99999  '.0e'  ->  1e+5
+
+xfmt12240  format  9.9999  'f'    ->  9.9999
+xfmt12241  format  9.9999  '.6f'  ->  9.999900
+xfmt12242  format  9.9999  '.5f'  ->  9.99990
+xfmt12243  format  9.9999  '.4f'  ->  9.9999
+xfmt12244  format  9.9999  '.3f'  ->  10.000
+xfmt12245  format  9.9999  '.2f'  ->  10.00
+xfmt12246  format  9.9999  '.1f'  ->  10.0
+xfmt12247  format  9.9999  '.0f'  ->  10
+
+xfmt12248  format  9.99e425000000  'g'    ->  9.99e+425000000
+xfmt12249  format  9.99e425000000  '.3g'  ->  9.99e+425000000
+xfmt12250  format  9.99e425000000  '.2g'  ->  1.0e+425000001
+xfmt12251  format  9.99e425000000  '.1g'  ->  1e+425000001
+xfmt12252  format  9.99e425000000  '.0g'  ->  1e+425000001
+
+xfmt12253  format  9.99e425000000  'e'    ->  9.99e+425000000
+xfmt12254  format  9.99e425000000  '.3e'  ->  9.990e+425000000
+xfmt12255  format  9.99e425000000  '.2e'  ->  9.99e+425000000
+xfmt12256  format  9.99e425000000  '.1e'  ->  1.0e+425000001
+xfmt12257  format  9.99e425000000  '.0e'  ->  1e+425000001
+
+
+-- target exponent less than min_etiny
+xfmt12258  format  1e-849999999  '.10e'  ->  1.0000000000e-849999999
+
+
+-- '%' formatting: add trailing percent sign for special values.
+xfmt12259  format  NaN123  '%'        ->  'NaN123%'
+xfmt12260  format  sNaN    '+10.10%'  ->  '    +sNaN%'
+xfmt12261  format  Inf     ' 10.10%'  ->  ' Infinity%'
+
+-- 'z' formatting: coerce to positive zero
+xfmt12300  format  '-.508e+412'  'D=-z,.44%'  ->  '-508,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000.00000000000000000000000000000000000000000000%'
+
+xfmt12301  format  '0.00000000000000000000E9227'  'Q>-z,.440%'  ->  '0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%'
+
+xfmt12302  format  '-0.00000000000000000000E9227'  'Q>-z,.440%'  ->  '0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%'
+
+
+-- 64-bit only
+cov64x00001  format  9.99e999999999999999999  'g'    ->  9.99e+999999999999999999
+cov64x00002  format  9.99e999999999999999999  '.3g'  ->  9.99e+999999999999999999
+cov64x00003  format  9.99e999999999999999999  '.2g'  ->  1.0e+1000000000000000000
+cov64x00004  format  9.99e999999999999999999  '.1g'  ->  1e+1000000000000000000
+cov64x00005  format  9.99e999999999999999999  '.0g'  ->  1e+1000000000000000000
+
+cov64x00006  format  9.99e999999999999999999  'e'    ->  9.99e+999999999999999999
+cov64x00007  format  9.99e999999999999999999  '.3e'  ->  9.990e+999999999999999999
+cov64x00008  format  9.99e999999999999999999  '.2e'  ->  9.99e+999999999999999999
+cov64x00009  format  9.99e999999999999999999  '.1e'  ->  1.0e+1000000000000000000
+cov64x00010  format  9.99e999999999999999999  '.0e'  ->  1e+1000000000000000000
+
+-- target exponent less than min_etiny
+cov64x00011  format  1e-1999999999999999997  '.10e'  ->  1.0000000000e-1999999999999999997
+
+
+-- 32-bit only: result has too many digits for 'f' specifier.
+-- This test can legitimately fail with MPD_Malloc_error, thus it can produce
+-- a false positive.
+-- cov32x00012  format  1e-849999999 ',f'  ->  NULL  Invalid_operation
diff --git a/tests/testdata_dist/getint.decTest b/tests/testdata_dist/getint.decTest
new file mode 100644 (file)
index 0000000..cd1ee02
--- /dev/null
@@ -0,0 +1,307 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision: 16
+rounding: half_up
+maxExponent: 384
+minExponent: -383
+
+-- get_uint64_abs
+intx001 get_uint64_abs 0 -> 0
+intx002 get_uint64_abs -0 -> 0
+intx003 get_uint64_abs 0e100 -> 0
+intx004 get_uint64_abs -0e100 -> 0
+
+intx007 get_uint64_abs 18446744073709551615 -> 18446744073709551615
+intx008 get_uint64_abs 184467440737095516150e-1 -> 18446744073709551615
+
+intx028 get_uint64_abs -18446744073709551615 -> 18446744073709551615
+intx029 get_uint64_abs -184467440737095516150e-1 -> 18446744073709551615
+
+intx049 get_uint64_abs 18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx051 get_uint64_abs 1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+intx053 get_uint64_abs -18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx055 get_uint64_abs -1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+-- get_uint64
+intx127 get_uint64 0 -> 0
+intx128 get_uint64 -0 -> 0
+intx129 get_uint64 0e100 -> 0
+intx130 get_uint64 -0e100 -> 0
+
+intx133 get_uint64 18446744073709551615 -> 18446744073709551615
+intx134 get_uint64 184467440737095516150e-1 -> 18446744073709551615
+
+intx154 get_uint64 -18446744073709551615 -> 18446744073709551615 Invalid_operation
+
+intx175 get_uint64 18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx177 get_uint64 1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+intx179 get_uint64 -18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx181 get_uint64 -1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+intx183 get_uint64 1e0 -> 1
+intx184 get_uint64 1e1 -> 10
+intx202 get_uint64 1e19 -> 10000000000000000000
+intx203 get_uint64 1e20 -> 18446744073709551615 Invalid_operation
+
+-- get_u64
+intx253 get_u64 0 -> 0
+intx254 get_u64 -0 -> 0
+intx255 get_u64 0e100 -> 0
+intx256 get_u64 -0e100 -> 0
+
+intx259 get_u64 18446744073709551615 -> 18446744073709551615
+intx260 get_u64 184467440737095516150e-1 -> 18446744073709551615
+
+intx280 get_u64 -18446744073709551615 -> 18446744073709551615 Invalid_operation
+
+intx301 get_u64 18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx303 get_u64 1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+intx305 get_u64 -18446744073709551616 -> 18446744073709551615 Invalid_operation
+intx307 get_u64 -1844674407370955161600000000000000000000e-20 -> 18446744073709551615 Invalid_operation
+
+intx309 get_u64 1e0 -> 1
+intx320 get_u64 1e11 -> 100000000000
+intx328 get_u64 1e19 -> 10000000000000000000
+intx329 get_u64 1e20 -> 18446744073709551615 Invalid_operation
+
+intx330 get_u64 -1e0 -> 18446744073709551615 Invalid_operation
+
+intx351 get_u64 1.0 -> 1
+intx352 get_u64 1.2 -> 18446744073709551615 Invalid_operation
+
+intx375 get_u64 -0.1 -> 18446744073709551615 Invalid_operation
+intx377 get_u64 -191831e99999 -> 18446744073709551615 Invalid_operation
+
+
+-- get_ssize64
+intx379 get_ssize64 0 -> 0
+intx380 get_ssize64 -0 -> 0
+intx383 get_ssize64 0e-1000 -> 0
+intx384 get_ssize64 -0e-1000 -> 0
+
+intx385 get_ssize64 9223372036854775807 -> 9223372036854775807
+intx386 get_ssize64 92233720368547758070e-1 -> 9223372036854775807
+intx405 get_ssize64 922337203685477580700000000000000000000e-20 -> 9223372036854775807
+
+intx406 get_ssize64 -9223372036854775808 -> -9223372036854775808
+intx417 get_ssize64 -922337203685477580800000000000e-11 -> -9223372036854775808
+intx426 get_ssize64 -922337203685477580800000000000000000000e-20 -> -9223372036854775808
+
+intx428 get_ssize64 9999999999999999999 -> 9223372036854775807 Invalid_operation
+intx430 get_ssize64 999999999999999999900000000000000000000e-20 -> 9223372036854775807 Invalid_operation
+
+intx431 get_ssize64 -9223372036854775809 -> 9223372036854775807 Invalid_operation
+intx434 get_ssize64 -999999999999999999900000000000000000000e-20 -> 9223372036854775807 Invalid_operation
+
+intx435 get_ssize64 1e0 -> 1
+intx451 get_ssize64 1e16 -> 10000000000000000
+intx454 get_ssize64 1e19 -> 9223372036854775807 Invalid_operation
+
+intx455 get_ssize64 -1e0 -> -1
+intx466 get_ssize64 -1e11 -> -100000000000
+intx474 get_ssize64 -1e19 -> 9223372036854775807 Invalid_operation
+
+intx475 get_ssize64 1.0 -> 1
+intx477 get_ssize64 12.3 -> 9223372036854775807 Invalid_operation
+
+intx495 get_ssize64 0.1 -> 9223372036854775807 Invalid_operation
+intx499 get_ssize64 -0.1 -> 9223372036854775807 Invalid_operation
+
+
+-- get_i64
+intx503 get_i64 0 -> 0
+intx504 get_i64 -0 -> 0
+intx507 get_i64 0e-1000 -> 0
+intx508 get_i64 -0e-1000 -> 0
+
+intx509 get_i64 9223372036854775807 -> 9223372036854775807
+intx510 get_i64 92233720368547758070e-1 -> 9223372036854775807
+intx529 get_i64 922337203685477580700000000000000000000e-20 -> 9223372036854775807
+
+intx530 get_i64 -9223372036854775808 -> -9223372036854775808
+intx550 get_i64 -922337203685477580800000000000000000000e-20 -> -9223372036854775808
+
+intx551 get_i64 9223372036854775808 -> 9223372036854775807 Invalid_operation
+intx553 get_i64 922337203685477580800000000000000000000e-20 -> 9223372036854775807 Invalid_operation
+
+intx555 get_i64 -9223372036854775809 -> 9223372036854775807 Invalid_operation
+intx557 get_i64 -922337203685477580900000000000000000000e-20 -> 9223372036854775807 Invalid_operation
+
+intx559 get_i64 1e0 -> 1
+intx577 get_i64 1e18 -> 1000000000000000000
+intx578 get_i64 1e19 -> 9223372036854775807 Invalid_operation
+
+intx579 get_i64 -1e0 -> -1
+intx597 get_i64 -1e18 -> -1000000000000000000
+intx598 get_i64 -1e19 -> 9223372036854775807 Invalid_operation
+
+intx599 get_i64 1.0 -> 1
+intx601 get_i64 12.3 -> 9223372036854775807 Invalid_operation
+
+intx625 get_i64 -191831e99999 -> 9223372036854775807 Invalid_operation
+
+
+-- get_uint32_abs
+intx627 get_uint32_abs 0 -> 0
+intx628 get_uint32_abs -0 -> 0
+intx629 get_uint32_abs 0e100 -> 0
+intx630 get_uint32_abs -0e100 -> 0
+intx631 get_uint32_abs 0e-1000 -> 0
+intx632 get_uint32_abs -0e-1000 -> 0
+
+intx633 get_uint32_abs 4294967295 -> 4294967295
+intx653 get_uint32_abs 429496729500000000000000000000e-20 -> 4294967295
+
+intx654 get_uint32_abs -4294967295 -> 4294967295
+intx674 get_uint32_abs -429496729500000000000000000000e-20 -> 4294967295
+
+intx675 get_uint32_abs 4294967296 -> 4294967295 Invalid_operation
+intx678 get_uint32_abs 999999999900000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx679 get_uint32_abs -4294967296 -> 4294967295 Invalid_operation
+intx682 get_uint32_abs -999999999900000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx683 get_uint32_abs 1e0 -> 1
+intx684 get_uint32_abs 1e1 -> 10
+intx692 get_uint32_abs 1e9 -> 1000000000
+intx693 get_uint32_abs 1e10 -> 4294967295 Invalid_operation
+
+intx694 get_uint32_abs -1e0 -> 1
+intx701 get_uint32_abs -1e7 -> 10000000
+intx704 get_uint32_abs -1e10 -> 4294967295 Invalid_operation
+
+intx705 get_uint32_abs 1.0 -> 1
+intx707 get_uint32_abs 12.3 -> 4294967295 Invalid_operation
+
+intx726 get_uint32_abs 0.01 -> 4294967295 Invalid_operation
+
+
+-- get_uint32
+intx733 get_uint32 0 -> 0
+intx734 get_uint32 -0 -> 0
+intx737 get_uint32 0e-1000 -> 0
+intx738 get_uint32 -0e-1000 -> 0
+
+intx739 get_uint32 4294967295 -> 4294967295
+intx740 get_uint32 42949672950e-1 -> 4294967295
+intx741 get_uint32 429496729500e-2 -> 4294967295
+
+intx760 get_uint32 -4294967295 -> 4294967295 Invalid_operation
+intx761 get_uint32 -42949672950e-1 -> 4294967295 Invalid_operation
+
+intx781 get_uint32 4294967296 -> 4294967295 Invalid_operation
+intx783 get_uint32 429496729600000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx785 get_uint32 -4294967296 -> 4294967295 Invalid_operation
+intx788 get_uint32 -999999999900000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx789 get_uint32 1e0 -> 1
+intx798 get_uint32 1e9 -> 1000000000
+intx799 get_uint32 1e10 -> 4294967295 Invalid_operation
+
+intx800 get_uint32 -1e0 -> 4294967295 Invalid_operation
+intx801 get_uint32 -1e1 -> 4294967295 Invalid_operation
+
+intx811 get_uint32 1.0 -> 1
+intx812 get_uint32 1.2 -> 4294967295 Invalid_operation
+intx813 get_uint32 12.3 -> 4294967295 Invalid_operation
+
+intx831 get_uint32 0.1 -> 4294967295 Invalid_operation
+intx832 get_uint32 0.01 -> 4294967295 Invalid_operation
+
+
+-- get_u32
+intx839 get_u32 0 -> 0
+intx840 get_u32 -0 -> 0
+intx843 get_u32 0e-1000 -> 0
+intx844 get_u32 -0e-1000 -> 0
+
+intx845 get_u32 4294967295 -> 4294967295
+intx847 get_u32 429496729500e-2 -> 4294967295
+
+intx866 get_u32 -4294967295 -> 4294967295 Invalid_operation
+intx868 get_u32 -429496729500e-2 -> 4294967295 Invalid_operation
+
+intx888 get_u32 9999999999 -> 4294967295 Invalid_operation
+intx890 get_u32 999999999900000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx891 get_u32 -4294967296 -> 4294967295 Invalid_operation
+intx894 get_u32 -999999999900000000000000000000e-20 -> 4294967295 Invalid_operation
+
+intx895 get_u32 1e0 -> 1
+intx904 get_u32 1e9 -> 1000000000
+intx905 get_u32 1e10 -> 4294967295 Invalid_operation
+
+intx906 get_u32 -1e0 -> 4294967295 Invalid_operation
+
+intx927 get_u32 1.0 -> 1
+intx928 get_u32 1.2 -> 4294967295 Invalid_operation
+
+intx948 get_u32 0.01 -> 4294967295 Invalid_operation
+intx951 get_u32 -0.1 -> 4294967295 Invalid_operation
+
+-- get_ssize32
+intx955 get_ssize32 0 -> 0
+intx960 get_ssize32 -0e-1000 -> 0
+
+intx961 get_ssize32 2147483647 -> 2147483647
+intx981 get_ssize32 214748364700000000000000000000e-20 -> 2147483647
+
+intx982 get_ssize32 -2147483648 -> -2147483648
+intx983 get_ssize32 -21474836480e-1 -> -2147483648
+
+intx1003 get_ssize32 2147483648 -> 2147483647 Invalid_operation
+intx1006 get_ssize32 999999999900000000000000000000e-20 -> 2147483647 Invalid_operation
+
+intx1007 get_ssize32 -2147483649 -> 2147483647 Invalid_operation
+intx1010 get_ssize32 -999999999900000000000000000000e-20 -> 2147483647 Invalid_operation
+
+intx1011 get_ssize32 1e0 -> 1
+intx1021 get_ssize32 1e10 -> 2147483647 Invalid_operation
+
+intx1022 get_ssize32 -1e0 -> -1
+intx1023 get_ssize32 -1e1 -> -10
+intx1032 get_ssize32 -1e10 -> 2147483647 Invalid_operation
+
+intx1033 get_ssize32 1.0 -> 1
+intx1035 get_ssize32 12.3 -> 2147483647 Invalid_operation
+
+intx1053 get_ssize32 0.1 -> 2147483647 Invalid_operation
+intx1054 get_ssize32 0.01 -> 2147483647 Invalid_operation
+
+
+-- get_i32
+intx1061 get_i32 0 -> 0
+intx1062 get_i32 -0 -> 0
+intx1065 get_i32 0e-1000 -> 0
+intx1066 get_i32 -0e-1000 -> 0
+
+intx1087 get_i32 214748364700000000000000000000e-20 -> 2147483647
+
+intx1088 get_i32 -2147483648 -> -2147483648
+intx1107 get_i32 -21474836480000000000000000000e-19 -> -2147483648
+intx1108 get_i32 -214748364800000000000000000000e-20 -> -2147483648
+
+intx1109 get_i32 2147483648 -> 2147483647 Invalid_operation
+intx1112 get_i32 999999999900000000000000000000e-20 -> 2147483647 Invalid_operation
+
+intx1113 get_i32 -2147483649 -> 2147483647 Invalid_operation
+intx1116 get_i32 -999999999900000000000000000000e-20 -> 2147483647 Invalid_operation
+
+intx1117 get_i32 1e0 -> 1
+intx1118 get_i32 1e1 -> 10
+intx1127 get_i32 1e10 -> 2147483647 Invalid_operation
+
+intx1128 get_i32 -1e0 -> -1
+intx1138 get_i32 -1e10 -> 2147483647 Invalid_operation
+
+intx1139 get_i32 1.0 -> 1
+intx1140 get_i32 1.2 -> 2147483647 Invalid_operation
+
+intx1159 get_i32 0.1 -> 2147483647 Invalid_operation
+intx1166 get_i32 -192312e-99999 -> 2147483647 Invalid_operation
diff --git a/tests/testdata_dist/invroot.decTest b/tests/testdata_dist/invroot.decTest
new file mode 100644 (file)
index 0000000..f459e44
--- /dev/null
@@ -0,0 +1,19 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision: 300
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+invr0  invroot  -Infinity  ->  NaN  Invalid_operation
+invr2  invroot  -3376565240538317615037767038165755236116203447734678670744153013395372825208152016626465050595503278853396341085504348076568638479251916469338156396026574105877198631284532958688897642411006633822625089845756964537796850154627554386765663430233408597155470339398891901177074562300320044771370817242748115757639488987096292  ->  NaN  Invalid_operation 
+invr3  invroot  +95557790255393130740393196310110154284984751359031309896398764952438051628915583632179283447391171528179135502526608913896698702181735323913607751130208974016914650039266324499752512639801785865733011946576983092213847146916959759670480  ->  1.02297954737445094825826091353591897908034078907362836953104996508129183034044873965399342835094488559981276170073085746439896855924382620964142769617650621159588395307098063232472761245685892549208662446992817391636726995669662784742850399639941776275468962154520979557600061978823233542390107411560E-118  Inexact Rounded 
+invr4  invroot  +8051652260715717610263596110901879049239.3440890507241630654113595115188390222902940530  ->  1.11444206152562569343119139170646814590687673654520947187475195773625010295109279239701370204558834676388207588338406433950233296077761519272905614305165226142108699992490893369385504606331664848177785618942682376871229067701973143985740722295023921892208001610719113184272379066174701404312597426825E-20  Inexact Rounded 
+invr5  invroot  -.36592197165789919876969283257371189200220402200002659961343068719568750580103064629288885229809757695723911672911158669042664271337478150632454244454960565234911315537252232074269938437761369352899081342085708843126846060396303945350939465545638101  ->  NaN  Invalid_operation 
+invr6  invroot  -44746722843951604605041558461383456959182507845890557497798391539993092079028123553869176194186087843018195092434490484241070467623257968572786647008622287052058689209189964425521897319959862625870991347425877073690538415750266535581321654559177420756494308462900355003716612396795433991910  ->  NaN  Invalid_operation 
+invr7  invroot  +948943248730899668078135699979058061509555547156272936842321883099986210361071671784923581065994346170593144382682672978251687228086752034258114675547822342248175243738443748555587177693108781764137437494362833726107839479656881344405669222835358836515928499629038518395413414623714986535354329243601061487796782578547555724171276979324853544047867997682662973927475596357370151608948634124805660835860927562392504753703385812781424464842992506914140686889577535698890662689110270312013809940854818331439242777832632044817511942491643768346925240287503557045420121622930380562123041337404040098906874134159081948444913584344609448236967767455043319958191959963868118208517350380776078952546003182041690777965037559329942566975282571898259137917930121840676845801080  ->  3.24623443149519044146774385327836533216464183727554392679254765473291522509121311915692833886535654787361124041696472280514828762946576638098283717652191479560502039986192618456351921133995783561356878919873169984960441104204131287360651888424979333021787801486657416363806384287256165526847351704137E-383  Inexact Rounded 
+invr8  invroot  -36239581670653731300376393991270825298462698702529251372002981419327268900757988734157481351979218060250537055351261009894843519440401788657349170163016901917917815994316715255465947532963406193334061  ->  NaN  Invalid_operation 
+invr9  invroot  -261991398311695734643807.1535693133488382941868280431130928308788012431642456251576173417538422878251082524398e746378140  ->  NaN  Invalid_operation 
+invr10  invroot  5392994273424119957746641665203905176810793924546640338873763628068012901449347244884952406414806255774716519234359728634226215180798042381782514743562547260129885619190278005510661849262278265697792656530595774113949566960819294222315  ->  4.30610901552659999687939774182099552710011122859492366694590522944895625441106646296363694968075067418269832950495070518505429967811546889821795179881341981402317016586182144229577452648802459619383751681523643205218709146884421021314046308217102813588276744258032743878674683129154055178648878023013E-118  Inexact Rounded 
diff --git a/tests/testdata_dist/largeint.decTest b/tests/testdata_dist/largeint.decTest
new file mode 100644 (file)
index 0000000..ccd8460
--- /dev/null
@@ -0,0 +1,10 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision: 100000
+maxExponent: 100000
+minExponent: -100000
+
+mulx0 multiply 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -> 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+mul1 multiply 259855745323357232745890712253887416451031208967471470449474019695124644498423062949885155972388210929667968254753295807982809714377324353571159505725644474734025502157155585162121635329432951408923118497245594490844554514688555440386816694997271623738290867169838195324237382194632207972711820382722740832983119604772185759970531438734329257155448968357111498209319084687169297453768970243030196461820663515402922337244484747253569092293427194166336182946357953738189838946454487067757988318473463822892516894441885092115954989959874205368851025381845599412924834920868072593660225597609244258738124962509946323212487548741489023120311051975685625748462237119934353872499936114406070315958956840973495535670511961892832193556457073257685605625038869932428148474509692594861627984867168885747718098146606498469714709293233799961423893668297419966348859667514469748340756070244851481677941290982124188310727641641215737140894530557581125699755018698874779084610381966724506193601294216140108254951242363356714276433163835876834854356143830424081928156290751569375844735768779812122430307924366071270887125661218641244215455122944686197251233356728185437618187079427307532495495832595571876935718576224244810857240710367454751592276668977385938028534736362962557198957763593864841497962998453853504348235154663122215720321585727302082383850532858453585633746127559597 73863586366471071858632272772171571272935882441964516479374616316149272931593790497343216682598208230224776373372289858589011895845763564288514706982417281174353896147435747340773086473590956777728516424099993058303195637730931246816961994176434648563358435712833022466985692450507790737511830353437274532276953535921289733142272811233542749431151059279796273214118559957111493674547797374997443368110838125677976123334032342972204555966867389436141847597922862854964356674996648632875172159206979572295769675037307124373351245570841619414034545940107746372155318810479842336964975170618299746010385749731941063722230682448974898102934297285843816406151446415582099214661179010342379946358425311724962127303486094389312195368597386112406237347969437188351448611030296883504086844741981641694501930815688732070767673167531230473192564030425685616736775846815557596331974775308588836372387674187342243425434228675144969498435611574942444923821533431932180908625605460555369824624841142089893798195849724937757744727434276628170171086696129315430721422534108489034832144794322738667021572750512730410451811850982989512402719741253389355857495331704986893830679335156490961079531990428859673483569862198614828674245341647345312962315369386725846320817518285927388297750444493304037629796793879360514545746440935946472907498712429499422511068519933128276728360864581286722884150272311031494871476635354176719503633173244981711292241173882729810570569719753358059828324518747025 -> 19193877287515508286922610371049921778857059399790795016581612265042228107039504913715562077646993848105094295745360224746342690227769637865395325054741273218852120791444484701577808145489223038641121091810835953960123579601712425560811948773117879756979041632553106934933927828572431116043611673648252222129526414558451393266089253556964502030814080526133007281638344837512909248966169241598912583666543979728947406461733476537623030840723596255571399454422191021577574497216814740257343138864353302036298370183927901489946670276962374132234245372086367484502901752759921343138247786992265406131790463163576372655961473522665719287194385674219337912544704475303747244853462352399551391208515967112189523400102442007932348001056607278667226016551106795065720547891247304320358379578467982727240933292848188246106056791639457448021498975095172772456008061185362921689912037993117573119576389658912157022859096202791974330434556764001719020733628375683269628942342299059624343656712407586638962328031818855718404137756326555989340923308009070679898401299791222925817471459571506420077885348082557102117200980861921626269397323351618499931175515323021634484247385932321752650794158161488681652287081234887060006512598587134524596079431648377891594334234750374058680256729237374126734415919703883234922909694379491328883400376499131702162663953855371652794487306696291617874462197750894210810261341153310763612641180443950763885334442094654068375086705889233388960066850559841402563705437199405343628681593636306975680894866452877680618993491977808097347579251107579990855925679412318567625492258074775822297971074909424039696321891452442339752171065707350308935339602163840909734389967240232715365727353571877037562705281178429717673679954106028776676185724842384861212016652890117745849230098420379322080637506306557272997704877330273099524084419452193442300656886415489998034315852044682744794216564005898325116882881751644948309769873399847689407670047198636502488995292902799331405539487324898049413613585321533210323210928357273291891338454743521825223648610395632991747277123345656468186334886809795340409030630923971743710850653786020433640822793357291602435995357704698262459357661905694938331044292051395489170358876685746070436343466749533202951673118720531719833945005384623228403561753675662770508079216486819521715969085722283895024359135657105876101964808028701642713726540675586225807178905557579017231762153608495826990622178813107795131391760592728467145079772257147110886349721383456740692525338708440950122667396532993356875780360895183526593289806895764791451982461776514277306618909930243786109292349495253115847233032713759586436561495251888930166306235346001549927398718788430181496686713646770904788146742951646660507514597372962237987772719514344420727695857036426972375086225127034356454338178195441902239453948925
diff --git a/tests/testdata_dist/maxprec.decTest b/tests/testdata_dist/maxprec.decTest
new file mode 100644 (file)
index 0000000..f66aac7
--- /dev/null
@@ -0,0 +1,472 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision:   MAX_PREC
+rounding:    half_up
+maxExponent: MAX_EMAX
+minexponent: MIN_EMIN
+
+-- basics
+sqtx001 squareroot 1       -> 1
+sqtx002 squareroot -1      -> NaN Invalid_operation
+sqtx003 squareroot 1.00    -> 1.0
+sqtx004 squareroot -1.00   -> NaN Invalid_operation
+sqtx005 squareroot 0       -> 0
+sqtx006 squareroot 00.0    -> 0.0
+sqtx007 squareroot 0.00    -> 0.0
+sqtx008 squareroot 00.00   -> 0.0
+sqtx009 squareroot 00.000  -> 0.00
+sqtx010 squareroot 00.0000 -> 0.00
+
+sqtx012 squareroot -2      -> NaN Invalid_operation
+sqtx014 squareroot -2.00   -> NaN Invalid_operation
+sqtx016 squareroot -0      -> -0
+sqtx017 squareroot -0.0    -> -0.0
+sqtx018 squareroot -00.00  -> -0.0
+sqtx019 squareroot -00.000 -> -0.00
+sqtx020 squareroot -0.0000 -> -0.00
+sqtx021 squareroot -0E+9   -> -0E+4
+sqtx022 squareroot -0E+10  -> -0E+5
+sqtx023 squareroot -0E+11  -> -0E+5
+sqtx024 squareroot -0E+12  -> -0E+6
+sqtx025 squareroot -00     -> -0
+sqtx026 squareroot 0E+5    -> 0E+2
+sqtx027 squareroot 4.0     -> 2.0
+sqtx028 squareroot 4.00    -> 2.0
+
+sqtx031 squareroot -0.1            -> NaN Invalid_operation
+sqtx032 squareroot +0.01           -> 0.1
+sqtx033 squareroot -0.01           -> NaN Invalid_operation
+sqtx035 squareroot -0.001          -> NaN Invalid_operation
+sqtx036 squareroot +0.000001       -> 0.001
+sqtx037 squareroot -0.000001       -> NaN Invalid_operation
+sqtx038 squareroot +0.000000000001 -> 0.000001
+sqtx039 squareroot -0.000000000001 -> NaN Invalid_operation
+
+sqtx045 squareroot -1.1       -> NaN Invalid_operation
+sqtx046 squareroot -1.10      -> NaN Invalid_operation
+sqtx047 squareroot -1.100     -> NaN Invalid_operation
+sqtx048 squareroot -1.110     -> NaN Invalid_operation
+sqtx053 squareroot -9.9       -> NaN Invalid_operation
+sqtx054 squareroot -9.90      -> NaN Invalid_operation
+sqtx055 squareroot -9.900     -> NaN Invalid_operation
+sqtx056 squareroot -9.990     -> NaN Invalid_operation
+
+sqtx060 squareroot  1           -> 1
+sqtx061 squareroot  1.0         -> 1.0
+sqtx062 squareroot  1.00        -> 1.0
+sqtx067 squareroot  100         -> 10
+sqtx068 squareroot  100.0       -> 10.0
+sqtx069 squareroot  100.00      -> 10.0
+sqtx072 squareroot -10.0        -> NaN Invalid_operation
+sqtx073 squareroot -10.00       -> NaN Invalid_operation
+sqtx074 squareroot -100.0       -> NaN Invalid_operation
+sqtx075 squareroot -100.00      -> NaN Invalid_operation
+sqtx076 squareroot -1.1000E+3   -> NaN Invalid_operation
+sqtx077 squareroot -1.10000E+3  -> NaN Invalid_operation
+sqtx078 squareroot  1.000       -> 1.00
+sqtx079 squareroot  1.0000      -> 1.00
+
+---- famous squares
+sqtx080 squareroot     1  -> 1
+sqtx081 squareroot     4  -> 2
+sqtx082 squareroot     9  -> 3
+sqtx083 squareroot    16  -> 4
+sqtx084 squareroot    25  -> 5
+sqtx085 squareroot    36  -> 6
+sqtx086 squareroot    49  -> 7
+sqtx087 squareroot    64  -> 8
+sqtx088 squareroot    81  -> 9
+sqtx089 squareroot   100  -> 10
+sqtx090 squareroot   121  -> 11
+sqtx091 squareroot   144  -> 12
+sqtx092 squareroot   169  -> 13
+sqtx093 squareroot   256  -> 16
+sqtx094 squareroot  1024  -> 32
+sqtx095 squareroot  4096  -> 64
+sqtx100 squareroot   0.01 -> 0.1
+sqtx101 squareroot   0.04 -> 0.2
+sqtx102 squareroot   0.09 -> 0.3
+sqtx103 squareroot   0.16 -> 0.4
+sqtx104 squareroot   0.25 -> 0.5
+sqtx105 squareroot   0.36 -> 0.6
+sqtx106 squareroot   0.49 -> 0.7
+sqtx107 squareroot   0.64 -> 0.8
+sqtx108 squareroot   0.81 -> 0.9
+sqtx109 squareroot   1.00 -> 1.0
+sqtx110 squareroot   1.21 -> 1.1
+sqtx111 squareroot   1.44 -> 1.2
+sqtx112 squareroot   1.69 -> 1.3
+sqtx113 squareroot   2.56 -> 1.6
+sqtx114 squareroot  10.24 -> 3.2
+sqtx115 squareroot  40.96 -> 6.4
+
+---- Precision 1 squareroot tests [exhaustive, plus exponent adjusts]
+sqtx1202 squareroot 0.01 -> 0.1
+sqtx1204 squareroot 1.00E-2 -> 0.10
+sqtx1207 squareroot 1E+2 -> 1E+1
+sqtx1226 squareroot 0.04 -> 0.2
+sqtx1228 squareroot 4.00E-2 -> 0.20
+sqtx1231 squareroot 4E+2 -> 2E+1
+sqtx1266 squareroot 0.09 -> 0.3
+sqtx1268 squareroot 9.00E-2 -> 0.30
+sqtx1271 squareroot 9E+2 -> 3E+1
+
+---- Precision 2 squareroot tests [exhaustive, plus exponent adjusts]
+sqtx2202 squareroot 0.01 -> 0.1
+sqtx2204 squareroot 1.00E-2 -> 0.10
+sqtx2207 squareroot 1E+2 -> 1E+1
+sqtx2226 squareroot 0.04 -> 0.2
+sqtx2228 squareroot 4.00E-2 -> 0.20
+--sqtx2231 squareroot 4E+2 -> 2E+1
+sqtx2266 squareroot 0.09 -> 0.3
+sqtx2268 squareroot 9.00E-2 -> 0.30
+sqtx2271 squareroot 9E+2 -> 3E+1
+sqtx2274 squareroot 0.010 -> 0.10
+sqtx2275 squareroot 10.0E-1 -> 1.0
+sqtx2277 squareroot 10E-3 -> 0.10
+sqtx2278 squareroot 10E+1 -> 10
+sqtx2280 squareroot 10E+3 -> 1.0E+2
+sqtx2321 squareroot 0.16 -> 0.4
+sqtx2324 squareroot 16.00E-2 -> 0.40
+sqtx2327 squareroot 16E+2 -> 4E+1
+sqtx2399 squareroot 25E+2 -> 5E+1
+sqtx2484 squareroot 36.00E-2 -> 0.60
+sqtx2487 squareroot 36E+2 -> 6E+1
+sqtx2514 squareroot 0.040 -> 0.20
+sqtx2515 squareroot 40.0E-1 -> 2.0
+sqtx2517 squareroot 40E-3 -> 0.20
+sqtx2518 squareroot 40E+1 -> 20
+sqtx2520 squareroot 40E+3 -> 2.0E+2
+sqtx2585 squareroot 0.49 -> 0.7
+sqtx2588 squareroot 49.00E-2 -> 0.70
+sqtx2591 squareroot 49E+2 -> 7E+1
+sqtx2705 squareroot 0.64 -> 0.8
+sqtx2708 squareroot 64.00E-2 -> 0.80
+sqtx2711 squareroot 64E+2 -> 8E+1
+sqtx2841 squareroot 0.81 -> 0.9
+sqtx2844 squareroot 81.00E-2 -> 0.90
+sqtx2847 squareroot 81E+2 -> 9E+1
+sqtx2914 squareroot 0.090 -> 0.30
+sqtx2915 squareroot 90.0E-1 -> 3.0
+sqtx2917 squareroot 90E-3 -> 0.30
+sqtx2918 squareroot 90E+1 -> 30
+sqtx2920 squareroot 90E+3 -> 3.0E+2
+
+sqtx3002 squareroot 0.01 -> 0.1
+sqtx3008 squareroot 0.04 -> 0.2
+
+precision:   MAX_PREC
+rounding:    half_up
+maxExponent: MAX_EMAX
+minexponent: MIN_EMIN
+
+divx001 divide  1     1    ->  1
+divx002 divide  2     1    ->  2
+divx003 divide  1     2    ->  0.5
+divx004 divide  2     2    ->  1
+divx005 divide  0     1    ->  0
+divx006 divide  0     2    ->  0
+divx009 divide  3     3    ->  1
+
+divx010 divide  2.4   1    ->  2.4
+divx011 divide  2.4   -1   ->  -2.4
+divx012 divide  -2.4  1    ->  -2.4
+divx013 divide  -2.4  -1   ->  2.4
+divx014 divide  2.40  1    ->  2.40
+divx015 divide  2.400 1    ->  2.400
+divx016 divide  2.4   2    ->  1.2
+divx017 divide  2.400 2    ->  1.200
+divx018 divide  2.    2    ->  1
+divx019 divide  20    20   ->  1
+
+divx020 divide  187   187    ->  1
+divx021 divide  5     2      ->  2.5
+divx022 divide  50    20     ->  2.5
+divx023 divide  500   200    ->  2.5
+divx024 divide  50.0  20.0   ->  2.5
+divx025 divide  5.00  2.00   ->  2.5
+divx026 divide  5     2.0    ->  2.5
+divx027 divide  5     2.000  ->  2.5
+divx028 divide  5     0.20   ->  25
+divx029 divide  5     0.200  ->  25
+divx030 divide  10    1      ->  10
+divx031 divide  100   1      ->  100
+divx032 divide  1000  1      ->  1000
+divx033 divide  1000  100    ->  10
+
+divx035 divide  1     2      ->  0.5
+divx036 divide  1     4      ->  0.25
+divx037 divide  1     8      ->  0.125
+divx038 divide  1     16     ->  0.0625
+divx039 divide  1     32     ->  0.03125
+divx040 divide  1     64     ->  0.015625
+divx041 divide  1    -2      ->  -0.5
+divx042 divide  1    -4      ->  -0.25
+divx043 divide  1    -8      ->  -0.125
+divx044 divide  1    -16     ->  -0.0625
+divx045 divide  1    -32     ->  -0.03125
+divx046 divide  1    -64     ->  -0.015625
+divx047 divide -1     2      ->  -0.5
+divx048 divide -1     4      ->  -0.25
+divx049 divide -1     8      ->  -0.125
+divx050 divide -1     16     ->  -0.0625
+divx051 divide -1     32     ->  -0.03125
+divx052 divide -1     64     ->  -0.015625
+divx053 divide -1    -2      ->  0.5
+divx054 divide -1    -4      ->  0.25
+divx055 divide -1    -8      ->  0.125
+divx056 divide -1    -16     ->  0.0625
+divx057 divide -1    -32     ->  0.03125
+divx058 divide -1    -64     ->  0.015625
+
+divx070 divide  999999999        1    ->  999999999
+
+divx083 divide  999999        1  ->  999999
+divx084 divide  99999         1  ->  99999
+divx085 divide  9999          1  ->  9999
+divx086 divide  999           1  ->  999
+divx087 divide  99            1  ->  99
+divx088 divide  9             1  ->  9
+
+divx090 divide  0.            1    ->  0
+divx091 divide  .0            1    ->  0.0
+divx092 divide  0.00          1    ->  0.00
+divx093 divide  0.00E+9       1    ->  0E+7
+divx094 divide  0.0000E-50    1    ->  0E-54
+
+divx095 divide  1            1E-8  ->  1E+8
+divx096 divide  1            1E-9  ->  1E+9
+divx097 divide  1            1E-10 ->  1E+10
+divx098 divide  1            1E-11 ->  1E+11
+divx099 divide  1            1E-12 ->  1E+12
+
+divx100 divide  1  1   -> 1
+divx101 divide  1  2   -> 0.5
+divx103 divide  1  4   -> 0.25
+divx104 divide  1  5   -> 0.2
+divx107 divide  1  8   -> 0.125
+divx109 divide  1  10  -> 0.1
+divx110 divide  1  1   -> 1
+divx111 divide  2  1   -> 2
+divx112 divide  3  1   -> 3
+divx113 divide  4  1   -> 4
+divx114 divide  5  1   -> 5
+divx115 divide  6  1   -> 6
+divx116 divide  7  1   -> 7
+divx117 divide  8  1   -> 8
+divx118 divide  9  1   -> 9
+divx119 divide  10 1   -> 10
+
+divx120 divide  3E+1 0.001  -> 3E+4
+divx121 divide  2.200 2     -> 1.100
+
+divx133 divide  12345  5      ->  2469
+
+
+divx301 divide    0    7     -> 0
+divx302 divide    0    7E-5  -> 0E+5
+divx303 divide    0    7E-1  -> 0E+1
+divx304 divide    0    7E+1  -> 0.0
+divx305 divide    0    7E+5  -> 0.00000
+divx306 divide    0    7E+6  -> 0.000000
+divx307 divide    0    7E+7  -> 0E-7
+divx308 divide    0   70E-5  -> 0E+5
+divx309 divide    0   70E-1  -> 0E+1
+divx310 divide    0   70E+0  -> 0
+divx311 divide    0   70E+1  -> 0.0
+divx312 divide    0   70E+5  -> 0.00000
+divx313 divide    0   70E+6  -> 0.000000
+divx314 divide    0   70E+7  -> 0E-7
+divx315 divide    0  700E-5  -> 0E+5
+divx316 divide    0  700E-1  -> 0E+1
+divx317 divide    0  700E+0  -> 0
+divx318 divide    0  700E+1  -> 0.0
+divx319 divide    0  700E+5  -> 0.00000
+divx320 divide    0  700E+6  -> 0.000000
+divx321 divide    0  700E+7  -> 0E-7
+divx322 divide    0  700E+77 -> 0E-77
+
+divx351 divide 0E-92   7E-1  -> 0E-91
+divx352 divide 0E-92   7E+1  -> 0E-93
+divx353 divide 0E-92   7E+5  -> 0E-97
+divx354 divide 0E-92   7E+6  -> 0E-98
+divx356 divide 0E-92 777E-1  -> 0E-91
+divx357 divide 0E-92 777E+1  -> 0E-93
+divx358 divide 0E-92 777E+3  -> 0E-95
+divx359 divide 0E-92 777E+4  -> 0E-96
+divx360 divide 0E-92 777E+5  -> 0E-97
+divx361 divide 0E-92 777E+6  -> 0E-98
+
+divx386 divide 0E+90 777E-2  -> 0E+92
+
+divx391 divide 0E+90 700E+1  -> 0E+89
+divx392 divide 0E+90 700E-1  -> 0E+91
+divx393 divide 0E+90 700E-2  -> 0E+92
+
+divx441 divide 12345678000 1 -> 12345678000
+divx443 divide 1234567800  1 -> 1234567800
+divx445 divide 1234567890  1 -> 1234567890
+divx447 divide 1234567891  1 -> 1234567891
+divx449 divide 12345678901 1 -> 12345678901
+divx451 divide 1234567896  1 -> 1234567896
+
+divx453 divide 1e+1   1    ->   1E+1
+divx454 divide 1e+1   1.0  ->   1E+1
+divx455 divide 1e+1   1.00 ->   1E+1
+divx456 divide 1e+2   2    ->   5E+1
+divx457 divide 1e+2   2.0  ->   5E+1
+divx458 divide 1e+2   2.00 ->   5E+1
+
+divx460 divide 3e0      2e0     -> 1.5
+divx461 divide 30e-1    2e0     -> 1.5
+divx462 divide 300e-2   2e0     -> 1.50
+divx464 divide 3000e-3  2e0     -> 1.500
+divx465 divide 3e0      20e-1   -> 1.5
+divx466 divide 30e-1    20e-1   -> 1.5
+divx467 divide 300e-2   20e-1   -> 1.5
+divx468 divide 3000e-3  20e-1   -> 1.50
+divx469 divide 3e0      200e-2  -> 1.5
+divx470 divide 30e-1    200e-2  -> 1.5
+divx471 divide 300e-2   200e-2  -> 1.5
+divx472 divide 3000e-3  200e-2  -> 1.5
+divx473 divide 3e0      2000e-3 -> 1.5
+divx474 divide 30e-1    2000e-3 -> 1.5
+divx475 divide 300e-2   2000e-3 -> 1.5
+divx476 divide 3000e-3  2000e-3 -> 1.5
+
+divx480 divide 1        1.0E+33 -> 1E-33
+divx481 divide 1        10E+33  -> 1E-34
+divx482 divide 1        1.0E-33 -> 1E+33
+divx483 divide 1        10E-33  -> 1E+32
+
+divx484 divide 0e5     1e3 ->   0E+2
+divx485 divide 0e5     2e3 ->   0E+2
+divx486 divide 0e5    10e2 ->   0E+3
+divx487 divide 0e5    20e2 ->   0E+3
+divx488 divide 0e5   100e1 ->   0E+4
+divx489 divide 0e5   200e1 ->   0E+4
+
+divx491 divide 1e5     1e3 ->   1E+2
+divx492 divide 1e5     2e3 ->   5E+1
+divx493 divide 1e5    10e2 ->   1E+2
+divx494 divide 1e5    20e2 ->   5E+1
+divx495 divide 1e5   100e1 ->   1E+2
+divx496 divide 1e5   200e1 ->   5E+1
+
+divx511 divide 1         2    -> 0.5
+divx512 divide 1.0       2    -> 0.5
+divx513 divide 1.00      2    -> 0.50
+divx514 divide 1.000     2    -> 0.500
+divx515 divide 1.0000    2    -> 0.5000
+divx516 divide 1.00000   2    -> 0.50000
+divx517 divide 1.000000  2    -> 0.500000
+divx518 divide 1.0000000 2    -> 0.5000000
+divx519 divide 1.00      2.00 -> 0.5
+
+divx521 divide 2    1         -> 2
+divx522 divide 2    1.0       -> 2
+divx523 divide 2    1.00      -> 2
+divx524 divide 2    1.000     -> 2
+divx525 divide 2    1.0000    -> 2
+divx526 divide 2    1.00000   -> 2
+divx527 divide 2    1.000000  -> 2
+divx528 divide 2    1.0000000 -> 2
+divx529 divide 2.00 1.00      -> 2
+
+divx530 divide  2.40   2      ->  1.20
+divx531 divide  2.40   4      ->  0.60
+divx532 divide  2.40  10      ->  0.24
+divx533 divide  2.40   2.0    ->  1.2
+divx534 divide  2.40   4.0    ->  0.6
+divx535 divide  2.40  10.0    ->  0.24
+divx536 divide  2.40   2.00   ->  1.2
+divx537 divide  2.40   4.00   ->  0.6
+divx538 divide  2.40  10.00   ->  0.24
+divx539 divide  0.9    0.1    ->  9
+divx540 divide  0.9    0.01   ->  9E+1
+divx541 divide  0.9    0.001  ->  9E+2
+divx542 divide  5      2      ->  2.5
+divx543 divide  5      2.0    ->  2.5
+divx544 divide  5      2.00   ->  2.5
+divx545 divide  5      20     ->  0.25
+divx546 divide  5      20.0   ->  0.25
+divx547 divide  2.400  2      ->  1.200
+divx548 divide  2.400  2.0    ->  1.20
+divx549 divide  2.400  2.400  ->  1
+
+divx550 divide  240    1      ->  240
+divx551 divide  240    10     ->  24
+divx552 divide  240    100    ->  2.4
+divx553 divide  240    1000   ->  0.24
+divx554 divide  2400   1      ->  2400
+divx555 divide  2400   10     ->  240
+divx556 divide  2400   100    ->  24
+divx557 divide  2400   1000   ->  2.4
+
+divx570 divide  2.4E+6     2  ->  1.2E+6
+divx571 divide  2.40E+6    2  ->  1.20E+6
+divx572 divide  2.400E+6   2  ->  1.200E+6
+divx573 divide  2.4000E+6  2  ->  1.2000E+6
+divx574 divide  24E+5      2  ->  1.2E+6
+divx575 divide  240E+4     2  ->  1.20E+6
+divx576 divide  2400E+3    2  ->  1.200E+6
+divx577 divide  24000E+2   2  ->  1.2000E+6
+divx580 divide  2.4E+6     2  ->  1.2E+6
+divx581 divide  2.40E+6    2  ->  1.20E+6
+divx582 divide  2.400E+6   2  ->  1.200E+6
+divx583 divide  2.4000E+6  2  ->  1.2000E+6
+divx584 divide  24E+5      2  ->  1.2E+6
+divx585 divide  240E+4     2  ->  1.20E+6
+divx586 divide  2400E+3    2  ->  1.200E+6
+divx587 divide  24000E+2   2  ->  1.2000E+6
+divx590 divide  2.4E+6     2  ->  1.2E+6
+divx591 divide  2.40E+6    2  ->  1.20E+6
+divx592 divide  2.400E+6   2  ->  1.200E+6
+divx593 divide  2.4000E+6  2  ->  1.2000E+6
+divx594 divide  24E+5      2  ->  1.2E+6
+divx595 divide  240E+4     2  ->  1.20E+6
+divx596 divide  2400E+3    2  ->  1.200E+6
+divx597 divide  24000E+2   2  ->  1.2000E+6
+divx600 divide  2.4E+9     2  ->  1.2E+9
+divx601 divide  2.40E+9    2  ->  1.20E+9
+divx602 divide  2.400E+9   2  ->  1.200E+9
+divx603 divide  2.4000E+9  2  ->  1.2000E+9
+divx604 divide  24E+8      2  ->  1.2E+9
+divx605 divide  240E+7     2  ->  1.20E+9
+divx606 divide  2400E+6    2  ->  1.200E+9
+divx607 divide  24000E+5   2  ->  1.2000E+9
+
+divx731 divide 5.00 1E-3    -> 5.00E+3
+
+divx768 divide  1    -0.0   -> -Infinity Division_by_zero
+
+divx771 divide  0.0  -1.0   -> -0
+divx772 divide -0.0  -1.0   ->  0
+divx773 divide  0.0   1.0   ->  0
+divx774 divide -0.0   1.0   -> -0
+divx775 divide -1.0   0.0   -> -Infinity Division_by_zero
+divx776 divide -1.0  -0.0   ->  Infinity Division_by_zero
+divx777 divide  1.0   0.0   ->  Infinity Division_by_zero
+divx778 divide  1.0  -0.0   -> -Infinity Division_by_zero
+
+divx1021  divide 1E0          1E0 -> 1
+divx1022  divide 1E0          2E0 -> 0.5
+divx1024  divide 100E-2   1000E-3 -> 1
+divx1025  divide 24E-1        2E0 -> 1.2
+divx1026  divide 2400E-3      2E0 -> 1.200
+divx1027  divide 5E0          2E0 -> 2.5
+divx1028  divide 5E0        20E-1 -> 2.5
+divx1029  divide 5E0      2000E-3 -> 2.5
+divx1030  divide 5E0         2E-1 -> 25
+divx1031  divide 5E0        20E-2 -> 25
+divx1032  divide 480E-2       3E0 -> 1.60
+divx1033  divide 47E-1        2E0 -> 2.35
+
+precision:   MAX_PREC
+rounding:    floor
+maxExponent: MAX_EMAX
+minexponent: MIN_EMIN
+
+covx10101 divide 99960 -8.4E+2 -> -119.0
diff --git a/tests/testdata_dist/powmod.decTest b/tests/testdata_dist/powmod.decTest
new file mode 100644 (file)
index 0000000..566ac26
--- /dev/null
@@ -0,0 +1,11 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+precision: 300
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+pmod0  powmod  7247904686162156268756641871543644042277623398853554468867975319666037255091265943032239093584347186840121801614360964715652905098992835894184096312341779376823207799350069002402412482877974177233546988057101295882961124291232141624373889565162488116166423046293656727734178579205421906196474298466264071397384981078088815708020547567692908284576079431039273026512391195375268504826615611695961562278978710576401808163854217637647917342787283928090945990324408427  72911381473307811290418643363695136875276855271908308737368782518679837994154352406227698386756871608461870214874280926511589848849509  31906341065580964484578663620717058806515367511816490829946418525273500969682346304935113670002560820475247356732823750538744195557414833644061144544241638354271446743426604392102101801937477784637031313407445437800551648717678380666928485445940200364432619268497489262492344529240796301572454958839525931754442716050812573815074211063046917762775052046523027021740926   ->  NaN  Invalid_operation 
+pmod1  powmod  59816342012942315098970462658792059892275264984666990446685694836081374853969464705719653121102559161063448464450002818593554666036972  1808440009884288651308444897630656534698586825767821618301948133780421907801717966360865675146296117338393174811466066268142506120020368981269222935839579136181190608002826379175231653484083965817749509752489756776920528193757984279793894488949075815424261779157359579298699535689561175771493220024997103730141269778525271810651437708512930946335604586826191007094827137940404116546099548730264289007279419487949687312989144242506906630322687986264669543618191810165243349766739960425031531762481513737729696609184323341159543939333974610146195474375073717006915780114030367865416102826534713708505196953254429653890718217760115162556533325875262622658650892203751800063944733453791111519247757916131707317329186318624  5441234431046630110640268228481505590553050843321198167278506680894253863154515922871385873365865160472294826298674650   ->  1868917102694134207252102040168102130107084437203870633712404767805533972261967368751886815417341648241691466033330156  
diff --git a/tests/testdata_dist/powmod_eq.decTest b/tests/testdata_dist/powmod_eq.decTest
new file mode 100644 (file)
index 0000000..13e4d49
--- /dev/null
@@ -0,0 +1,15 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+rounding: half_even
+minExponent: -999999999
+maxExponent: 999999999
+
+precision: 241
+powmod_eq_eq_op0  powmod_eq_eq_op  5172935023082998  61096828599662907  ->  39312309313006360 
+precision: 154
+powmod_eq_eq_op1  powmod_eq_eq_op  501984777  951714845  ->  361293017 
+precision: 53
+powmod_eq_eq_op2  powmod_eq_eq_op  271669153387921210695131  564448204364277556933406  ->  194556909709302006741699 
+
diff --git a/tests/testdata_dist/shiftlr.decTest b/tests/testdata_dist/shiftlr.decTest
new file mode 100644 (file)
index 0000000..3b9d221
--- /dev/null
@@ -0,0 +1,19 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+shlx0  shiftleft  0  0  ->  0
+shlx1  shiftleft  0  1  ->  0
+shlx2  shiftleft  0  2  ->  0
+shlx3  shiftleft  0  3  ->  0
+
+shlx190  shiftleft  1  90  ->  1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+shlx200  shiftleft  12  0  ->  12
+shlx201  shiftleft  12  1  ->  120
+shlx209  shiftleft  12  9  ->  12000000000
+
+shlr4800  shiftright  12123456789  0  ->  12123456789
+shlr4801  shiftright  12123456789  1  ->  1212345678
+shlr4802  shiftright  12123456789  2  ->  121234567
+shlr4810  shiftright  12123456789  10  ->  1
diff --git a/tests/testdata_dist/testruntest.decTest b/tests/testdata_dist/testruntest.decTest
new file mode 100644 (file)
index 0000000..c5e8304
--- /dev/null
@@ -0,0 +1,116 @@
+-- Selected test cases to reduce both the archive size and runtime.  For the
+-- large test suite, download the separate mpdecimal-testdata distribution and
+-- replace the testdata_dist directory.
+
+-- Quis custodiet ipsos custodes?
+-- Small sanity check that failures are indeed reported.
+
+Precision: 100
+maxExponent: 100000
+minExponent: -100000
+
+rt_should_fail000   tosci  918231.131  ->  8123813091.1
+rt_should_fail001   apply  0.0391301  ->  10E+2387432
+rt_should_fail002   toeng  1.1111e22  ->  433
+rt_should_fail003   class  NaN  ->  +Infinity
+
+rt_should_fail004   abs  -99  -> -99
+rt_should_fail005   copy  2  ->  3
+rt_should_fail006   copyabs  -2  ->  -2
+rt_should_fail007   copynegate  -2  ->  -2
+rt_should_fail008   exp  5  ->  101
+rt_should_fail009   invert  0101  ->  1111
+rt_should_fail010   invroot  35  ->  47
+rt_should_fail011   ln  101  ->  5
+rt_should_fail012   log10  200  ->  137
+rt_should_fail013   logb  1000  ->  440
+rt_should_fail014   minus  22  -> 222
+rt_should_fail015   nextminus  12838  -> 213123
+rt_should_fail016   nextplus  23132  ->  3223
+rt_should_fail017   plus  102  ->  110
+rt_should_fail018   reduce  0.21321  ->  213213892
+rt_should_fail019   squareroot  1281  ->  7
+rt_should_fail020   tointegral  1281.444  ->  1781
+rt_should_fail021   tointegralx  1281.444  ->  1783
+
+rt_should_fail022   samequantum  88 99  ->  -1
+rt_should_fail023   samequantum_eq  88  ->  -1
+
+rt_should_fail024   add  0.111 0.222  ->  -133.1381
+rt_should_fail025   and  1010101 111  ->  100000000
+rt_should_fail026   copysign  -2  3  ->  1000
+rt_should_fail027   divide  22 0.001 ->  27
+rt_should_fail028   divideint  11 22  -> 11
+rt_should_fail029   max  231 13221  -> 12312
+rt_should_fail030   maxmag  320193 12312  ->  322
+rt_should_fail031   min  1029 322323 -> 3322
+rt_should_fail032   minmag 23232 1230921  -> 1232131
+rt_should_fail033   multiply  25 25  ->  25
+rt_should_fail034   nexttoward  3893792  0.2  ->  0.21
+rt_should_fail035   or  1010101 111  ->  100000000
+rt_should_fail036   power  0.444 0.554  ->  244
+rt_should_fail037   quantize  234 22  ->  0.2342
+rt_should_fail038   remainder  0  1   -> 2
+rt_should_fail039   remaindernear  4  1   -> 774
+rt_should_fail040   rotate  2  2  ->  1000
+rt_should_fail041   scaleb  111  20  -> 22
+rt_should_fail042   shift  10  -1  -> 100
+rt_should_fail043   subtract  0.444 0.123  ->  0E-11
+rt_should_fail044   xor 11111111111 0000011  ->  10101
+
+rt_should_fail045   add_eq  0.111  ->  -133.1381
+rt_should_fail046   and_eq  1010101  ->  100000000
+rt_should_fail047   copysign_eq  -2  ->  1000
+rt_should_fail048   divide_eq  22 ->  27
+rt_should_fail049   divideint_eq  11  ->  11
+rt_should_fail050   max_eq  231  ->  12312
+rt_should_fail051   maxmag_eq  320193  ->  322
+rt_should_fail052   min_eq  1029  -> 3322
+rt_should_fail053   minmag_eq 23232  -> 1232131
+rt_should_fail054   multiply_eq  25  ->  25
+rt_should_fail055   nexttoward_eq  3893792  ->  0.21
+rt_should_fail056   or_eq  1010101  ->  100000000
+rt_should_fail057   power_eq  0.444  ->  244
+rt_should_fail058   quantize_eq  234  ->  0.2342
+rt_should_fail059   remainder_eq  5  ->  2
+rt_should_fail060   remaindernear_eq  4  ->  774
+rt_should_fail061   rotate_eq  2  ->  1000
+rt_should_fail062   scaleb_eq  111  ->  22
+rt_should_fail063   shift_eq  10  -> 100
+rt_should_fail064   subtract_eq  0.444  ->  345
+rt_should_fail065   xor_eq 11111111  ->  1111
+
+rt_should_fail066   divmod  22 8  ->  27  28
+rt_should_fail067   divmod_eq  22  ->  27  28
+
+rt_should_fail068   fma  22 11 377  ->  551
+rt_should_fail069   powmod  12 11 10  ->  27
+
+rt_should_fail070   fma_eq_eq_op  11 377  ->  551
+rt_should_fail071   powmod_eq_eq_op  11 10  ->  27
+
+rt_should_fail072   fma_op_eq_eq  11 377  ->  551
+rt_should_fail073   powmod_op_eq_eq  11 10  ->  27
+
+rt_should_fail074   fma_eq_eq_eq  11  ->  551
+rt_should_fail075   powmod_eq_eq_eq  11  ->  27
+
+rt_should_fail076   compare  102938 012938  ->  0 
+rt_should_fail077   comparesig  102938 012938  ->  0 
+rt_should_fail078   comparetotal  1239210  -2103  ->  -1
+rt_should_fail079   comparetotmag  1239210  -2103  ->  -1
+
+rt_should_fail080   compare_eq  102938  ->  1 
+rt_should_fail081   comparesig_eq  102938  ->  1 
+rt_should_fail082   comparetotal_eq  1239210  ->  -1
+rt_should_fail083   comparetotmag_eq  1239210  ->  -1
+
+rt_should_fail084   shiftleft  10239  5  ->  129
+rt_should_fail085   shiftright  1029  3  ->  32
+rt_should_fail086   baseconv  2130  ->  3432
+  
+-- status test
+
+rt_should_fail087   divide  10  0  ->  NaN
+rt_should_fail088   power  1E44  1E444444  ->  Infinity
+rt_should_fail089   remaindernear 10 6  ->  -2  Clamped Conversion_syntax Division_by_zero Division_impossible Division_undefined Fpu_error Inexact Invalid_context Invalid_operation Malloc_error Not_implemented Overflow Rounded Subnormal Underflow
diff --git a/tests/vctest.h b/tests/vctest.h
new file mode 100644 (file)
index 0000000..778f22f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2008-2025 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef LIBMPDEC_VCTEST_H_
+#define LIBMPDEC_VCTEST_H_
+
+
+/* Visual C fixes */
+#ifdef _MSC_VER
+  #undef random
+  #define random rand
+  #undef srandom
+  #define srandom srand
+  #undef strncasecmp
+  #define strncasecmp _strnicmp
+  #undef strcasecmp
+  #define strcasecmp _stricmp
+  #define strdup _strdup
+#endif
+
+/* MinGW fixes */
+#ifdef __MINGW32__
+  #undef random
+  #define random rand
+  #undef srandom
+  #define srandom srand
+#endif
+
+
+#endif /* LIBMPDEC_VCTEST_H_ */
diff --git a/vcbuild/README.txt b/vcbuild/README.txt
new file mode 100644 (file)
index 0000000..7736287
--- /dev/null
@@ -0,0 +1,69 @@
+
+
+libmpdec and libmpdec++ build instructions for Visual Studio
+============================================================
+
+   For all builds:  After a successful build, the static libraries, the dynamic
+   libraries and the header files should be in the dist64 or dist32 directory.
+
+   The unit tests attempt to download the official IBM test cases (text files).
+   No executables are downloaded.
+
+
+   64-bit release build
+   --------------------
+
+      # Clean the build directory if files from a previous build are present.
+      vcdistclean.bat
+
+      # Build the libraries.  Optionally use pgobuild64.bat instead of vcbuild64.bat
+      # for a profile-guided optimization build.
+      vcbuild64.bat
+
+      # Run the unit tests.
+      runshort.bat
+
+
+   64-bit debug build
+   ------------------
+
+      # Clean the build directory if files from a previous build are present.
+      vcdistclean.bat
+
+      # Build the libraries.
+      vcbuild64.bat /d
+
+      # Run the unit tests.
+      runshort.bat /d
+
+
+   32-bit release build
+   --------------------
+
+      # Clean the build directory if files from a previous build are present.
+      vcdistclean.bat
+
+      # Build the libraries.  Optionally use pgobuild32.bat instead of vcbuild32.bat
+      # for a profile-guided optimization build.
+      vcbuild32.bat
+
+      # Run the unit tests.
+      runshort.bat
+
+
+   32-bit debug build
+   --------------------
+
+      # Clean the build directory if files from a previous build are present.
+      vcdistclean.bat
+
+      # Build the libraries.
+      vcbuild32.bat /d
+
+      # Run the unit tests.
+      runshort.bat /d
+
+
+
+
+
diff --git a/vcbuild/pgobuild32.bat b/vcbuild/pgobuild32.bat
new file mode 100755 (executable)
index 0000000..deddbf6
--- /dev/null
@@ -0,0 +1,40 @@
+@ECHO off\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x86\r
+\r
+\r
+if not exist dist32 mkdir dist32\r
+if exist dist32\* del /q dist32\*\r
+\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=ppro profile\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist32\r
+copy /y "mpdecimal.h" ..\vcbuild\dist32\r
+\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist32\r
+copy /y "decimal.hh" ..\vcbuild\dist32\r
+\r
+\r
+cd ..\vcbuild\r
+\r
+\r
+\r
diff --git a/vcbuild/pgobuild64.bat b/vcbuild/pgobuild64.bat
new file mode 100755 (executable)
index 0000000..d0ae6e8
--- /dev/null
@@ -0,0 +1,37 @@
+@ECHO off\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64\r
+\r
+if not exist dist64 mkdir dist64\r
+if exist dist64\* del /q dist64\*\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=x64 profile\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist64\r
+copy /y "mpdecimal.h" ..\vcbuild\dist64\r
+\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist64\r
+copy /y "decimal.hh" ..\vcbuild\dist64\r
+\r
+cd ..\vcbuild\r
+\r
+\r
+\r
diff --git a/vcbuild/runshort.bat b/vcbuild/runshort.bat
new file mode 100755 (executable)
index 0000000..8337f89
--- /dev/null
@@ -0,0 +1,138 @@
+@ECHO OFF\r
+\r
+rem Release or debug build.\r
+set DBG=0\r
+if "%1%" == "/d" set DBG=1\r
+\r
+rem Build libmpdec tests.\r
+cd ..\tests\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%DBG%\r
+\r
+rem # Download the official test cases (text files).\r
+call gettests.bat\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                         libmpdec: static library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests ... ")\r
+echo.\r
+echo.\r
+runtest.exe official.decTest\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests ... ")\r
+echo.\r
+echo.\r
+runtest.exe additional.decTest\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                         libmpdec: shared library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe official.decTest\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe additional.decTest\r
+if %errorlevel% neq 0 goto out\r
+\r
+\r
+rem Build libmpdec++ tests.\r
+cd ..\tests++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%DBG%\r
+\r
+rem # Copy or download the official test cases (text files).\r
+call gettests.bat\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                        libmpdec++: static library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests ... ")\r
+echo.\r
+echo.\r
+runtest.exe official.topTest --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests ... ")\r
+echo.\r
+echo.\r
+runtest.exe additional.topTest --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (single thread) ... ")\r
+echo.\r
+echo.\r
+apitest.exe\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (threaded) ... ")\r
+echo.\r
+echo.\r
+apitest.exe --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                        libmpdec++: shared library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe official.topTest --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe additional.topTest --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (single thread) ... ")\r
+echo.\r
+echo.\r
+apitest_shared.exe\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (threaded) ... ")\r
+echo.\r
+echo.\r
+apitest_shared.exe --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+\r
+:out\r
+set exitcode=%errorlevel%\r
+cd ..\vcbuild\r
+exit /b %exitcode%\r
diff --git a/vcbuild/runshort_alloc.bat b/vcbuild/runshort_alloc.bat
new file mode 100755 (executable)
index 0000000..cf16234
--- /dev/null
@@ -0,0 +1,140 @@
+@ECHO OFF\r
+\r
+\r
+rem Choose release or debug build.\r
+set DBG=0\r
+if "%1%" == "/d" set DBG=1\r
+\r
+rem Build libmpdec tests.\r
+cd ..\tests\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%DBG%\r
+\r
+\r
+rem # Download the official test cases (text files).\r
+call gettests.bat\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                         libmpdec: static library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest.exe official.decTest --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest.exe additional.decTest --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                         libmpdec: shared library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe official.decTest --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe additional.decTest --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+\r
+rem Build libmpdec++ tests.\r
+cd ..\tests++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%DBG%\r
+\r
+rem # Copy or download the official test cases (text files).\r
+call gettests.bat\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                        libmpdec++: static library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest.exe official.topTest --thread --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest.exe additional.topTest --thread --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (single thread) ... ")\r
+echo.\r
+echo.\r
+apitest.exe\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (threaded) ... ")\r
+echo.\r
+echo.\r
+apitest.exe --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+echo # ========================================================================\r
+echo #                        libmpdec++: shared library\r
+echo # ========================================================================\r
+echo.\r
+\r
+echo.\r
+<nul (set /p x="Running official tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe official.topTest --thread --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running additional tests with allocation failures ... ")\r
+echo.\r
+echo.\r
+runtest_shared.exe additional.topTest --thread --alloc\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (single thread) ... ")\r
+echo.\r
+echo.\r
+apitest_shared.exe\r
+if %errorlevel% neq 0 goto out\r
+\r
+echo.\r
+<nul (set /p x="Running API tests (threaded) ... ")\r
+echo.\r
+echo.\r
+apitest_shared.exe --thread\r
+if %errorlevel% neq 0 goto out\r
+\r
+\r
+:out\r
+set exitcode=%errorlevel%\r
+cd ..\vcbuild\r
+exit /b %exitcode%\r
diff --git a/vcbuild/vcbuild32.bat b/vcbuild/vcbuild32.bat
new file mode 100755 (executable)
index 0000000..1402aec
--- /dev/null
@@ -0,0 +1,48 @@
+@ECHO off\r
+\r
+set dbg=0\r
+set machine=ppro\r
+\r
+:getopt\r
+if "%~1"=="/d" (set dbg=1) & shift & goto getopt\r
+if "%~1"=="/m" (set machine=%2) & shift & shift & goto getopt\r
+\r
+rem These options are for testing only, always use 'ppro' (the default).  The\r
+rem 'ansi-legacy' option has been removed since it is not needed on Windows.\r
+if "%machine%"=="ppro" goto success\r
+if "%machine%"=="ansi32" goto success\r
+echo "valid values for /m are [ppro, ansi32]"\r
+exit /b 1\r
+:success\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x86\r
+\r
+if not exist dist32 mkdir dist32\r
+if exist dist32\* del /q dist32\*\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=%machine% DEBUG=%dbg%\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist32\r
+copy /y "mpdecimal.h" ..\vcbuild\dist32\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%dbg%\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist32\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist32\r
+copy /y "decimal.hh" ..\vcbuild\dist32\r
+\r
+cd ..\vcbuild\r
diff --git a/vcbuild/vcbuild64.bat b/vcbuild/vcbuild64.bat
new file mode 100755 (executable)
index 0000000..663a989
--- /dev/null
@@ -0,0 +1,50 @@
+@ECHO off\r
+\r
+set dbg=0\r
+set machine=x64\r
+\r
+:getopt\r
+if "%~1"=="/d" (set dbg=1) & shift & goto getopt\r
+if "%~1"=="/m" (set machine=%2) & shift & shift & goto getopt\r
+\r
+if "%machine%"=="x64" goto success\r
+if "%machine%"=="ansi64" goto success\r
+echo "valid values for /m are [x64, ansi64]"\r
+exit /b 1\r
+:success\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64\r
+\r
+if not exist dist64 mkdir dist64\r
+if exist dist64\* del /q dist64\*\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=%machine% DEBUG=%dbg%\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist64\r
+copy /y "mpdecimal.h" ..\vcbuild\dist64\r
+\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%dbg%\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist64\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist64\r
+copy /y "decimal.hh" ..\vcbuild\dist64\r
+\r
+cd ..\vcbuild\r
+\r
+\r
+\r
diff --git a/vcbuild/vcbuild_arm32.bat b/vcbuild/vcbuild_arm32.bat
new file mode 100755 (executable)
index 0000000..aba17fa
--- /dev/null
@@ -0,0 +1,38 @@
+@ECHO off\r
+\r
+set dbg=0\r
+set machine=ansi32\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64_arm\r
+\r
+if not exist dist_arm32 mkdir dist_arm32\r
+if exist dist_arm32\* del /q dist_arm32\*\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=%machine% DEBUG=%dbg%\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist_arm32\r
+copy /y "mpdecimal.h" ..\vcbuild\dist_arm32\r
+\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%dbg%\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist_arm32\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist_arm32\r
+copy /y "decimal.hh" ..\vcbuild\dist_arm32\r
+\r
+cd ..\vcbuild\r
+\r
diff --git a/vcbuild/vcbuild_arm64.bat b/vcbuild/vcbuild_arm64.bat
new file mode 100755 (executable)
index 0000000..15de140
--- /dev/null
@@ -0,0 +1,37 @@
+@ECHO off\r
+\r
+set dbg=0\r
+set machine=ansi64\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64_arm64\r
+\r
+if not exist dist_arm64 mkdir dist_arm64\r
+if exist dist_arm64\* del /q dist_arm64\*\r
+\r
+cd ..\libmpdec\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake MACHINE=%machine% DEBUG=%dbg%\r
+\r
+copy /y "libmpdec-4.0.1.lib" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec-4.0.1.dll" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec-4.0.1.dll.lib" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec-4.0.1.dll.exp" ..\vcbuild\dist_arm64\r
+copy /y "mpdecimal.h" ..\vcbuild\dist_arm64\r
+\r
+\r
+cd ..\libmpdec++\r
+copy /y Makefile.vc Makefile\r
+nmake clean\r
+nmake DEBUG=%dbg%\r
+\r
+copy /y "libmpdec++-4.0.1.lib" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec++-4.0.1.dll" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec++-4.0.1.dll.lib" ..\vcbuild\dist_arm64\r
+copy /y "libmpdec++-4.0.1.dll.exp" ..\vcbuild\dist_arm64\r
+copy /y "decimal.hh" ..\vcbuild\dist_arm64\r
+\r
+cd ..\vcbuild\r
diff --git a/vcbuild/vcclean.bat b/vcbuild/vcclean.bat
new file mode 100755 (executable)
index 0000000..f8e76c8
--- /dev/null
@@ -0,0 +1,24 @@
+@ECHO off\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64\r
+\r
+cd ..\libmpdec\r
+if exist Makefile nmake clean\r
+\r
+cd ..\libmpdec++\r
+if exist Makefile nmake clean\r
+\r
+cd ..\tests\r
+if exist Makefile nmake clean\r
+\r
+cd ..\tests++\r
+if exist Makefile nmake clean\r
+\r
+cd ..\vcbuild\r
+if exist dist64 rd /q /s dist64\r
+if exist dist32 rd /q /s dist32\r
+if exist dist_arm64 rd /q /s dist_arm64\r
+if exist dist_arm32 rd /q /s dist_arm32\r
diff --git a/vcbuild/vcdistclean.bat b/vcbuild/vcdistclean.bat
new file mode 100755 (executable)
index 0000000..0c561e9
--- /dev/null
@@ -0,0 +1,29 @@
+@ECHO off\r
+\r
+"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt\r
+set /p vcpath=<vcpath.txt\r
+del vcpath.txt\r
+call "%vcpath%\VC\Auxiliary\Build\vcvarsall.bat" x64\r
+\r
+cd ..\libmpdec\r
+if exist Makefile nmake distclean\r
+\r
+cd ..\libmpdec++\r
+if exist Makefile nmake distclean\r
+\r
+cd ..\tests\r
+if exist Makefile nmake distclean\r
+\r
+cd ..\tests++\r
+if exist Makefile nmake distclean\r
+\r
+cd ..\vcbuild\r
+if exist additional.decTest del /q additional.decTest\r
+if exist official.decTest del /q official.decTest\r
+if exist dectest.zip del /q dectest.zip\r
+if exist dist64 rd /q /s dist64\r
+if exist dist32 rd /q /s dist32\r
+if exist dist_arm64 rd /q /s dist_arm64\r
+if exist dist_arm32 rd /q /s dist_arm32\r
+if exist testdata rd /q /s testdata\r
+\r