From: Ondřej Surý Date: Sun, 9 Feb 2025 10:41:21 +0000 (+0100) Subject: Import mpdecimal_4.0.0.orig.tar.gz X-Git-Tag: archive/raspbian/4.0.1-5+rpi1^2^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=cffe50c7abb6e0da341047e9f58e187165cd4eb7;p=mpdecimal.git Import mpdecimal_4.0.0.orig.tar.gz [dgit import orig mpdecimal_4.0.0.orig.tar.gz] --- cffe50c7abb6e0da341047e9f58e187165cd4eb7 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=