From cffe50c7abb6e0da341047e9f58e187165cd4eb7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sun, 9 Feb 2025 11:41:21 +0100 Subject: [PATCH] Import mpdecimal_4.0.0.orig.tar.gz [dgit import orig mpdecimal_4.0.0.orig.tar.gz] --- CHANGELOG.txt | 480 ++ COPYRIGHT.txt | 23 + INSTALL.txt | 95 + Makefile.in | 161 + README.txt | 53 + config.guess | 1803 +++++ config.h.in | 111 + config.sub | 1895 +++++ configure | 6842 ++++++++++++++++ configure.ac | 1110 +++ doc/COPYRIGHT.txt | 60 + doc/libmpdec++.3 | 32 + doc/libmpdec.3 | 32 + doc/mpdecimal.3 | 32 + install-sh | 541 ++ libmpdec++/.objs/README.txt | 3 + libmpdec++/.pc/libmpdec++.pc.in | 12 + libmpdec++/.profile/train.sh | 35 + libmpdec++/Makefile.in | 181 + libmpdec++/Makefile.vc | 99 + libmpdec++/bench.cc | 103 + libmpdec++/bench_full.cc | 144 + libmpdec++/decimal.cc | 324 + libmpdec++/decimal.hh | 1289 +++ libmpdec++/examples/factorial.cc | 111 + libmpdec++/examples/pi.cc | 108 + libmpdec/.objs/README.txt | 8 + libmpdec/.objs/symbols32.exp | 343 + libmpdec/.objs/symbols64.exp | 347 + libmpdec/.pc/libmpdec.pc.in | 11 + libmpdec/.profile/train.sh | 35 + libmpdec/Makefile.in | 315 + libmpdec/Makefile.vc | 288 + libmpdec/README.txt | 84 + libmpdec/basearith.c | 649 ++ libmpdec/basearith.h | 217 + libmpdec/bench.c | 135 + libmpdec/bench_full.c | 192 + libmpdec/bits.h | 188 + libmpdec/constants.c | 129 + libmpdec/constants.h | 88 + libmpdec/context.c | 285 + libmpdec/convolute.c | 172 + libmpdec/convolute.h | 48 + libmpdec/crt.c | 178 + libmpdec/crt.h | 45 + libmpdec/difradix2.c | 171 + libmpdec/difradix2.h | 46 + libmpdec/examples/README.txt | 7 + libmpdec/examples/compare.c | 76 + libmpdec/examples/div.c | 76 + libmpdec/examples/divmod.c | 79 + libmpdec/examples/multiply.c | 76 + libmpdec/examples/pow.c | 76 + libmpdec/examples/powmod.c | 79 + libmpdec/examples/shift.c | 76 + libmpdec/examples/sqrt.c | 73 + libmpdec/fnt.c | 77 + libmpdec/fnt.h | 46 + libmpdec/fourstep.c | 242 + libmpdec/fourstep.h | 46 + libmpdec/io.c | 1610 ++++ libmpdec/io.h | 61 + libmpdec/literature/REFERENCES.txt | 55 + libmpdec/literature/bignum.txt | 83 + libmpdec/literature/fnt.py | 207 + libmpdec/literature/matrix-transform.txt | 256 + libmpdec/literature/mulmod-64.txt | 127 + libmpdec/literature/mulmod-ppro.txt | 266 + libmpdec/literature/six-step.txt | 63 + libmpdec/literature/umodarith.lisp | 690 ++ libmpdec/mpalloc.c | 347 + libmpdec/mpalloc.h | 53 + libmpdec/mpdecimal.c | 9155 ++++++++++++++++++++++ libmpdec/mpdecimal.h.in | 804 ++ libmpdec/mpdecimal32vc.h | 762 ++ libmpdec/mpdecimal64vc.h | 768 ++ libmpdec/mpsignal.c | 966 +++ libmpdec/numbertheory.c | 129 + libmpdec/numbertheory.h | 75 + libmpdec/sixstep.c | 212 + libmpdec/sixstep.h | 46 + libmpdec/transpose.c | 275 + libmpdec/transpose.h | 60 + libmpdec/typearith.h | 661 ++ libmpdec/umodarith.h | 645 ++ libmpdec/vcdiv64.asm | 47 + tests++/Makefile.in | 90 + tests++/Makefile.vc | 79 + tests++/README.txt | 27 + tests++/additional.topTest | 30 + tests++/apitest.cc | 2861 +++++++ tests++/gettests.bat | 9 + tests++/gettests.sh | 5 + tests++/official.topTest | 163 + tests++/runshort.sh | 75 + tests++/runshort_alloc.sh | 72 + tests++/runtest.cc | 3210 ++++++++ tests++/test.cc | 269 + tests++/test.hh | 171 + tests++/vctest.hh | 53 + tests/Makefile.in | 59 + tests/Makefile.vc | 59 + tests/README.txt | 27 + tests/additional.decTest | 31 + tests/gettests.bat | 16 + tests/gettests.sh | 25 + tests/official.decTest | 163 + tests/runshort.sh | 62 + tests/runshort_alloc.sh | 58 + tests/runtest.c | 5649 +++++++++++++ tests/test.c | 270 + tests/test.h | 53 + tests/testdata_dist/baseconv.decTest | 53 + tests/testdata_dist/binop_eq.decTest | 120 + tests/testdata_dist/cov.decTest | 277 + tests/testdata_dist/divmod.decTest | 19 + tests/testdata_dist/divmod_eq.decTest | 19 + tests/testdata_dist/extra.decTest | 10 + tests/testdata_dist/fma_eq.decTest | 19 + tests/testdata_dist/format.decTest | 133 + tests/testdata_dist/getint.decTest | 307 + tests/testdata_dist/invroot.decTest | 19 + tests/testdata_dist/largeint.decTest | 10 + tests/testdata_dist/maxprec.decTest | 472 ++ tests/testdata_dist/powmod.decTest | 11 + tests/testdata_dist/powmod_eq.decTest | 15 + tests/testdata_dist/shiftlr.decTest | 19 + tests/testdata_dist/testruntest.decTest | 116 + tests/vctest.h | 54 + vcbuild/README.txt | 69 + vcbuild/pgobuild32.bat | 40 + vcbuild/pgobuild64.bat | 37 + vcbuild/runshort.bat | 138 + vcbuild/runshort_alloc.bat | 140 + vcbuild/vcbuild32.bat | 48 + vcbuild/vcbuild64.bat | 50 + vcbuild/vcbuild_arm32.bat | 38 + vcbuild/vcbuild_arm64.bat | 37 + vcbuild/vcclean.bat | 24 + vcbuild/vcdistclean.bat | 29 + 141 files changed, 55014 insertions(+) create mode 100644 CHANGELOG.txt create mode 100644 COPYRIGHT.txt create mode 100644 INSTALL.txt create mode 100644 Makefile.in create mode 100644 README.txt create mode 100755 config.guess create mode 100644 config.h.in create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.ac create mode 100644 doc/COPYRIGHT.txt create mode 100644 doc/libmpdec++.3 create mode 100644 doc/libmpdec.3 create mode 100644 doc/mpdecimal.3 create mode 100755 install-sh create mode 100644 libmpdec++/.objs/README.txt create mode 100644 libmpdec++/.pc/libmpdec++.pc.in create mode 100755 libmpdec++/.profile/train.sh create mode 100644 libmpdec++/Makefile.in create mode 100644 libmpdec++/Makefile.vc create mode 100644 libmpdec++/bench.cc create mode 100644 libmpdec++/bench_full.cc create mode 100644 libmpdec++/decimal.cc create mode 100644 libmpdec++/decimal.hh create mode 100644 libmpdec++/examples/factorial.cc create mode 100644 libmpdec++/examples/pi.cc create mode 100644 libmpdec/.objs/README.txt create mode 100644 libmpdec/.objs/symbols32.exp create mode 100644 libmpdec/.objs/symbols64.exp create mode 100644 libmpdec/.pc/libmpdec.pc.in create mode 100755 libmpdec/.profile/train.sh create mode 100644 libmpdec/Makefile.in create mode 100644 libmpdec/Makefile.vc create mode 100644 libmpdec/README.txt create mode 100644 libmpdec/basearith.c create mode 100644 libmpdec/basearith.h create mode 100644 libmpdec/bench.c create mode 100644 libmpdec/bench_full.c create mode 100644 libmpdec/bits.h create mode 100644 libmpdec/constants.c create mode 100644 libmpdec/constants.h create mode 100644 libmpdec/context.c create mode 100644 libmpdec/convolute.c create mode 100644 libmpdec/convolute.h create mode 100644 libmpdec/crt.c create mode 100644 libmpdec/crt.h create mode 100644 libmpdec/difradix2.c create mode 100644 libmpdec/difradix2.h create mode 100644 libmpdec/examples/README.txt create mode 100644 libmpdec/examples/compare.c create mode 100644 libmpdec/examples/div.c create mode 100644 libmpdec/examples/divmod.c create mode 100644 libmpdec/examples/multiply.c create mode 100644 libmpdec/examples/pow.c create mode 100644 libmpdec/examples/powmod.c create mode 100644 libmpdec/examples/shift.c create mode 100644 libmpdec/examples/sqrt.c create mode 100644 libmpdec/fnt.c create mode 100644 libmpdec/fnt.h create mode 100644 libmpdec/fourstep.c create mode 100644 libmpdec/fourstep.h create mode 100644 libmpdec/io.c create mode 100644 libmpdec/io.h create mode 100644 libmpdec/literature/REFERENCES.txt create mode 100644 libmpdec/literature/bignum.txt create mode 100644 libmpdec/literature/fnt.py create mode 100644 libmpdec/literature/matrix-transform.txt create mode 100644 libmpdec/literature/mulmod-64.txt create mode 100644 libmpdec/literature/mulmod-ppro.txt create mode 100644 libmpdec/literature/six-step.txt create mode 100644 libmpdec/literature/umodarith.lisp create mode 100644 libmpdec/mpalloc.c create mode 100644 libmpdec/mpalloc.h create mode 100644 libmpdec/mpdecimal.c create mode 100644 libmpdec/mpdecimal.h.in create mode 100644 libmpdec/mpdecimal32vc.h create mode 100644 libmpdec/mpdecimal64vc.h create mode 100644 libmpdec/mpsignal.c create mode 100644 libmpdec/numbertheory.c create mode 100644 libmpdec/numbertheory.h create mode 100644 libmpdec/sixstep.c create mode 100644 libmpdec/sixstep.h create mode 100644 libmpdec/transpose.c create mode 100644 libmpdec/transpose.h create mode 100644 libmpdec/typearith.h create mode 100644 libmpdec/umodarith.h create mode 100644 libmpdec/vcdiv64.asm create mode 100644 tests++/Makefile.in create mode 100644 tests++/Makefile.vc create mode 100644 tests++/README.txt create mode 100644 tests++/additional.topTest create mode 100644 tests++/apitest.cc create mode 100644 tests++/gettests.bat create mode 100755 tests++/gettests.sh create mode 100644 tests++/official.topTest create mode 100755 tests++/runshort.sh create mode 100755 tests++/runshort_alloc.sh create mode 100644 tests++/runtest.cc create mode 100644 tests++/test.cc create mode 100644 tests++/test.hh create mode 100644 tests++/vctest.hh create mode 100644 tests/Makefile.in create mode 100644 tests/Makefile.vc create mode 100644 tests/README.txt create mode 100644 tests/additional.decTest create mode 100755 tests/gettests.bat create mode 100755 tests/gettests.sh create mode 100644 tests/official.decTest create mode 100755 tests/runshort.sh create mode 100755 tests/runshort_alloc.sh create mode 100644 tests/runtest.c create mode 100644 tests/test.c create mode 100644 tests/test.h create mode 100644 tests/testdata_dist/baseconv.decTest create mode 100644 tests/testdata_dist/binop_eq.decTest create mode 100644 tests/testdata_dist/cov.decTest create mode 100644 tests/testdata_dist/divmod.decTest create mode 100644 tests/testdata_dist/divmod_eq.decTest create mode 100644 tests/testdata_dist/extra.decTest create mode 100644 tests/testdata_dist/fma_eq.decTest create mode 100644 tests/testdata_dist/format.decTest create mode 100644 tests/testdata_dist/getint.decTest create mode 100644 tests/testdata_dist/invroot.decTest create mode 100644 tests/testdata_dist/largeint.decTest create mode 100644 tests/testdata_dist/maxprec.decTest create mode 100644 tests/testdata_dist/powmod.decTest create mode 100644 tests/testdata_dist/powmod_eq.decTest create mode 100644 tests/testdata_dist/shiftlr.decTest create mode 100644 tests/testdata_dist/testruntest.decTest create mode 100644 tests/vctest.h create mode 100644 vcbuild/README.txt create mode 100755 vcbuild/pgobuild32.bat create mode 100755 vcbuild/pgobuild64.bat create mode 100755 vcbuild/runshort.bat create mode 100755 vcbuild/runshort_alloc.bat create mode 100755 vcbuild/vcbuild32.bat create mode 100755 vcbuild/vcbuild64.bat create mode 100755 vcbuild/vcbuild_arm32.bat create mode 100755 vcbuild/vcbuild_arm64.bat create mode 100755 vcbuild/vcclean.bat create mode 100755 vcbuild/vcdistclean.bat diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..da57e11 --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,480 @@ + + +Changelog +========= + +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 `_ + + 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 `_ + + **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 index 0000000..a78c3f6 --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,23 @@ +Copyright (c) 2008-2024 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 index 0000000..ca18bf7 --- /dev/null +++ b/INSTALL.txt @@ -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 index 0000000..cb5f13c --- /dev/null +++ b/Makefile.in @@ -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 index 0000000..3f7fc72 --- /dev/null +++ b/README.txt @@ -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 + + diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..b187213 --- /dev/null +++ b/config.guess @@ -0,0 +1,1803 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2023 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-07-20' + +# 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 . +# +# 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 . + + +# 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 ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2023 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 c89 c99 ; 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" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #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 /* 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 + + 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 + #include + + 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 + 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=`/usr/bin/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' /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 + GUESS=i586-unisys-sysv4 + ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # 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 + ;; +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" < +#include +#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 +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +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 + 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 +#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 <&2 <&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 index 0000000..0692f6f --- /dev/null +++ b/config.h.in @@ -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 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 header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the 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 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 , + , or 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 , + , or 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 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 index 0000000..c4e5853 --- /dev/null +++ b/config.sub @@ -0,0 +1,1895 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2023 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-07-31' + +# 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 . +# +# 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 . +# +# 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 ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2023 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 +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&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 + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova* | managarm-* \ + | 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 + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + 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 + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + 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 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 + ;; + 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 + case $basic_os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + basic_os=nextstep2 + ;; + *) + basic_os=nextstep3 + ;; + esac + ;; + 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/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&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. +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|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&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 in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* | linux-mlibc* ) + ;; + uclinux-uclibc* ) + ;; + managarm-mlibc* | managarm-kernel* ) + ;; + windows*-gnu* | windows*-msvc*) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* | -mlibc* ) + # 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* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + none-coff* | none-elf*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an output format "OS" + ;; + -*) + # Blank kernel with real OS 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*) + 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" +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 index 0000000..51750a2 --- /dev/null +++ b/configure @@ -0,0 +1,6842 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for mpdecimal 4.0.0. +# +# Report bugs to . +# +# +# Copyright (c) 2008-2024 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 &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.0' +PACKAGE_STRING='mpdecimal 4.0.0' +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 +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#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 +LD +LDXXFLAGS +LDXX +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 +runstatedir +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 +LDXX +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' +runstatedir='${localstatedir}/run' +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 ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -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 runstatedir +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.0 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] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --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.0:";; + 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 if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + MACHINE force configuration: x64, uint128, ansi64, ppro, ansi32, + ansi-legacy, universal + LDXX C++ linker (default is $CXX) + 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 home page: . +_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.0 +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-2024 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 +#include +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 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.0, 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 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.0.dylib" + CONFIGURE_LDFLAGS="-dynamiclib $FPIC -install_name @rpath/$LIBSONAME -compatibility_version 4.0 -current_version 4.0.0" + ;; + *-*-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" + ;; + *-*-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.0" + 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.0.dylib" + CONFIGURE_LDXXFLAGS="-dynamiclib $FPIC -install_name @rpath/$LIBSONAME_CXX -compatibility_version 4.0 -current_version 4.0.0" + ;; + *-*-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" + ;; + *-*-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.0" + 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" + +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 +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 +#include +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 + LD="$CC" + CXX=icpc + LDXX=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 + LD="$CC" + 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 + + + + +if test -z "$LD"; then + LD="$CC" +fi +if test -z "$LDXX"; then + LDXX="$CXX" +fi + + + + + + + + + + +# ============================================================================== +# 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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#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 +#include +#include +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.0, 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 home page: ." + +_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.0 +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` +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 +' >$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 = "" + +} +{ + 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 +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\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 index 0000000..bee070f --- /dev/null +++ b/configure.ac @@ -0,0 +1,1110 @@ +AC_COPYRIGHT([ +Copyright (c) 2008-2024 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.0], + [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 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.0.dylib" + CONFIGURE_LDFLAGS="-dynamiclib $FPIC -install_name @rpath/$LIBSONAME -compatibility_version 4.0 -current_version 4.0.0" + ;; + *-*-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" + ;; + *-*-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.0" + 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.0.dylib" + CONFIGURE_LDXXFLAGS="-dynamiclib $FPIC -install_name @rpath/$LIBSONAME_CXX -compatibility_version 4.0 -current_version 4.0.0" + ;; + *-*-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" + ;; + *-*-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.0" + 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" + +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 + LD="$CC" + CXX=icpc + LDXX=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 + LD="$CC" + 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(LDXX, [C++ linker (default is $CXX)]) +AC_ARG_VAR(LDXXFLAGS, [C++ linker flags]) + +if test -z "$LD"; then + LD="$CC" +fi +if test -z "$LDXX"; then + LDXX="$CXX" +fi + +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 +#include +#include +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 index 0000000..fd3c276 --- /dev/null +++ b/doc/COPYRIGHT.txt @@ -0,0 +1,60 @@ + +SOURCE CODE LICENSE +=================== + +Copyright (c) 2008-2024 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-2024 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 index 0000000..6a5bdbd --- /dev/null +++ b/doc/libmpdec++.3 @@ -0,0 +1,32 @@ +.TH LIBMPDEC++ 3 2024-01-10 mpdecimal-4.0.0 "Programmer's Manual" +.ft C +. +.SH NAME +libmpdec++ \- 4.0.0 +. +.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 index 0000000..39df609 --- /dev/null +++ b/doc/libmpdec.3 @@ -0,0 +1,32 @@ +.TH LIBMPDEC 3 2024-01-10 mpdecimal-4.0.0 "Programmer's Manual" +.ft C +. +.SH NAME +libmpdec \- 4.0.0 +. +.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 index 0000000..a4537ab --- /dev/null +++ b/doc/mpdecimal.3 @@ -0,0 +1,32 @@ +.TH MPDECIMAL 3 2024-01-10 mpdecimal-4.0.0 "Programmer's Manual" +.ft C +. +.SH NAME +mpdecimal \- 4.0.0 +. +.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 index 0000000..ec298b5 --- /dev/null +++ b/install-sh @@ -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 index 0000000..c46d57e --- /dev/null +++ b/libmpdec++/.objs/README.txt @@ -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 index 0000000..91a21ca --- /dev/null +++ b/libmpdec++/.pc/libmpdec++.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libmpdec++ +Description: C++ library for decimal floating point arithmetic +Version: 4.0.0 +URL: https://www.bytereef.org +Requires: libmpdec = 4.0.0 +Cflags: -I${includedir} +Libs: -L${libdir} -lmpdec++ -pthread diff --git a/libmpdec++/.profile/train.sh b/libmpdec++/.profile/train.sh new file mode 100755 index 0000000..1f2c738 --- /dev/null +++ b/libmpdec++/.profile/train.sh @@ -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 index 0000000..97005f1 --- /dev/null +++ b/libmpdec++/Makefile.in @@ -0,0 +1,181 @@ + +# ============================================================================== +# Unix Makefile for libmpdec++ +# ============================================================================== + +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 index 0000000..5985c33 --- /dev/null +++ b/libmpdec++/Makefile.vc @@ -0,0 +1,99 @@ + +# ====================================================================== +# Visual C (nmake) Makefile for libmpdec++ +# ====================================================================== + +SRCDIR = ..\libmpdec + +LIBSTATIC = libmpdec-4.0.0.lib +LIBIMPORT = libmpdec-4.0.0.dll.lib +LIBSHARED = libmpdec-4.0.0.dll + +LIBSTATIC_CXX = libmpdec++-4.0.0.lib +LIBIMPORT_CXX = libmpdec++-4.0.0.dll.lib +LIBSHARED_CXX = libmpdec++-4.0.0.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 index 0000000..e61cd27 --- /dev/null +++ b/libmpdec++/bench.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include +#include +#include + +#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 index 0000000..d21dcaa --- /dev/null +++ b/libmpdec++/bench_full.cc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include +#include + +#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 index 0000000..41e90f5 --- /dev/null +++ b/libmpdec++/decimal.cc @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2020-2024 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 + +#include +#include +#include + +#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 +void +raise(const std::string& msg) +{ + throw T(msg); +} + +const cmap signal_map[] = { + { MPD_IEEE_Invalid_operation, "IEEEInvalidOperation", "decimal::IEEEInvalidOperation", raise }, + { MPD_Division_by_zero, "DivisionByZero", "decimal::DivisionByZero", raise }, + { MPD_Overflow, "Overflow", "decimal::Overflow", raise }, + { MPD_Underflow, "Underflow", "decimal::Underflow", raise }, + { MPD_Subnormal, "Subnormal", "decimal::Subnormal", raise }, + { MPD_Inexact, "Inexact", "decimal::Inexact", raise }, + { MPD_Rounded, "Rounded", "decimal::Rounded", raise }, + { MPD_Clamped, "Clamped", "decimal::Clamped", raise }, + { UINT32_MAX, nullptr, nullptr, nullptr } +}; + +const cmap cond_map[] = { + { MPD_Invalid_operation, "InvalidOperation", "decimal::InvalidOperation", raise }, + { MPD_Conversion_syntax, "ConversionSyntax", "decimal::ConversionSyntax", raise }, + { MPD_Division_impossible, "DivisionImpossible", "decimal::DivisionImpossible", raise }, + { MPD_Division_undefined, "DivisionUndefined", "decimal::DivisionUndefined", raise }, + { 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(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 index 0000000..9040290 --- /dev/null +++ b/libmpdec++/decimal.hh @@ -0,0 +1,1289 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +inline dest_t +safe_downcast(src_t v) { + if (v < std::numeric_limits::min() || + v > std::numeric_limits::max()) { + throw ValueError("cast changes the original value"); + } + + return static_cast(v); +} + +inline std::shared_ptr +shared_cp(const char *cp) { + if (cp == nullptr) { + throw RuntimeError("util::shared_cp: invalid nullptr argument"); + } + + return std::shared_ptr(cp, [](const char *s){ mpd_free(const_cast(s)); }); +} + +inline std::string +string_from_cp(const char *cp) { + const auto p = shared_cp(cp); + return std::string(p.get()); +} + +template +struct int64_compat { +#define INT64_SUBSET(T) \ + (INT64_MIN <= std::numeric_limits::min() && std::numeric_limits::max() <= INT64_MAX) + + static const bool value = std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + (std::is_same::value && INT64_SUBSET(signed char)) || + (std::is_same::value && INT64_SUBSET(short)) || + (std::is_same::value && INT64_SUBSET(int)) || + (std::is_same::value && INT64_SUBSET(long)) || + (std::is_same::value && INT64_SUBSET(long long)); +}; + +template +struct uint64_compat { +#define UINT64_SUBSET(T) (std::numeric_limits::max() <= UINT64_MAX) + + static const bool value = std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + (std::is_same::value && UINT64_SUBSET(unsigned char)) || + (std::is_same::value && UINT64_SUBSET(unsigned short)) || + (std::is_same::value && UINT64_SUBSET(unsigned int)) || + (std::is_same::value && UINT64_SUBSET(unsigned long)) || + (std::is_same::value && UINT64_SUBSET(unsigned long long)); +}; + +#define ENABLE_IF_SIGNED(T) \ + template::value>::type> + +#define ENABLE_IF_UNSIGNED(T) \ + template::value>::type, typename = void> + +#define ENABLE_IF_INTEGRAL(T) \ + template::value || util::uint64_compat::value>::type> + +#define ASSERT_SIGNED(T) \ + static_assert(util::int64_compat::value, \ + "internal error: selected type is not int64 compatible") + +#define ASSERT_UNSIGNED(T) \ + static_assert(util::uint64_compat::value, \ + "internal error: selected type is not uint64 compatible") + +#define ASSERT_INTEGRAL(T) \ + static_assert(util::int64_compat::value || util::uint64_compat::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(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(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 divmod(const Decimal& other, Context& c=context) const { + std::pair 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(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(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(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(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 index 0000000..0af4065 --- /dev/null +++ b/libmpdec++/examples/factorial.cc @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020-2024 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 + +#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 index 0000000..497e3fc --- /dev/null +++ b/libmpdec++/examples/pi.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020-2024 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 +#include + +#include +#include +#include + +#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 index 0000000..a934a33 --- /dev/null +++ b/libmpdec/.objs/README.txt @@ -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 index 0000000..18e6eca --- /dev/null +++ b/libmpdec/.objs/symbols32.exp @@ -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 index 0000000..5cb7514 --- /dev/null +++ b/libmpdec/.objs/symbols64.exp @@ -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 index 0000000..603e23c --- /dev/null +++ b/libmpdec/.pc/libmpdec.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libmpdec +Description: C library for decimal floating point arithmetic +Version: 4.0.0 +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 index 0000000..c88e118 --- /dev/null +++ b/libmpdec/.profile/train.sh @@ -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 index 0000000..43fe051 --- /dev/null +++ b/libmpdec/Makefile.in @@ -0,0 +1,315 @@ + +# ============================================================================== +# Unix Makefile for libmpdec +# ============================================================================== + +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 index 0000000..7f72a4b --- /dev/null +++ b/libmpdec/Makefile.vc @@ -0,0 +1,288 @@ + +# ====================================================================== +# Visual C (nmake) Makefile for libmpdec +# ====================================================================== + +LIBSTATIC = libmpdec-4.0.0.lib +LIBIMPORT = libmpdec-4.0.0.dll.lib +LIBSHARED = libmpdec-4.0.0.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 index 0000000..49097a6 --- /dev/null +++ b/libmpdec/README.txt @@ -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 + + diff --git a/libmpdec/basearith.c b/libmpdec/basearith.c new file mode 100644 index 0000000..fc596b7 --- /dev/null +++ b/libmpdec/basearith.c @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 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 index 0000000..ba2a0e5 --- /dev/null +++ b/libmpdec/basearith.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..d19b512 --- /dev/null +++ b/libmpdec/bench.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + +#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 index 0000000..7b0ea59 --- /dev/null +++ b/libmpdec/bench_full.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + +#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 index 0000000..2189428 --- /dev/null +++ b/libmpdec/bits.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2008-2024 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 +/* + * 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 index 0000000..cec292c --- /dev/null +++ b/libmpdec/constants.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..efce60a --- /dev/null +++ b/libmpdec/constants.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2008-2024 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 + +#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 index 0000000..d632ec3 --- /dev/null +++ b/libmpdec/context.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include + +#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 index 0000000..4c4ce46 --- /dev/null +++ b/libmpdec/convolute.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..36eae8f --- /dev/null +++ b/libmpdec/convolute.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..9b2b3ca --- /dev/null +++ b/libmpdec/crt.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2008-2024 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 + +#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 index 0000000..804b351 --- /dev/null +++ b/libmpdec/crt.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..0de0864 --- /dev/null +++ b/libmpdec/difradix2.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008-2024 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 + +#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 index 0000000..acbcb76 --- /dev/null +++ b/libmpdec/difradix2.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..646ebed --- /dev/null +++ b/libmpdec/examples/README.txt @@ -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 index 0000000..d6702e7 --- /dev/null +++ b/libmpdec/examples/compare.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..a815fd0 --- /dev/null +++ b/libmpdec/examples/div.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..a939ec3 --- /dev/null +++ b/libmpdec/examples/divmod.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..8b7ab9e --- /dev/null +++ b/libmpdec/examples/multiply.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..f26cc75 --- /dev/null +++ b/libmpdec/examples/pow.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..b3eb240 --- /dev/null +++ b/libmpdec/examples/powmod.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..97cdea1 --- /dev/null +++ b/libmpdec/examples/shift.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..5b40982 --- /dev/null +++ b/libmpdec/examples/sqrt.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + + +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 index 0000000..45d1ed2 --- /dev/null +++ b/libmpdec/fnt.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..679a593 --- /dev/null +++ b/libmpdec/fnt.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..b44de73 --- /dev/null +++ b/libmpdec/fourstep.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2008-2024 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 + +#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= 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 +#include +#include +#include +#include +#include +#include +#include + +#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<= 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<= 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<= 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 index 0000000..db48205 --- /dev/null +++ b/libmpdec/io.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2008-2024 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 + +#include "mpdecimal.h" + + +#if SIZE_MAX == MPD_SIZE_MAX + #define mpd_strtossize _mpd_strtossize +#else +#include + +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 index 0000000..8a6eb55 --- /dev/null +++ b/libmpdec/literature/REFERENCES.txt @@ -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 index 0000000..f34ff67 --- /dev/null +++ b/libmpdec/literature/bignum.txt @@ -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 index 0000000..9971dcc --- /dev/null +++ b/libmpdec/literature/fnt.py @@ -0,0 +1,207 @@ +# +# Copyright (c) 2008-2024 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 index 0000000..18fe284 --- /dev/null +++ b/libmpdec/literature/matrix-transform.txt @@ -0,0 +1,256 @@ + + +(* Copyright (c) 2011-2024 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 index 0000000..e35ead6 --- /dev/null +++ b/libmpdec/literature/mulmod-64.txt @@ -0,0 +1,127 @@ + + +(* Copyright (c) 2011-2024 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 index 0000000..ab06f9b --- /dev/null +++ b/libmpdec/literature/mulmod-ppro.txt @@ -0,0 +1,266 @@ + + +(* Copyright (c) 2011-2024 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 index 0000000..509f9dd --- /dev/null +++ b/libmpdec/literature/six-step.txt @@ -0,0 +1,63 @@ + + +(* Copyright (c) 2011-2024 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 index 0000000..f6f78fd --- /dev/null +++ b/libmpdec/literature/umodarith.lisp @@ -0,0 +1,690 @@ +; +; Copyright (c) 2008-2024 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= 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= 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> 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 index 0000000..7d176bc --- /dev/null +++ b/libmpdec/mpalloc.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include + +#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 index 0000000..f7ef767 --- /dev/null +++ b/libmpdec/mpalloc.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-2024 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 + +#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 index 0000000..86455ec --- /dev/null +++ b/libmpdec/mpdecimal.c @@ -0,0 +1,9155 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include +#include +#include + +#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 + #pragma float_control(precise, on) + #pragma fenv_access(on) + #elif !defined(__OpenBSD__) && !defined(__NetBSD__) + /* The C99 standard requires the pragma */ + #include + #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)<>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 index 0000000..b2d9072 --- /dev/null +++ b/libmpdec/mpdecimal.h.in @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2008-2024 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 + #include + #include + #include + #include + #define MPD_UINT8_C(x) (static_cast(x)) +extern "C" { +#else + #include + #include + #include + #include + #include + #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 0 + +#define MPD_VERSION "4.0.0" + +#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 index 0000000..6a35951 --- /dev/null +++ b/libmpdec/mpdecimal32vc.h @@ -0,0 +1,762 @@ +/* + * Copyright (c) 2008-2024 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 + #include + #include + #include + #include + #define MPD_UINT8_C(x) (static_cast(x)) +extern "C" { +#else + #include + #include + #include + #include + #include + #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 0 + +#define MPD_VERSION "4.0.0" + +#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 index 0000000..9320176 --- /dev/null +++ b/libmpdec/mpdecimal64vc.h @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2008-2024 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 + #include + #include + #include + #include + #define MPD_UINT8_C(x) (static_cast(x)) +extern "C" { +#else + #include + #include + #include + #include + #include + #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 0 + +#define MPD_VERSION "4.0.0" + +#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 index 0000000..f9892bf --- /dev/null +++ b/libmpdec/mpsignal.c @@ -0,0 +1,966 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..8468aa8 --- /dev/null +++ b/libmpdec/numbertheory.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..888bc65 --- /dev/null +++ b/libmpdec/numbertheory.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..594403f --- /dev/null +++ b/libmpdec/sixstep.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..d60051e --- /dev/null +++ b/libmpdec/sixstep.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..7467e6d --- /dev/null +++ b/libmpdec/transpose.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include +#include + + +#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 index 0000000..886ec7d --- /dev/null +++ b/libmpdec/transpose.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..92d87d7 --- /dev/null +++ b/libmpdec/typearith.h @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2008-2024 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 + +#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 +#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 index 0000000..c5d3207 --- /dev/null +++ b/libmpdec/umodarith.h @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..2bd29ed --- /dev/null +++ b/libmpdec/vcdiv64.asm @@ -0,0 +1,47 @@ +; +; Copyright (c) 2008-2024 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 index 0000000..120fb5b --- /dev/null +++ b/tests++/Makefile.in @@ -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 index 0000000..8e7bffe --- /dev/null +++ b/tests++/Makefile.vc @@ -0,0 +1,79 @@ + +SRCDIR = ..\libmpdec +LIBSTATIC = libmpdec-4.0.0.lib +LIBSHARED = libmpdec-4.0.0.dll +LIBIMPORT = libmpdec-4.0.0.dll.lib + +SRCDIR_CXX = ..\libmpdec++ +LIBSTATIC_CXX = libmpdec++-4.0.0.lib +LIBSHARED_CXX = libmpdec++-4.0.0.dll +LIBIMPORT_CXX = libmpdec++-4.0.0.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 index 0000000..5dc0633 --- /dev/null +++ b/tests++/README.txt @@ -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 index 0000000..4614693 --- /dev/null +++ b/tests++/additional.topTest @@ -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 index 0000000..ffeb437 --- /dev/null +++ b/tests++/apitest.cc @@ -0,0 +1,2861 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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::value)); + + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::value)); + assertTrue((std::is_base_of::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 +static void +signed_construction() +{ + const T min = std::numeric_limits::min(); + const T max = std::numeric_limits::max(); + + const std::vector 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 signs = {-1, 1}; + for (int n = 0; n < std::numeric_limits::digits; n++) { + for (const T& sign : signs) { + for (T x = -5; x < 5; x++) { + const T i = static_cast(sign * ((static_cast(1) << n) + x)); + const Decimal d = {i}; + assertEqual(d, i); + assertEqual(i, d); + } + } + } +} + +template +static void +unsigned_construction() +{ + const T max = std::numeric_limits::max(); + + const std::vector 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::digits; n++) { + for (T x = 0; x < 5; x++) { + const T i = static_cast((static_cast(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_construction(); + signed_construction(); + signed_construction(); + signed_construction(); + + signed_construction(); + signed_construction(); + signed_construction(); + signed_construction(); + + unsigned_construction(); + unsigned_construction(); + unsigned_construction(); + unsigned_construction(); + unsigned_construction(); + + unsigned_construction(); + unsigned_construction(); + unsigned_construction(); + unsigned_construction(); + + /*****************************************************************/ + /* 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 +static void +signed_construction_ctx(Context& ctx) +{ + const T min = std::numeric_limits::min(); + const T max = std::numeric_limits::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 +static void +unsigned_construction_ctx(Context& ctx) +{ + const T v = std::numeric_limits::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(ctx); + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + signed_construction_ctx(ctx); + + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(ctx); + unsigned_construction_ctx(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 +static void +signed_assignment() +{ + const T min = std::numeric_limits::min(); + const T max = std::numeric_limits::max(); + const std::vector 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 +static void +unsigned_assignment() +{ + const T max = std::numeric_limits::max(); + const std::vector 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_assignment(); + signed_assignment(); + signed_assignment(); + signed_assignment(); + + signed_assignment(); + signed_assignment(); + signed_assignment(); + signed_assignment(); + + unsigned_assignment(); + unsigned_assignment(); + unsigned_assignment(); + unsigned_assignment(); + unsigned_assignment(); + + unsigned_assignment(); + unsigned_assignment(); + unsigned_assignment(); + unsigned_assignment(); +} + +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(x), reinterpret_cast(d) + 10 * (sizeof *d)); + + x -= 10; + assertEqual(reinterpret_cast(x), reinterpret_cast(d)); +#endif + + delete x; +} + + +/******************************************************************************/ +/* Comparison operators */ +/******************************************************************************/ + +template +static void +signed_comparison() +{ + const T min = std::numeric_limits::min(); + const T max = std::numeric_limits::max(); + const std::vector 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 +static void +unsigned_comparison() +{ + const T max = std::numeric_limits::max(); + const std::vector 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_comparison(); + signed_comparison(); + signed_comparison(); + signed_comparison(); + + signed_comparison(); + signed_comparison(); + signed_comparison(); + signed_comparison(); + + unsigned_comparison(); + unsigned_comparison(); + unsigned_comparison(); + unsigned_comparison(); + unsigned_comparison(); + + unsigned_comparison(); + unsigned_comparison(); + unsigned_comparison(); + unsigned_comparison(); + + + /* 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 +static void +signed_arithmetic() +{ + const T min = std::numeric_limits::min(); + const T max = std::numeric_limits::max(); + const std::vector 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 +static void +unsigned_arithmetic() +{ + const T max = std::numeric_limits::max(); + const std::vector 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_arithmetic(); + signed_arithmetic(); + signed_arithmetic(); + signed_arithmetic(); + + signed_arithmetic(); + signed_arithmetic(); + signed_arithmetic(); + signed_arithmetic(); + + unsigned_arithmetic(); + unsigned_arithmetic(); + unsigned_arithmetic(); + unsigned_arithmetic(); + unsigned_arithmetic(); + + unsigned_arithmetic(); + unsigned_arithmetic(); + unsigned_arithmetic(); + unsigned_arithmetic(); +} + +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(x), reinterpret_cast(d) + 10 * (sizeof *d)); + + x = x - 10; + assertEqual(reinterpret_cast(x), reinterpret_cast(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> 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> 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> 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(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 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 map1; + std::vector 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 map2; + std::vector 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 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 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 map1; + std::vector 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 map2; + std::vector 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> 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& 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 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& 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& 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& tests) +{ + const size_t n = tests.size(); + std::vector 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& tests) +{ + const size_t n = tests.size(); + std::vector status(n, "PASS"); + std::vector 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& tests) +{ + const size_t n = tests.size(); + std::vector status(n, "PASS"); + std::random_device rd; + std::mt19937 g(rd()); + std::vector 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& tests) +{ + const size_t n = tests.size(); + std::vector status(n, "PASS"); + std::vector t(n); + std::random_device rd; + std::mt19937 g(rd()); + std::vector 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 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 == 0, "MPD_MICRO_VERSION must be 0"); + + if (mpd_version() != std::string("4.0.0")) { + err_exit("mpd_version() != 4.0.0"); + } + if (MPD_VERSION != std::string("4.0.0")) { + err_exit("MPD_VERSION != 4.0.0"); + } + + /* 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 index 0000000..92f9cdd --- /dev/null +++ b/tests++/gettests.bat @@ -0,0 +1,9 @@ +@ECHO OFF + +cd ..\tests +call gettests.bat +cd ..\tests++ + +if not exist testdata mkdir testdata +if not exist testdata\baseconv.decTest copy /y ..\tests\testdata_dist\* testdata +if not exist testdata\add.decTest copy /y ..\tests\testdata\* testdata diff --git a/tests++/gettests.sh b/tests++/gettests.sh new file mode 100755 index 0000000..cecb7f0 --- /dev/null +++ b/tests++/gettests.sh @@ -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 index 0000000..c5fce0a --- /dev/null +++ b/tests++/official.topTest @@ -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 index 0000000..2b94c3e --- /dev/null +++ b/tests++/runshort.sh @@ -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 index 0000000..0caf0f4 --- /dev/null +++ b/tests++/runshort_alloc.sh @@ -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 index 0000000..c8aa3a9 --- /dev/null +++ b/tests++/runtest.cc @@ -0,0 +1,3210 @@ +/* + * Copyright (c) 2020-2024 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 + #endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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& 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(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(*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(*end))) { + break; + } + if (!istokchar(static_cast(*end))) { + return -1; + } + } + next_start = end; + return 0; + } + } + + start = next_start = end; + return 0; +} + +/* split a line into tokens */ +static std::vector +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 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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 ptr(new char[strlen(s)+1], std::default_delete()); + 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(u); + *cp++ = static_cast(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 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(ul); + bytes.push_back(static_cast(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& 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& 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& 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& 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& 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& 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& 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& 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& 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::*DecimalPair_DecimalDecimalContext)(const Decimal&, Context&) const; + +static void +DecPair_DecDecCtx_RunSingle(std::pair& result, Decimal& tmp1, Decimal& tmp2, + const std::vector& 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& token, + const bool scan_equal, + const bool extended) +{ + Context maxcontext{readcontext(extended)}; + std::pair 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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 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 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 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(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& status) +{ + for (auto p : status) { + if (p != "PASS") { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +static void +do_file(const std::string& filename, std::vector& 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& files) +{ + const size_t n = files.size(); + std::vector 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& files) +{ + const size_t n = files.size(); + std::vector status(n, "PASS"); + std::vector 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 *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(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& files) +{ + const size_t n = files.size(); + std::vector status(n, "PASS"); + std::vector 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 +collect_files(const std::string& topfile) +{ + std::vector 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 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 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 << " ... " << e.what() << "\n" << std::flush; + return EXIT_FAILURE; + } + std::cout << " ... PASS\n\n" << std::flush; + return EXIT_SUCCESS; + } + + /* Collect test files */ + std::vector 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 index 0000000..1e181cc --- /dev/null +++ b/tests++/test.cc @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include + +#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 index 0000000..bb61197 --- /dev/null +++ b/tests++/test.hh @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2020-2024 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 +#include +#include +#include + + +namespace test { + + +/******************************************************************************/ +/* Util */ +/******************************************************************************/ + +template +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 +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 + explicit Failure(Args... args) : m(stringize(args...)) {} + virtual const char* what() const noexcept; +}; + +template +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 +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 +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 +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(__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 index 0000000..6a34b34 --- /dev/null +++ b/tests++/vctest.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020-2024 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 index 0000000..3a33070 --- /dev/null +++ b/tests/Makefile.in @@ -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 index 0000000..39982c2 --- /dev/null +++ b/tests/Makefile.vc @@ -0,0 +1,59 @@ + +SRCDIR = ..\libmpdec +LIBSTATIC = libmpdec-4.0.0.lib +LIBSHARED = libmpdec-4.0.0.dll +LIBIMPORT = libmpdec-4.0.0.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 index 0000000..5dc0633 --- /dev/null +++ b/tests/README.txt @@ -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 index 0000000..eedaeb9 --- /dev/null +++ b/tests/additional.decTest @@ -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 index 0000000..b7afb6c --- /dev/null +++ b/tests/gettests.bat @@ -0,0 +1,16 @@ +@ECHO OFF + +if not exist testdata mkdir testdata +rem copy additional tests +if not exist testdata\baseconv.decTest copy /y testdata_dist\* testdata + +if exist testdata\add.decTest goto OUT + +rem get official tests +if exist dectest.zip goto UNZIP +powershell -Command "wget http://speleotrove.com/decimal/dectest.zip -outfile dectest.zip" + +:UNZIP +powershell -Command "Expand-Archive dectest.zip -DestinationPath testdata" + +:OUT diff --git a/tests/gettests.sh b/tests/gettests.sh new file mode 100755 index 0000000..53ccab3 --- /dev/null +++ b/tests/gettests.sh @@ -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 index 0000000..c5fce0a --- /dev/null +++ b/tests/official.decTest @@ -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 index 0000000..a45e638 --- /dev/null +++ b/tests/runshort.sh @@ -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 index 0000000..c307402 --- /dev/null +++ b/tests/runshort_alloc.sh @@ -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 index 0000000..dbc51a4 --- /dev/null +++ b/tests/runtest.c @@ -0,0 +1,5649 @@ +/* + * Copyright (c) 2008-2024 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mpdecimal.h" +#include "test.h" +#include "vctest.h" + + +#define MAXLINE 400000 +#define MAXTOKEN 32 + +#ifndef _MSC_VER + #include + #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.0") != (0)) { + fputs("runtest: error: mpd_version() != 4.0.0\n", stderr); + exit(EXIT_FAILURE); + } + if (strcmp(MPD_VERSION, "4.0.0") != (0)) { + fputs("runtest: error: MPD_VERSION != 4.0.0\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 != (0)) { + fputs("runtest: error: MPD_MICRO_VERSION != 0\n", stderr); + exit(EXIT_FAILURE); + } + if (MPD_VERSION_HEX != (0x04000000)) { + fputs("runtest: error: MPD_VERSION_HEX != 0x04000000\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 index 0000000..893830c --- /dev/null +++ b/tests/test.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..f05b8be --- /dev/null +++ b/tests/test.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-2024 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 +#include + +#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 index 0000000..7d0d18c --- /dev/null +++ b/tests/testdata_dist/baseconv.decTest @@ -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 index 0000000..b2c4c93 --- /dev/null +++ b/tests/testdata_dist/binop_eq.decTest @@ -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 index 0000000..1ba0ce8 --- /dev/null +++ b/tests/testdata_dist/cov.decTest @@ -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 index 0000000..f21e445 --- /dev/null +++ b/tests/testdata_dist/divmod.decTest @@ -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 index 0000000..d5d6fc6 --- /dev/null +++ b/tests/testdata_dist/divmod_eq.decTest @@ -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 index 0000000..f44477b --- /dev/null +++ b/tests/testdata_dist/extra.decTest @@ -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 index 0000000..97095f9 --- /dev/null +++ b/tests/testdata_dist/fma_eq.decTest @@ -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 index 0000000..e8f1d85 --- /dev/null +++ b/tests/testdata_dist/format.decTest @@ -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 index 0000000..cd1ee02 --- /dev/null +++ b/tests/testdata_dist/getint.decTest @@ -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 index 0000000..f459e44 --- /dev/null +++ b/tests/testdata_dist/invroot.decTest @@ -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 index 0000000..ccd8460 --- /dev/null +++ b/tests/testdata_dist/largeint.decTest @@ -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 index 0000000..f66aac7 --- /dev/null +++ b/tests/testdata_dist/maxprec.decTest @@ -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 index 0000000..566ac26 --- /dev/null +++ b/tests/testdata_dist/powmod.decTest @@ -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 index 0000000..13e4d49 --- /dev/null +++ b/tests/testdata_dist/powmod_eq.decTest @@ -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 index 0000000..3b9d221 --- /dev/null +++ b/tests/testdata_dist/shiftlr.decTest @@ -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 index 0000000..c5e8304 --- /dev/null +++ b/tests/testdata_dist/testruntest.decTest @@ -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 index 0000000..9c3af22 --- /dev/null +++ b/tests/vctest.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2024 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 index 0000000..7736287 --- /dev/null +++ b/vcbuild/README.txt @@ -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 index 0000000..090524e --- /dev/null +++ b/vcbuild/pgobuild32.bat @@ -0,0 +1,40 @@ +@ECHO off + +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath > vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath= vcpath.txt +set /p vcpath=