From d4fdd9ef1788073f56cbbfa173374d96bb87e9dc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 29 Oct 2014 18:32:47 -0700 Subject: [PATCH] Begin QofInt128 class. Constructs, adds & subtracts, prints, and compares. Includes unit tests. --- src/libqof/qof/qofint128.cpp | 349 ++++++++++++++++++++++++ src/libqof/qof/qofint128.hpp | 193 +++++++++++++ src/libqof/qof/test/Makefile.am | 17 ++ src/libqof/qof/test/gtest-qofint128.cpp | 319 ++++++++++++++++++++++ 4 files changed, 878 insertions(+) create mode 100644 src/libqof/qof/qofint128.cpp create mode 100644 src/libqof/qof/qofint128.hpp create mode 100644 src/libqof/qof/test/gtest-qofint128.cpp diff --git a/src/libqof/qof/qofint128.cpp b/src/libqof/qof/qofint128.cpp new file mode 100644 index 0000000000..09084c6988 --- /dev/null +++ b/src/libqof/qof/qofint128.cpp @@ -0,0 +1,349 @@ +/******************************************************************** + * qofmath128.c -- an 128-bit integer library * + * Copyright (C) 2004 Linas Vepstas * + * Copyright (C) 2014 John Ralls * + * * + * This program 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 2 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *******************************************************************/ +extern "C" +{ +#include "config.h" +#include +} + +#include "qofint128.hpp" + +#include + +/* All algorithms from Donald E. Knuth, "The Art of Computer + * Programming, Volume 2: Seminumerical Algorithms", 3rd Ed., + * Addison-Wesley, 1998. + */ + +QofInt128::QofInt128 () : m_flags {}, m_hi {0}, m_lo {0}{} + +QofInt128::QofInt128 (int64_t lower) : + m_flags {static_cast(lower < 0 ? neg : pos)}, + m_hi {0}, + m_lo {static_cast(lower < 0 ? -lower : lower)} {} + +QofInt128::QofInt128 (uint64_t lower) : + m_flags {}, m_hi {0}, m_lo {lower} {} + +QofInt128::QofInt128 (int64_t upper, int64_t lower, unsigned char flags) : + m_flags {static_cast(flags ^ (upper < 0 ? neg : + upper == 0 && lower < 0 ? neg : pos))}, + m_hi {static_cast(upper < 0 ? -upper : upper)}, + m_lo {static_cast(lower < 0 ? -lower : lower)} +{ + if ((upper < 0 && lower > 0) || (upper > 0 && lower < 0)) + m_lo = (m_hi << 63) - m_lo; + else + m_lo += (m_hi << 63); + + m_hi >>= 1; +} + + QofInt128::QofInt128 (uint64_t upper, uint64_t lower, unsigned char flags) : + m_flags {flags}, m_hi {upper}, + m_lo {lower} {} + +QofInt128::operator int64_t() const +{ + if ((m_flags & neg) && isBig()) + throw std::underflow_error ("Negative value to large to represent as int64_t"); + if ((m_flags & (overflow | NaN)) || isBig()) + throw std::overflow_error ("Value to large to represent as int64_t"); + int64_t retval = static_cast(m_lo); + return m_flags & neg ? -retval : retval; +} + +QofInt128::operator uint64_t() const +{ + if (m_flags & neg) + throw std::underflow_error ("Can't represent negative value as uint64_t"); + if ((m_flags & (overflow | NaN)) || (m_hi || m_lo > UINT64_MAX)) + throw std::overflow_error ("Value to large to represent as uint64_t"); + return m_lo; +} + + +int +QofInt128::cmp (const QofInt128& b) const noexcept +{ + if (m_flags & (overflow | NaN)) + return -9; + if (b.isOverflow () || b.isNan ()) + return 9; + if (m_flags & neg) + { + if (!b.isNeg()) return -1; + if (m_hi > b.m_hi) return -3; + if (m_hi < b.m_hi) return 3; + if (m_lo > b.m_lo) return -2; + if (m_lo < b.m_lo) return 2; + return 0; + } + if (b.isNeg()) return 1; + if (m_hi < b.m_hi) return -5; + if (m_hi > b.m_hi) return 5; + if (m_lo < b.m_lo) return -4; + if (m_lo > b.m_lo) return 4; + return 0; +} + +bool +QofInt128::isNeg () const noexcept +{ + return (m_flags & neg); +} + +bool +QofInt128::isBig () const noexcept +{ + return (m_hi || m_lo > INT64_MAX); +} + +bool +QofInt128::isOverflow () const noexcept +{ + return (m_flags & overflow); +} + +bool +QofInt128::isNan () const noexcept +{ + return (m_flags & NaN); +} + +bool +QofInt128::isZero() const noexcept +{ + return ((m_flags & (overflow | NaN)) == 0 && m_hi == 0 && m_lo == 0); +} + +QofInt128 +QofInt128::operator-() const noexcept +{ + auto retval = *this; + if (isNeg()) + retval.m_flags ^= neg; + else + retval.m_flags |= neg; + return retval; +} + +QofInt128::operator bool() const noexcept +{ + return ! isZero (); +} + +QofInt128& +QofInt128::operator+= (const QofInt128& b) noexcept +{ + if ((isNeg () && !b.isNeg ()) || (!isNeg () && b.isNeg ())) + return this->operator-= (-b); + uint64_t result = m_lo + b.m_lo; + uint64_t carry = static_cast(result < m_lo); //Wrapping + m_lo = result; + result = m_hi + b.m_hi + carry; + if (result < m_hi) + m_flags |= overflow; + m_hi = result; + return *this; +} + + +QofInt128& +QofInt128::operator-= (const QofInt128& b) noexcept +{ + if ((!isNeg() && b.isNeg()) || (isNeg() && !b.isNeg())) + return this->operator+= (-b); + bool operand_bigger {cmp (b) < 0}; + if (operand_bigger) + { + m_flags ^= neg; // ^= flips the bit + uint64_t far_hi {b.m_hi}; + if (m_lo > b.m_lo) + { + /* The + 1 on the end is because we really want to use 2^64, or + * UINT64_MAX + 1, but that can't be represented in a uint64_t. + */ + m_lo = UINT64_MAX - m_lo + b.m_lo + 1; + --far_hi; //borrow + } + else + m_lo = b.m_lo - m_lo; + + m_hi = far_hi - m_hi; + return *this; + } + if (m_lo < b.m_lo) + { + m_lo = UINT64_MAX - b.m_lo + m_lo + 1; //See UINT64_MAX comment above + --m_hi; //borrow + } + else + m_lo -= b.m_lo; + + m_hi -= b.m_hi; + + return *this; +} + +QofInt128& +QofInt128::operator*= (const QofInt128& b) noexcept +{ + return *this; +} + +QofInt128& +QofInt128::operator/= (const QofInt128& b) noexcept +{ + return *this; +} + +QofInt128& +QofInt128::operator%= (const QofInt128& b) noexcept +{ + return *this; +} + +static const uint8_t dec_array_size {5}; +/* Convert a uint128 represented as a binary number into 4 10-digit decimal + * equivalents. Adapted from Douglas W. Jones, "Binary to Decimal Conversion in + * Limited Precision", http://homepage.cs.uiowa.edu/~jones/bcd/decimal.html, + * accessed 28 Oct 2014. */ +static void +decimal_from_binary (uint64_t d[dec_array_size], uint64_t hi, uint64_t lo) +{ + /* Coefficients are the values of 2^96, 2^64, and 2^32 divided into 8-digit + * segments: + * 2^96 = 79228,16251426,43375935,43950336 + * 2^64 = 1844,67440737,09551616 + * 2^32 = 42,94967296 + */ + const uint8_t coeff_array_size = dec_array_size - 1; + const uint32_t coeff_3 [coeff_array_size] {79228, 16251426, 43375935, 43950336}; + const uint32_t coeff_2 [coeff_array_size] {0, 1844, 67440737, 9551616}; + const uint32_t coeff_1 [coeff_array_size] {0, 0, 42, 94967296}; + const uint32_t bin_mask {0xffffffff}; + const uint32_t dec_div {UINT32_C(100000000)}; + const uint8_t last {dec_array_size - 1}; + + d[0] = lo & bin_mask; + d[1] = (lo >> 32) & bin_mask; + d[2] = hi & bin_mask; + d[3] = (hi >> 32) & bin_mask, 0; + + d[0] += coeff_3[3] * d[3] + coeff_2[3] * d[2] + coeff_1[3] * d[1]; + uint64_t q {d[0] / dec_div}; + d[0] %= dec_div; + + for (int i {1}; i < coeff_array_size; ++i) + { + int j = coeff_array_size - i - 1; + d[i] = q + coeff_3[j] * d[3] + coeff_2[j] * d[2] + coeff_1[j] * d[1]; + q = d[i] / dec_div; + d[i] %= dec_div; + } + d[last] = q; + return; +} + +static const uint8_t char_buf_size {41}; //39 digits plus sign and trailing null + +char* +QofInt128::asCharBufR(char* buf) const noexcept +{ + if (isOverflow()) + { + sprintf (buf, "%s", "Overflow"); + return buf; + } + if (isNan()) + { + sprintf (buf, "%s", "NaN"); + return buf; + } + uint64_t d[dec_array_size] {}; + decimal_from_binary(d, m_hi, m_lo); + char* next = buf; + char neg {'-'}; + + if (isNeg()) *(next++) = neg; + bool trailing {false}; + for (uint i {dec_array_size}; i; --i) + if (d[i - 1] || trailing) + { + if (trailing) + next += sprintf (next, "%8.8" PRIu64, d[i - 1]); + else + next += sprintf (next, "%" PRIu64, d[i - 1]); + + trailing = true; + } + + return buf; +} + +std::ostream& +operator<< (std::ostream& stream, const QofInt128& a) noexcept +{ + char buf[char_buf_size] {}; + stream << a.asCharBufR (buf); + return stream; +} + +bool +operator== (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) == 0; +} + +bool +operator!= (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) != 0; +} + +bool +operator< (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) < 0; +} + +bool +operator> (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) > 0; +} + +bool +operator<= (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) <= 0; +} + +bool +operator>= (const QofInt128& a, const QofInt128& b) noexcept +{ + return a.cmp(b) >= 0; +} + diff --git a/src/libqof/qof/qofint128.hpp b/src/libqof/qof/qofint128.hpp new file mode 100644 index 0000000000..03b2ac9fd0 --- /dev/null +++ b/src/libqof/qof/qofint128.hpp @@ -0,0 +1,193 @@ +/******************************************************************** + * qofmath128.h -- an 128-bit integer library * + * Copyright (C) 2004 Linas Vepstas * + * Copyright (C) 2014 John Ralls * + * * + * This program 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 2 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *******************************************************************/ + +#ifndef QOFINT128_H +#define QOFINT128_H + +#include + +#include +#include +#include + +//using std::string; + +/** + * @brief provides a 128-bit int as a base class for GncNumeric. + * + * All the usual operators are provided. Only the explicit integer + * conversions throw; all other errors are indicated by the overflow + * and NaN ("Not a Number") flags. Note that performing any operation + * on an overflowed or NaN QofInt128 will yield an overflowed or NaN + * result, so calling routines need not check until the end of a + * chained calculation. + */ +class QofInt128 +{ + unsigned char m_flags; + uint64_t m_hi; + uint64_t m_lo; + +public: +enum // Values for m_flags +{ + pos = 0, + neg = 1, + overflow = 2, + NaN = 4 +}; + + QofInt128 (); + QofInt128 (int64_t lower); + QofInt128 (uint64_t lower); +/** + * Construct a QofInt128 from two int64_t. + * + * N.B.: If the two parameters are of differing sign, it's taken to + * mean that the lower magnitude is *reducing* the magnitude of the + * upper, so the lower magnitude will be subracted from UINT64_MAX to + * obtain the lower limb value. + */ + QofInt128 (int64_t upper, int64_t lower, unsigned char flags = '\0'); + QofInt128 (uint64_t upper, uint64_t lower, unsigned char flags = '\0'); +/** + * Compare function. + * + * @return -1 if the object is less than the parameter, 0 if they're + * equal, and 1 if the object is greater. + */ + int cmp (const QofInt128& b) const noexcept; + +/** + * Computes the Greatest Common Divisor between the object and paramter + * + * @return A QofInt128 having the GCD. + */ + QofInt128 gcd (const QofInt128& b) const noexcept; +/** + * Computes the Least Common Multiple between the object and paramter + * + * @return A QofInt128 having the LCM. + */ + QofInt128 lcm (const QofInt128& b) const noexcept; + +/** + * Computes the object raised to the parameter's power + * + * @return A QofInt128; it will be NaN if the parameter is negative. + */ + QofInt128 pow (const QofInt128& b) const noexcept; + +/** + * Explicit conversion to int64_t. + * + * @return A int64_t + * @throws std::overflow_error if the object's value is > INT64_MAX or NaN. + * @throws std::underflow_error if the object's value is < INT64_MIN + */ + explicit operator int64_t() const; +/** + * Explicit conversion to uint64_t. + * + * @return A uint64_t + * @throws std::overflow_error if the object's value is > UINT64_MAX or NaN. + * @throws std::underflow_error if the object's value is < 0. + */ + explicit operator uint64_t() const; + +/** + * @return true if the object value is < 0 + */ + bool isNeg () const noexcept; +/** + * @return true if the object value is > INT64_MAX or < INT64_MIN + */ + bool isBig () const noexcept; +/** + * @return true if a calculation has produced a result of larger + * magnitude than can be contained in the 128 bits available. + */ + bool isOverflow () const noexcept; +/** + * @return true if an illegal calculation has occured. + */ + bool isNan () const noexcept; +/** + * @return true if the object represents 0. + */ + bool isZero() const noexcept; + +/** + * Fills a supplied buffer with a representation of the number in base 10. If + * the QofInt128 is overflowed or NaN it will contain the words "Overflow" or + * "NaN" respectively. + * + * @param buf char[41], 39 digits plus sign and trailing 0. + * @return pointer to the buffer for convenience + */ + char* asCharBufR(char* buf) const noexcept; + + + QofInt128 operator-() const noexcept; + explicit operator bool() const noexcept; + + QofInt128& operator++ () noexcept; + QofInt128 operator++ (int) noexcept; + QofInt128& operator+= (const QofInt128& b) noexcept; + QofInt128& operator-= (const QofInt128& b) noexcept; + QofInt128& operator*= (const QofInt128& b) noexcept; + QofInt128& operator/= (const QofInt128& b) noexcept; + QofInt128& operator%= (const QofInt128& b) noexcept; + +}; + +static const QofInt128 k_qofInt128_Max {UINT64_MAX, UINT64_MAX, QofInt128::pos}; +static const QofInt128 k_qofInt128_Min {UINT64_MAX, UINT64_MAX, QofInt128::neg}; + +QofInt128 operator+ (QofInt128 a, QofInt128 b) noexcept; +QofInt128 operator- (QofInt128 a, QofInt128 b) noexcept; +QofInt128 operator* (QofInt128 a, QofInt128 b) noexcept; +QofInt128 operator/ (QofInt128 a, QofInt128 b) noexcept; +QofInt128 operator% (QofInt128 a, QofInt128 b) noexcept; + +bool operator== (const QofInt128& a, const QofInt128& b) noexcept; +bool operator!= (const QofInt128& a, const QofInt128& b) noexcept; +bool operator<= (const QofInt128& a, const QofInt128& b) noexcept; +bool operator>= (const QofInt128& a, const QofInt128& b) noexcept; +bool operator< (const QofInt128& a, const QofInt128& b) noexcept; +bool operator> (const QofInt128& a, const QofInt128& b) noexcept; + +std::ostream& operator<< (std::ostream&, const QofInt128&) noexcept; + +/** Compute the greatest common denominator of two integers + */ +QofInt128 gcd (int64_t a, int64_t b); + +/** Compute the least common multiple of two integers + */ +QofInt128 lcm (int64_t a, int64_t b); + +#endif //QOFINT128_H + +/** @} */ diff --git a/src/libqof/qof/test/Makefile.am b/src/libqof/qof/test/Makefile.am index 2641069da4..0c0210f00e 100644 --- a/src/libqof/qof/test/Makefile.am +++ b/src/libqof/qof/test/Makefile.am @@ -33,6 +33,23 @@ check_PROGRAMS = \ TESTS = ${check_PROGRAMS} +if WITH_GOOGLE_TEST +test_qofint128_SOURCES = \ + $(top_srcdir)/${MODULEPATH}/qofint128.cpp \ + ${GTEST_ROOT}/src/gtest_main.cc \ + gtest-qofint128.cpp + +test_qofint128_CPPFLAGS = \ + -I${GTEST_HEADERS} \ + ${GLIB_CFLAGS} + +test_qofint128_LDADD = \ + ${GLIB_LIBS} \ + $(top_builddir)/src/test-core/libgtest.a + +check_PROGRAMS += test-qofint128 +endif + test_qofdir = ${GNC_LIBEXECDIR}/${MODULEPATH}/test #The tests might require more libraries, but try to keep them diff --git a/src/libqof/qof/test/gtest-qofint128.cpp b/src/libqof/qof/test/gtest-qofint128.cpp new file mode 100644 index 0000000000..18bd59b33c --- /dev/null +++ b/src/libqof/qof/test/gtest-qofint128.cpp @@ -0,0 +1,319 @@ +/******************************************************************** + * gtest-qofmath128.cpp -- unit tests for the QofInt128 class * + * Copyright (C) 2014 John Ralls * + * * + * This program 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 2 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *******************************************************************/ + +#include +#include "../qofint128.hpp" + +TEST(qofint128_constructors, test_default_constructor) +{ + QofInt128 value {}; + EXPECT_EQ (true, value.isZero()); + EXPECT_EQ (false, value.isNeg()); + EXPECT_EQ (false, value.isBig()); + EXPECT_EQ (false, value.isOverflow()); + EXPECT_EQ (false, value.isNan()); +} + +TEST(qofint128_constructors, test_single_arg_constructor) +{ + QofInt128 value1 (INT64_C(0)); + EXPECT_EQ (true, value1.isZero()); + EXPECT_EQ (false, value1.isNeg()); + EXPECT_EQ (false, value1.isBig()); + EXPECT_EQ (false, value1.isOverflow()); + EXPECT_EQ (false, value1.isNan()); + + QofInt128 value2 (INT64_C(567894392130486208)); + EXPECT_EQ (false, value2.isZero()); + EXPECT_EQ (false, value2.isNeg()); + EXPECT_EQ (false, value2.isBig()); + EXPECT_EQ (false, value2.isOverflow()); + EXPECT_EQ (false, value2.isNan()); + + QofInt128 value3 (INT64_C(-567894392130486208)); + EXPECT_EQ (false, value3.isZero()); + EXPECT_EQ (true, value3.isNeg()); + EXPECT_EQ (false, value3.isBig()); + EXPECT_EQ (false, value3.isOverflow()); + EXPECT_EQ (false, value3.isNan()); + + QofInt128 value4 (UINT64_C(13567894392130486208)); + EXPECT_EQ (false, value4.isZero()); + EXPECT_EQ (false, value4.isNeg()); + EXPECT_EQ (true, value4.isBig()); + EXPECT_EQ (false, value4.isOverflow()); + EXPECT_EQ (false, value4.isNan()); +} + +TEST(qofint128_constructors, test_double_arg_constructor) +{ + QofInt128 value1 (INT64_C(0), INT64_C(0)); + EXPECT_EQ (true, value1.isZero()); + EXPECT_EQ (false, value1.isNeg()); + EXPECT_EQ (false, value1.isBig()); + EXPECT_EQ (false, value1.isOverflow()); + EXPECT_EQ (false, value1.isNan()); + + QofInt128 value2 (INT64_C(0), INT64_C(567894392130486208)); + EXPECT_EQ (false, value2.isZero()); + EXPECT_EQ (false, value2.isNeg()); + EXPECT_EQ (false, value2.isBig()); + EXPECT_EQ (false, value2.isOverflow()); + EXPECT_EQ (false, value2.isNan()); + + QofInt128 value3 (INT64_C(567894392130486208), INT64_C(0)); + EXPECT_EQ (false, value3.isZero()); + EXPECT_EQ (false, value3.isNeg()); + EXPECT_EQ (true, value3.isBig()); + EXPECT_EQ (false, value3.isOverflow()); + EXPECT_EQ (false, value3.isNan()); + + QofInt128 value4 (INT64_C(567894392130486208), INT64_C(567894392130486208)); + EXPECT_EQ (false, value4.isZero()); + EXPECT_EQ (false, value4.isNeg()); + EXPECT_EQ (true, value4.isBig()); + EXPECT_EQ (false, value4.isOverflow()); + EXPECT_EQ (false, value4.isNan()); + + QofInt128 value5 (INT64_C(567894392130486208), + INT64_C(-567894392130486208)); + EXPECT_EQ (false, value5.isZero()); + EXPECT_EQ (false, value5.isNeg()); + EXPECT_EQ (true, value5.isBig()); + EXPECT_EQ (false, value5.isOverflow()); + EXPECT_EQ (false, value5.isNan()); + + QofInt128 value6 (INT64_C(-567894392130486208), + INT64_C(567894392130486208)); + EXPECT_EQ (false, value6.isZero()); + EXPECT_EQ (true, value6.isNeg()); + EXPECT_EQ (true, value6.isBig()); + EXPECT_EQ (false, value6.isOverflow()); + EXPECT_EQ (false, value6.isNan()); + + QofInt128 value7 (UINT64_C(13567894392130486208), + UINT64_C(13567894392130486208), QofInt128::pos); + EXPECT_EQ (false, value7.isZero()); + EXPECT_EQ (false, value7.isNeg()); + EXPECT_EQ (true, value7.isBig()); + EXPECT_EQ (false, value7.isOverflow()); + EXPECT_EQ (false, value7.isNan()); + + QofInt128 value8 (UINT64_C(13567894392130486208), + UINT64_C(13567894392130486208), QofInt128::neg); + EXPECT_EQ (false, value8.isZero()); + EXPECT_EQ (true, value8.isNeg()); + EXPECT_EQ (true, value8.isBig()); + EXPECT_EQ (false, value8.isOverflow()); + EXPECT_EQ (false, value8.isNan()); + + QofInt128 value9 (UINT64_C(13567894392130486208), + UINT64_C(13567894392130486208), QofInt128::overflow); + EXPECT_EQ (false, value9.isZero()); + EXPECT_EQ (false, value9.isNeg()); + EXPECT_EQ (true, value9.isBig()); + EXPECT_EQ (true, value9.isOverflow()); + EXPECT_EQ (false, value9.isNan()); + + QofInt128 value10 (UINT64_C(13567894392130486208), + UINT64_C(13567894392130486208), QofInt128::NaN); + EXPECT_EQ (false, value10.isZero()); + EXPECT_EQ (false, value10.isNeg()); + EXPECT_EQ (true, value10.isBig()); + EXPECT_EQ (false, value10.isOverflow()); + EXPECT_EQ (true, value10.isNan()); +} + +TEST(qofint128_functions, test_int_functions) +{ + int64_t arg {INT64_C(567894392130486208)}; + int64_t narg {INT64_C(-567894392130486208)}; + uint64_t uarg {UINT64_C(13567894392130486208)}; + QofInt128 value1 (INT64_C(0), arg); + EXPECT_EQ (arg, static_cast(value1)); + EXPECT_EQ (static_cast(arg), static_cast(value1)); + + QofInt128 value2 (UINT64_C(0), uarg); + EXPECT_THROW (static_cast(value2), std::overflow_error); + EXPECT_EQ (uarg, static_cast(value2)); + + QofInt128 value3 (UINT64_C(0), uarg, QofInt128::neg); + EXPECT_THROW (static_cast(value3), std::underflow_error); + EXPECT_THROW (static_cast(value3), std::underflow_error); + + QofInt128 value4 (UINT64_C(0), uarg, QofInt128::overflow); + EXPECT_THROW (static_cast(value4), std::overflow_error); + EXPECT_THROW (static_cast(value4), std::overflow_error); + + QofInt128 value5 (UINT64_C(0), uarg, QofInt128::NaN); + EXPECT_THROW (static_cast(value5), std::overflow_error); + EXPECT_THROW (static_cast(value5), std::overflow_error); + + QofInt128 value6 (INT64_C(1), arg); + EXPECT_THROW (static_cast(value6), std::overflow_error); + EXPECT_EQ (arg + (UINT64_C(0x1) << 63), static_cast(value6)); + + QofInt128 value7 (INT64_C(-1), arg); + EXPECT_EQ (-static_cast((UINT64_C(0x1) << 63) - arg), + static_cast(value7)); + EXPECT_THROW (static_cast(value7), std::underflow_error); + + QofInt128 value8 (INT64_C(0), narg); + EXPECT_EQ (narg, static_cast(value8)); + EXPECT_THROW (static_cast(value8), std::underflow_error); + + QofInt128 value9 (INT64_C(1), narg); + EXPECT_EQ (static_cast((UINT64_C(0x1) << 63) + narg), + static_cast(value9)); + EXPECT_EQ ((UINT64_C(0x1) << 63) + narg, static_cast(value9)); + + QofInt128 value10 (INT64_C(-2), arg); + EXPECT_THROW (static_cast(value10), std::underflow_error); + EXPECT_THROW (static_cast(value10), std::underflow_error); + +} + +TEST(qofint128_functions, test_compare) +{ + int64_t barg {INT64_C(567894392130486208)}; + int64_t nbarg {INT64_C(-567894392130486208)}; + int64_t sarg {INT64_C(567894392130486207)}; + int64_t nsarg {INT64_C(-567894392130486207)}; + + QofInt128 big (barg); + QofInt128 small (sarg); + QofInt128 neg_big (nbarg); + QofInt128 neg_small (nsarg); + + QofInt128 really_big (barg, sarg); + QofInt128 slightly_bigger (barg, barg); + QofInt128 not_as_big (sarg, barg); + QofInt128 a_little_smaller (sarg, sarg); + + QofInt128 neg_really_big (barg, sarg, QofInt128::neg); + QofInt128 neg_slightly_bigger (barg, barg, QofInt128::neg); + QofInt128 neg_not_as_big (sarg, barg, QofInt128::neg); + QofInt128 neg_a_little_smaller (sarg, sarg, QofInt128::neg); + + QofInt128 overflowed (INT64_C(0), INT64_C(0), QofInt128::overflow); + QofInt128 not_a_number (INT64_C(0), INT64_C(0), QofInt128::NaN); + + EXPECT_EQ (-9, overflowed.cmp (big)); + EXPECT_EQ (-9, not_a_number.cmp (big)); + EXPECT_EQ (9, big.cmp (overflowed)); + EXPECT_EQ (9, big.cmp (not_a_number)); + + EXPECT_EQ (-1, neg_big.cmp(big)); + EXPECT_EQ (-1, neg_big.cmp(small)); + EXPECT_EQ (-1, neg_small.cmp(big)); + EXPECT_EQ (-2, neg_big.cmp(neg_small)); + EXPECT_EQ (2, neg_small.cmp(neg_big)); + + EXPECT_EQ (-4, small.cmp(big)); + EXPECT_EQ (4, big.cmp(small)); + EXPECT_EQ (1, small.cmp(neg_big)); + EXPECT_EQ (1, big.cmp(neg_small)); + EXPECT_EQ (-5, big.cmp(a_little_smaller)); + EXPECT_EQ (1, big.cmp(neg_a_little_smaller)); + + EXPECT_EQ (-4, really_big.cmp(slightly_bigger)); + EXPECT_EQ (5, really_big.cmp(not_as_big)); + EXPECT_EQ (-5, big.cmp(really_big)); + + EXPECT_EQ (-1, neg_really_big.cmp(big)); + EXPECT_EQ (-1, neg_really_big.cmp(not_as_big)); + EXPECT_EQ (-1, neg_really_big.cmp(slightly_bigger)); + EXPECT_EQ (-3, neg_really_big.cmp(neg_not_as_big)); + EXPECT_EQ (-3, neg_really_big.cmp(neg_a_little_smaller)); + EXPECT_EQ (2, neg_really_big.cmp(neg_slightly_bigger)); + EXPECT_EQ (3, neg_not_as_big.cmp(neg_really_big)); + EXPECT_EQ (-2, neg_not_as_big.cmp(neg_a_little_smaller)); + EXPECT_EQ (-3, neg_really_big.cmp(neg_a_little_smaller)); + + EXPECT_EQ (0, neg_really_big.cmp(QofInt128(barg, sarg, QofInt128::neg))); + EXPECT_EQ (0, really_big.cmp(QofInt128(barg, sarg))); + EXPECT_EQ (0, really_big.cmp(QofInt128(barg, sarg, QofInt128::pos))); + + EXPECT_EQ (0, really_big.cmp(-neg_really_big)); + EXPECT_EQ (0, neg_really_big.cmp(-really_big)); +} + +TEST(qofint128_functions, stream_output) +{ + int64_t barg {INT64_C(567894392130486208)}; + int64_t sarg {INT64_C(567894392130486207)}; + int64_t nsarg {INT64_C(-567894392130486207)}; + + QofInt128 small (sarg); + QofInt128 neg_small (nsarg); + + QofInt128 really_big (barg, sarg); + QofInt128 neg_really_big (barg, sarg, QofInt128::neg); + + QofInt128 overflowed (INT64_C(0), INT64_C(0), QofInt128::overflow); + QofInt128 not_a_number (INT64_C(0), INT64_C(0), QofInt128::NaN); + QofInt128 boundary_value (UINT64_C(1), UINT64_MAX); + + static const uint8_t char_buf_size {41}; + char buf[char_buf_size] {}; + + EXPECT_STREQ("567894392130486207", small.asCharBufR (buf)); + EXPECT_STREQ("-567894392130486207", neg_small.asCharBufR (buf)); + EXPECT_STREQ("5237901256262967342410779070006542271", really_big.asCharBufR (buf)); + EXPECT_STREQ("-5237901256262967342410779070006542271", neg_really_big.asCharBufR (buf)); + EXPECT_STREQ("36893488147419103231", boundary_value.asCharBufR (buf)); + EXPECT_STREQ("Overflow", overflowed.asCharBufR (buf)); + EXPECT_STREQ("NaN", not_a_number.asCharBufR (buf)); +} + +TEST(qofint128_functions, add_and_subtract) +{ + /* UINT64_MAX = 18,446,744,073,709,551,615 + * INT64_MAX = 9,223,372,036,854,775,807 + * barg + sarg = INT64_MAX + * barg + uarg = UINT64_MAX + */ + int64_t barg {INT64_C(4878849681579065407)}; + int64_t sarg {INT64_C(4344522355275710400)}; + uint64_t uarg {UINT64_C(13567894392130486208)}; + + QofInt128 smallest (sarg + 100); + QofInt128 smaller (barg + 500); + QofInt128 small (uarg); + QofInt128 big (sarg, barg); + QofInt128 bigger (static_cast(barg), uarg); + QofInt128 biggest (uarg, static_cast(barg)); + + EXPECT_EQ (QofInt128(INT64_C(2), INT64_C(499)), small += smaller); + EXPECT_EQ (QofInt128(uarg), small -= smaller); + EXPECT_EQ (QofInt128(static_cast(barg + sarg/2), UINT64_MAX), + bigger += big); + EXPECT_EQ (QofInt128(static_cast(barg), uarg), bigger -= big); + bigger += biggest; + EXPECT_EQ (QofInt128(UINT64_MAX, UINT64_MAX), bigger); + bigger += smallest; + EXPECT_TRUE (bigger.isOverflow()); + bigger -= biggest; + EXPECT_TRUE (bigger.isOverflow()); + } +