LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-numeric.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 60.3 % 730 440
Test Date: 2025-02-07 16:25:45 Functions: 88.5 % 61 54
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************
       2                 :             :  * gnc-numeric.c -- an exact-number library for accounting use      *
       3                 :             :  * Copyright (C) 2000 Bill Gribble                                  *
       4                 :             :  * Copyright (C) 2004 Linas Vepstas <linas@linas.org>               *
       5                 :             :  *                                                                  *
       6                 :             :  * This program is free software; you can redistribute it and/or    *
       7                 :             :  * modify it under the terms of the GNU General Public License as   *
       8                 :             :  * published by the Free Software Foundation; either version 2 of   *
       9                 :             :  * the License, or (at your option) any later version.              *
      10                 :             :  *                                                                  *
      11                 :             :  * This program is distributed in the hope that it will be useful,  *
      12                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      13                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      14                 :             :  * GNU General Public License for more details.                     *
      15                 :             :  *                                                                  *
      16                 :             :  * You should have received a copy of the GNU General Public License*
      17                 :             :  * along with this program; if not, contact:                        *
      18                 :             :  *                                                                  *
      19                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      20                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      21                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      22                 :             :  *                                                                  *
      23                 :             :  *******************************************************************/
      24                 :             : 
      25                 :             : #include <glib.h>
      26                 :             : 
      27                 :             : #include <cmath>
      28                 :             : #include <cstdio>
      29                 :             : #include <cstdlib>
      30                 :             : #include <cstring>
      31                 :             : #include <cstdint>
      32                 :             : #include <sstream>
      33                 :             : #include <boost/regex.hpp>
      34                 :             : #include <boost/locale/encoding_utf.hpp>
      35                 :             : 
      36                 :             : #include <config.h>
      37                 :             : #include <stdexcept>
      38                 :             : #include <stdint.h>
      39                 :             : #include "gnc-int128.hpp"
      40                 :             : #include "qof.h"
      41                 :             : 
      42                 :             : #include "gnc-numeric.hpp"
      43                 :             : #include "gnc-rational.hpp"
      44                 :             : 
      45                 :             : #include <optional>
      46                 :             : #include <charconv>
      47                 :             : 
      48                 :             : static QofLogModule log_module = "qof";
      49                 :             : 
      50                 :             : static const uint8_t max_leg_digits{18};
      51                 :             : static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
      52                 :             :                                10000000, 100000000, 1000000000,
      53                 :             :                                INT64_C(10000000000), INT64_C(100000000000),
      54                 :             :                                INT64_C(1000000000000), INT64_C(10000000000000),
      55                 :             :                                INT64_C(100000000000000),
      56                 :             :                                INT64_C(1000000000000000),
      57                 :             :                                INT64_C(10000000000000000),
      58                 :             :                                INT64_C(100000000000000000),
      59                 :             :                                INT64_C(1000000000000000000)};
      60                 :             : #define POWTEN_OVERFLOW -5
      61                 :             : 
      62                 :             : int64_t
      63                 :       51315 : powten (unsigned int exp)
      64                 :             : {
      65                 :       51315 :     if (exp > max_leg_digits)
      66                 :        1034 :         exp = max_leg_digits;
      67                 :       51315 :     return pten[exp];
      68                 :             : }
      69                 :             : 
      70                 :      256875 : GncNumeric::GncNumeric(GncRational rr)
      71                 :             : {
      72                 :             :     /* Can't use isValid here because we want to throw different exceptions. */
      73                 :      256875 :     if (rr.num().isNan() || rr.denom().isNan())
      74                 :           0 :         throw std::underflow_error("Operation resulted in NaN.");
      75                 :      256875 :     if (rr.num().isOverflow() || rr.denom().isOverflow())
      76                 :           0 :         throw std::overflow_error("Operation overflowed a 128-bit int.");
      77                 :      256875 :     if (rr.num().isBig() || rr.denom().isBig())
      78                 :             :     {
      79                 :          66 :         GncRational reduced(rr.reduce());
      80                 :          66 :         rr = reduced.round_to_numeric(); // A no-op if it's already small.
      81                 :             :     }
      82                 :      256847 :     m_num = static_cast<int64_t>(rr.num());
      83                 :      256847 :     m_den = static_cast<int64_t>(rr.denom());
      84                 :      256847 : }
      85                 :             : 
      86                 :         357 : GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
      87                 :             : {
      88                 :             :     static uint64_t max_leg_value{INT64_C(1000000000000000000)};
      89                 :         357 :     if (std::isnan(d) || fabs(d) > max_leg_value)
      90                 :             :     {
      91                 :           0 :         std::ostringstream msg;
      92                 :           0 :         msg << "Unable to construct a GncNumeric from " << d << ".\n";
      93                 :           0 :         throw std::invalid_argument(msg.str());
      94                 :           0 :     }
      95                 :         357 :     constexpr auto max_num = static_cast<double>(INT64_MAX);
      96                 :         357 :     auto logval = log10(fabs(d));
      97                 :             :     int64_t den;
      98                 :             :     uint8_t den_digits;
      99                 :         357 :     if (logval > 0.0)
     100                 :         328 :         den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval));
     101                 :             :     else
     102                 :          29 :         den_digits = max_leg_digits;
     103                 :         357 :     den = powten(den_digits);
     104                 :         357 :     auto num_d = static_cast<double>(den) * d;
     105                 :         629 :     while (fabs(num_d) > max_num && den_digits > 1)
     106                 :             :     {
     107                 :         272 :         den = powten(--den_digits);
     108                 :         272 :         num_d = static_cast<double>(den) * d;
     109                 :             :     }
     110                 :         357 :     auto num = static_cast<int64_t>(floor(num_d));
     111                 :             : 
     112                 :         357 :     if (num == 0)
     113                 :           6 :         return;
     114                 :         351 :     GncNumeric q(num, den);
     115                 :         351 :     auto r = q.reduce();
     116                 :         351 :     m_num = r.num();
     117                 :         351 :     m_den = r.denom();
     118                 :             : }
     119                 :             : 
     120                 :             : using boost::regex;
     121                 :             : using boost::regex_search;
     122                 :             : using boost::smatch;
     123                 :             : 
     124                 :             : 
     125                 :             : static std::pair<int64_t, int64_t>
     126                 :          14 : reduce_number_pair(std::pair<GncInt128, GncInt128>num_pair,
     127                 :             :                    const std::string& num_str, bool autoround)
     128                 :             : {
     129                 :          14 :     auto [n, d] = num_pair;
     130                 :          14 :     if (!autoround && n.isBig()) {
     131                 :           0 :         std::ostringstream errmsg;
     132                 :             :         errmsg << "Decimal string " << num_str
     133                 :           0 :                << "can't be represented in a GncNumeric without rounding.";
     134                 :           0 :         throw std::overflow_error(errmsg.str());
     135                 :           0 :     }
     136                 :          14 :     while (n.isBig() && d > 0) {
     137                 :           0 :         n >>= 1;
     138                 :           0 :         d >>= 1;
     139                 :             :     }
     140                 :          14 :     if (n.isBig()) // Shouldn't happen, of course
     141                 :             :     {
     142                 :           0 :         std::ostringstream errmsg;
     143                 :             :         errmsg << "Decimal string " << num_str
     144                 :             :                << " can't be represented in a GncNumeric, even after reducing "
     145                 :           0 :             "denom to "
     146                 :           0 :                << d;
     147                 :           0 :         throw std::overflow_error(errmsg.str());
     148                 :           0 :     }
     149                 :          14 :     return std::make_pair(static_cast<int64_t>(n), static_cast<int64_t>(d));
     150                 :             : }
     151                 :             : 
     152                 :             : static std::pair<GncInt128, int64_t>
     153                 :          14 : numeric_from_decimal_match(const std::string& integer, const std::string& decimal)
     154                 :             : {
     155                 :          14 :     auto neg = (!integer.empty() && integer[0] == '-');
     156                 :          14 :     GncInt128 high((neg && integer.length() > 1) || (!neg && !integer.empty())
     157                 :          13 :                    ? stoll(integer)
     158                 :          28 :                    : 0);
     159                 :          14 :     GncInt128 low{ decimal.empty() ? 0 : stoll(decimal)};
     160                 :          14 :     auto exp10 = decimal.length();
     161                 :          14 :     int64_t d = powten(exp10);
     162                 :          14 :     GncInt128 n = high * d + (neg ? -low : low);
     163                 :          14 :     while (exp10 > max_leg_digits) {
     164                 :             :         /* If the arg to powten is bigger than max_leg_digits
     165                 :             :            it returns 10**max_leg_digits so reduce exp10 by
     166                 :             :            that amount */
     167                 :           0 :         n = n / powten(exp10 - max_leg_digits);
     168                 :           0 :         exp10 -= max_leg_digits;
     169                 :             :     }
     170                 :          28 :     return std::make_pair(n, d);
     171                 :             : }
     172                 :             : 
     173                 :             : static std::pair<GncInt128, GncInt128>
     174                 :           0 : numeric_from_scientific_match(smatch &m)
     175                 :             : {
     176                 :           0 :     int exp{m[4].matched ? stoi(m[4].str()) : 0};
     177                 :           0 :     auto neg_exp{exp < 0};
     178                 :           0 :     exp = neg_exp ? -exp : exp;
     179                 :           0 :     if (exp >= max_leg_digits)
     180                 :             :     {
     181                 :           0 :         std::ostringstream errmsg;
     182                 :           0 :         errmsg << "Exponent " << m[3].str() << " in match " << m[0].str()
     183                 :           0 :                << " exceeds range that GnuCash can parse.";
     184                 :           0 :         throw std::overflow_error(errmsg.str());
     185                 :           0 :     }
     186                 :             : 
     187                 :           0 :     GncInt128 num, denom;
     188                 :           0 :     auto mult = powten(exp);
     189                 :             : 
     190                 :           0 :     if (m[1].matched)
     191                 :             :     {
     192                 :           0 :         denom = neg_exp ? mult : 1;
     193                 :           0 :         num = neg_exp ? stoll(m[1].str()) : mult * stoll(m[1].str());
     194                 :             :     }
     195                 :             :     else
     196                 :             :     {
     197                 :           0 :         auto [d_num, d_denom] = numeric_from_decimal_match(m[2].str(), m[3].str());
     198                 :             : 
     199                 :           0 :         if (neg_exp || d_denom > mult)
     200                 :             :         {
     201                 :           0 :             num = d_num;
     202                 :           0 :             denom = neg_exp ? d_denom * mult : d_denom / mult;
     203                 :             :         }
     204                 :             :         else
     205                 :             :         {
     206                 :           0 :             num = d_num * mult / d_denom;
     207                 :           0 :             denom = 1;
     208                 :             :         }
     209                 :             :     }
     210                 :           0 :     return std::make_pair(num, denom);
     211                 :             : }
     212                 :             : 
     213                 :             : static std::optional<gnc_numeric>
     214                 :        5162 : fast_numeral_rational (const char* str)
     215                 :             : {
     216                 :        5162 :     if (!str || !str[0])
     217                 :           1 :         return {};
     218                 :             : 
     219                 :             :     // because minint64 = -9223372036854775808 and has 20 characters,
     220                 :             :     // the maximum strlen to handle is 20+19+1 = 40. 48 is a nice
     221                 :             :     // number in 64-bit.
     222                 :        5161 :     auto end_ptr{(const char*)memchr (str, '\0', 48)};
     223                 :        5161 :     if (!end_ptr)
     224                 :           0 :         return {};
     225                 :             : 
     226                 :        5161 :     int64_t num, denom{};
     227                 :        5161 :     auto result = std::from_chars (str, end_ptr, num);
     228                 :        5161 :     if (result.ec != std::errc())
     229                 :           6 :         return {};
     230                 :             : 
     231                 :        5155 :     if (result.ptr == end_ptr)
     232                 :           1 :         return gnc_numeric_create (num, 1);
     233                 :             : 
     234                 :        5154 :     if (*result.ptr != '/')
     235                 :          20 :         return {};
     236                 :             : 
     237                 :        5134 :     result = std::from_chars (result.ptr + 1, end_ptr, denom);
     238                 :        5134 :     if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
     239                 :           0 :         return {};
     240                 :             : 
     241                 :        5134 :     return gnc_numeric_create (num, denom);
     242                 :             : }
     243                 :             : 
     244                 :          19 : GncNumeric::GncNumeric(const std::string &str, bool autoround) {
     245                 :          27 :     static const std::string maybe_sign ("(-?)");
     246                 :          27 :     static const std::string opt_signed_int("(-?[0-9]*)");
     247                 :          27 :     static const std::string unsigned_int("([0-9]+)");
     248                 :          27 :     static const std::string hex_frag("(0[xX][A-Fa-f0-9]+)");
     249                 :          27 :     static const std::string slash("[ \\t]*/[ \\t]*");
     250                 :          27 :     static const std::string whitespace("[ \\t]+");
     251                 :             :     /* The llvm standard C++ library refused to recognize the - in the
     252                 :             :      * opt_signed_int pattern with the default ECMAScript syntax so we use the
     253                 :             :      * awk syntax.
     254                 :             :      */
     255                 :          19 :     static const regex numeral(opt_signed_int);
     256                 :          19 :     static const regex hex(hex_frag);
     257                 :          19 :     static const regex numeral_rational(opt_signed_int + slash + unsigned_int);
     258                 :          19 :     static const regex integer_and_fraction(maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int);
     259                 :          19 :     static const regex hex_rational(hex_frag + slash + hex_frag);
     260                 :          19 :     static const regex hex_over_num(hex_frag + slash + unsigned_int);
     261                 :          19 :     static const regex num_over_hex(opt_signed_int + slash + hex_frag);
     262                 :          19 :     static const regex decimal(opt_signed_int + "[.,]" + unsigned_int);
     263                 :          19 :     static const regex scientific("(?:(-?[0-9]+[.,]?)|(-?[0-9]*)[.,]([0-9]+))[Ee](-?[0-9]+)");
     264                 :          19 :     static const regex has_hex_prefix(".*0[xX]$");
     265                 :          57 :     smatch m, x;
     266                 :             :     /* The order of testing the regexes is from the more restrictve to the less
     267                 :             :      * restrictive, as less-restrictive ones will match  patterns that would also
     268                 :             :      * match the more-restrictive and so invoke the wrong construction.
     269                 :             :      */
     270                 :          19 :     if (str.empty())
     271                 :             :         throw std::invalid_argument(
     272                 :           1 :             "Can't construct a GncNumeric from an empty string.");
     273                 :          18 :     if (auto res = fast_numeral_rational (str.c_str()))
     274                 :             :     {
     275                 :           0 :         m_num = res->num;
     276                 :           0 :         m_den = res->denom;
     277                 :           0 :         return;
     278                 :             :     }
     279                 :          18 :     if (regex_search(str, m, hex_rational))
     280                 :             :     {
     281                 :           0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16),
     282                 :           0 :                      stoll(m[2].str(), nullptr, 16));
     283                 :             : 
     284                 :           0 :         m_num = n.num();
     285                 :           0 :         m_den = n.denom();
     286                 :           0 :         return;
     287                 :             :     }
     288                 :          18 :     if (regex_search(str, m, hex_over_num))
     289                 :             :     {
     290                 :           0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16), stoll(m[2].str()));
     291                 :           0 :         m_num = n.num();
     292                 :           0 :         m_den = n.denom();
     293                 :           0 :         return;
     294                 :             :     }
     295                 :          18 :     if (regex_search(str, m, num_over_hex))
     296                 :             :     {
     297                 :           0 :         GncNumeric n(stoll(m[1].str()), stoll(m[2].str(), nullptr, 16));
     298                 :           0 :         m_num = n.num();
     299                 :           0 :         m_den = n.denom();
     300                 :           0 :         return;
     301                 :             :     }
     302                 :          18 :     if (regex_search(str, m, integer_and_fraction))
     303                 :             :     {
     304                 :           2 :         GncNumeric n(stoll(m[3].str()), stoll(m[4].str()));
     305                 :           2 :         n += stoll(m[2].str());
     306                 :           2 :         m_num = m[1].str().empty() ? n.num() : -n.num();
     307                 :           2 :         m_den = n.denom();
     308                 :           2 :         return;
     309                 :             :     }
     310                 :          16 :     if (regex_search(str, m, numeral_rational))
     311                 :             :     {
     312                 :           1 :         GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
     313                 :           1 :         m_num = n.num();
     314                 :           1 :         m_den = n.denom();
     315                 :           1 :         return;
     316                 :             :     }
     317                 :          15 :     if (regex_search(str, m, scientific) && ! regex_match(m.prefix().str(), x,  has_hex_prefix))
     318                 :             :     {
     319                 :           0 :         auto [num, denom] =
     320                 :           0 :             reduce_number_pair(numeric_from_scientific_match(m),
     321                 :             :                                    str, autoround);
     322                 :           0 :         m_num = num;
     323                 :           0 :         m_den = denom;
     324                 :           0 :         return;
     325                 :             :     }
     326                 :          15 :     if (regex_search(str, m, decimal))
     327                 :             :     {
     328                 :          14 :         std::string integer{m[1].matched ? m[1].str() : ""};
     329                 :          14 :         std::string decimal{m[2].matched ? m[2].str() : ""};
     330                 :          14 :         auto [num, denom] =
     331                 :          14 :             reduce_number_pair(numeric_from_decimal_match(integer, decimal),
     332                 :             :                                str, autoround);
     333                 :          14 :         m_num = num;
     334                 :          14 :         m_den = denom;
     335                 :          14 :         return;
     336                 :          14 :     }
     337                 :           1 :     if (regex_search(str, m, hex))
     338                 :             :     {
     339                 :           0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16), INT64_C(1));
     340                 :           0 :         m_num = n.num();
     341                 :           0 :         m_den = n.denom();
     342                 :           0 :         return;
     343                 :             :     }
     344                 :           1 :     if (regex_search(str, m, numeral))
     345                 :             :     {
     346                 :           2 :         GncNumeric n(stoll(m[1].str()), INT64_C(1));
     347                 :           0 :         m_num = n.num();
     348                 :           0 :         m_den = n.denom();
     349                 :           0 :         return;
     350                 :             :     }
     351                 :           0 :     std::ostringstream errmsg;
     352                 :           0 :     errmsg << "String " << str << " contains no recognizable numeric value.";
     353                 :           0 :     throw std::invalid_argument(errmsg.str());
     354                 :          21 : }
     355                 :             : 
     356                 :      539203 : GncNumeric::operator gnc_numeric() const noexcept
     357                 :             : {
     358                 :      539203 :     return {m_num, m_den};
     359                 :             : }
     360                 :             : 
     361                 :           0 : GncNumeric::operator double() const noexcept
     362                 :             : {
     363                 :           0 :     return static_cast<double>(m_num) / static_cast<double>(m_den);
     364                 :             : }
     365                 :             : 
     366                 :             : GncNumeric
     367                 :       70542 : GncNumeric::operator-() const noexcept
     368                 :             : {
     369                 :       70542 :     GncNumeric b(*this);
     370                 :       70542 :     b.m_num = - b.m_num;
     371                 :       70542 :     return b;
     372                 :             : }
     373                 :             : 
     374                 :             : GncNumeric
     375                 :         708 : GncNumeric::inv() const noexcept
     376                 :             : {
     377                 :         708 :     if (m_num == 0)
     378                 :           0 :         return *this;
     379                 :         708 :     if (m_num < 0)
     380                 :           0 :         return GncNumeric(-m_den, -m_num);
     381                 :         708 :     return GncNumeric(m_den, m_num);
     382                 :             : }
     383                 :             : 
     384                 :             : GncNumeric
     385                 :           0 : GncNumeric::abs() const noexcept
     386                 :             : {
     387                 :           0 :     if (m_num < 0)
     388                 :           0 :         return -*this;
     389                 :           0 :     return *this;
     390                 :             : }
     391                 :             : 
     392                 :             : GncNumeric
     393                 :        4382 : GncNumeric::reduce() const noexcept
     394                 :             : {
     395                 :        4382 :     return static_cast<GncNumeric>(GncRational(*this).reduce());
     396                 :             : }
     397                 :             : 
     398                 :             : GncNumeric::round_param
     399                 :      503122 : GncNumeric::prepare_conversion(int64_t new_denom) const
     400                 :             : {
     401                 :      503122 :     if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
     402                 :      411582 :         return {m_num, m_den, 0};
     403                 :       91540 :     GncRational conversion(new_denom, m_den);
     404                 :       91540 :     auto red_conv = conversion.reduce();
     405                 :       91540 :     GncInt128 old_num(m_num);
     406                 :       91540 :     auto new_num = old_num * red_conv.num();
     407                 :       91540 :     auto rem = new_num % red_conv.denom();
     408                 :       91540 :     new_num /= red_conv.denom();
     409                 :       91540 :     if (new_num.isBig())
     410                 :             :     {
     411                 :          15 :         GncRational rr(new_num, new_denom);
     412                 :          15 :         GncNumeric nn(rr);
     413                 :          15 :         rr = rr.convert<RoundType::truncate>(new_denom);
     414                 :          15 :         return {static_cast<int64_t>(rr.num()), new_denom, 0};
     415                 :             :     }
     416                 :       91525 :     return {static_cast<int64_t>(new_num),
     417                 :       91525 :             static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
     418                 :             : }
     419                 :             : 
     420                 :             : int64_t
     421                 :          33 : GncNumeric::sigfigs_denom(unsigned figs) const noexcept
     422                 :             : {
     423                 :          33 :     if (m_num == 0)
     424                 :           4 :         return 1;
     425                 :             : 
     426                 :          29 :     int64_t num_abs{std::abs(m_num)};
     427                 :          29 :     bool not_frac = num_abs > m_den;
     428                 :          29 :     int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
     429                 :          29 :     unsigned digits{};
     430                 :          44 :     while (val >= 10)
     431                 :             :     {
     432                 :          15 :         ++digits;
     433                 :          15 :         val /= 10;
     434                 :             :     }
     435                 :          29 :     return not_frac ? 
     436                 :          23 :             powten(digits < figs ? figs - digits - 1 : 0) : 
     437                 :          29 :             powten(figs + digits);
     438                 :             : }
     439                 :             : 
     440                 :             : std::string
     441                 :           0 : GncNumeric::to_string() const noexcept
     442                 :             : {
     443                 :           0 :     std::ostringstream out;
     444                 :           0 :     out << *this;
     445                 :           0 :     return out.str();
     446                 :           0 : }
     447                 :             : 
     448                 :             : bool
     449                 :       32341 : GncNumeric::is_decimal() const noexcept
     450                 :             : {
     451                 :       65837 :     for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
     452                 :             :     {
     453                 :       65065 :         if (m_den == pten[pwr])
     454                 :       31277 :             return true;
     455                 :       33788 :         if (m_den % pten[pwr])
     456                 :         292 :             return false;
     457                 :             :     }
     458                 :         772 :     return false;
     459                 :             : }
     460                 :             : 
     461                 :             : GncNumeric
     462                 :       32061 : GncNumeric::to_decimal(unsigned int max_places) const
     463                 :             : {
     464                 :       32061 :     if (max_places > max_leg_digits)
     465                 :           0 :         max_places = max_leg_digits;
     466                 :             : 
     467                 :       32061 :     if (m_num == 0)
     468                 :           0 :         return GncNumeric();
     469                 :             : 
     470                 :       32061 :     if (is_decimal())
     471                 :             :     {
     472                 :       30997 :         if (m_num == 0 || m_den < powten(max_places))
     473                 :       30997 :             return *this; // Nothing to do.
     474                 :             :         /* See if we can reduce m_num to fit in max_places */
     475                 :           0 :         auto excess = m_den / powten(max_places);
     476                 :           0 :         if (m_num % excess)
     477                 :             :         {
     478                 :           0 :             std::ostringstream msg;
     479                 :           0 :             msg << "GncNumeric " << *this
     480                 :           0 :                 << " could not be represented in " << max_places
     481                 :           0 :                 << " decimal places without rounding.\n";
     482                 :           0 :             throw std::range_error(msg.str());
     483                 :           0 :         }
     484                 :           0 :         return GncNumeric(m_num / excess, powten(max_places));
     485                 :             :     }
     486                 :        1064 :     GncRational rr(*this);
     487                 :        1064 :     rr = rr.convert<RoundType::never>(powten(max_places)); //May throw
     488                 :             :     /* rr might have gotten reduced a bit too much; if so, put it back: */
     489                 :         978 :     unsigned int pwr{1};
     490                 :       18582 :     for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
     491                 :         978 :     auto reduce_to = powten(pwr);
     492                 :         978 :     GncInt128 rr_num(rr.num()), rr_den(rr.denom());
     493                 :         978 :     if (rr_den % reduce_to)
     494                 :             :     {
     495                 :           0 :         auto factor(reduce_to / rr.denom());
     496                 :           0 :         rr_num *= factor;
     497                 :           0 :         rr_den *= factor;
     498                 :             :     }
     499                 :       16819 :     while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
     500                 :             :     {
     501                 :       15841 :         rr_num /= 10;
     502                 :       15841 :         rr_den /= 10;
     503                 :             :     }
     504                 :             :     try
     505                 :             :     {
     506                 :             :         /* Construct from the parts to avoid the GncRational constructor's
     507                 :             :          * automatic rounding.
     508                 :             :          */
     509                 :         978 :         return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
     510                 :             :     }
     511                 :           0 :     catch (const std::invalid_argument& err)
     512                 :             :     {
     513                 :           0 :         std::ostringstream msg;
     514                 :           0 :         msg << "GncNumeric " << *this
     515                 :           0 :             << " could not be represented as a decimal without rounding.\n";
     516                 :           0 :         throw std::range_error(msg.str());
     517                 :           0 :     }
     518                 :           0 :     catch (const std::overflow_error& err)
     519                 :             :     {
     520                 :           0 :         std::ostringstream msg;
     521                 :           0 :         msg << "GncNumeric " << *this
     522                 :           0 :             << " overflows when attempting to convert it to decimal.\n";
     523                 :           0 :         throw std::range_error(msg.str());
     524                 :           0 :     }
     525                 :           0 :     catch (const std::underflow_error& err)
     526                 :             :     {
     527                 :           0 :         std::ostringstream msg;
     528                 :           0 :         msg << "GncNumeric " << *this
     529                 :           0 :             << " underflows when attempting to convert it to decimal.\n";
     530                 :           0 :         throw std::range_error(msg.str());
     531                 :           0 :     }
     532                 :             : }
     533                 :             : 
     534                 :             : void
     535                 :         188 : GncNumeric::operator+=(GncNumeric b)
     536                 :             : {
     537                 :         188 :     *this = *this + b;
     538                 :         188 : }
     539                 :             : 
     540                 :             : void
     541                 :           0 : GncNumeric::operator-=(GncNumeric b)
     542                 :             : {
     543                 :           0 :     *this = *this - b;
     544                 :           0 : }
     545                 :             : 
     546                 :             : void
     547                 :           0 : GncNumeric::operator*=(GncNumeric b)
     548                 :             : {
     549                 :           0 :     *this = *this * b;
     550                 :           0 : }
     551                 :             : 
     552                 :             : void
     553                 :           0 : GncNumeric::operator/=(GncNumeric b)
     554                 :             : {
     555                 :           0 :     *this = *this / b;
     556                 :           0 : }
     557                 :             : 
     558                 :             : int
     559                 :        2680 : GncNumeric::cmp(GncNumeric b)
     560                 :             : {
     561                 :        2680 :     if (m_den == b.denom())
     562                 :             :     {
     563                 :          10 :         auto b_num = b.num();
     564                 :          10 :         return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
     565                 :             :     }
     566                 :        2670 :     GncRational an(*this), bn(b);
     567                 :        2670 :     return an.cmp(bn);
     568                 :             : }
     569                 :             : 
     570                 :             : GncNumeric
     571                 :      322734 : operator+(GncNumeric a, GncNumeric b)
     572                 :             : {
     573                 :      322734 :     if (a.num() == 0)
     574                 :       64599 :         return b;
     575                 :      258135 :     if (b.num() == 0)
     576                 :        8037 :         return a;
     577                 :      250098 :     GncRational ar(a), br(b);
     578                 :      250098 :     auto rr = ar + br;
     579                 :      250098 :     return static_cast<GncNumeric>(rr);
     580                 :             : }
     581                 :             : 
     582                 :             : GncNumeric
     583                 :       70542 : operator-(GncNumeric a, GncNumeric b)
     584                 :             : {
     585                 :       70542 :     return a + (-b);
     586                 :             : }
     587                 :             : 
     588                 :             : GncNumeric
     589                 :        4282 : operator*(GncNumeric a, GncNumeric b)
     590                 :             : {
     591                 :        4282 :     if (a.num() == 0 || b.num() == 0)
     592                 :             :     {
     593                 :        3189 :         GncNumeric retval;
     594                 :        3189 :         return retval;
     595                 :             :     }
     596                 :        1093 :     GncRational ar(a), br(b);
     597                 :        1093 :     auto rr = ar * br;
     598                 :        1093 :     return static_cast<GncNumeric>(rr);
     599                 :             : }
     600                 :             : 
     601                 :             : GncNumeric
     602                 :        1298 : operator/(GncNumeric a, GncNumeric b)
     603                 :             : {
     604                 :        1298 :     if (a.num() == 0)
     605                 :             :     {
     606                 :          10 :         GncNumeric retval;
     607                 :          10 :         return retval;
     608                 :             :     }
     609                 :        1288 :     if (b.num() == 0)
     610                 :           1 :         throw std::underflow_error("Attempt to divide by zero.");
     611                 :             : 
     612                 :        1287 :     GncRational ar(a), br(b);
     613                 :        1287 :     auto rr = ar / br;
     614                 :        1287 :     return static_cast<GncNumeric>(rr);
     615                 :             : }
     616                 :             : 
     617                 :             : template <typename T, typename I> T
     618                 :      508126 : convert(T num, I new_denom, int how)
     619                 :             : {
     620                 :      508126 :     auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
     621                 :      508126 :     unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
     622                 :             : 
     623                 :      508126 :     auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
     624                 :      508126 :     bool sigfigs = dtype == DenomType::sigfigs;
     625                 :      508126 :     if (dtype == DenomType::reduce)
     626                 :         740 :         num = num.reduce();
     627                 :             : 
     628                 :      508126 :     switch (rtype)
     629                 :             :     {
     630                 :           1 :         case RoundType::floor:
     631                 :           1 :             if (sigfigs)
     632                 :           0 :                 return num.template convert_sigfigs<RoundType::floor>(figs);
     633                 :             :             else
     634                 :           1 :                 return num.template convert<RoundType::floor>(new_denom);
     635                 :           1 :         case RoundType::ceiling:
     636                 :           1 :             if (sigfigs)
     637                 :           0 :                 return num.template convert_sigfigs<RoundType::ceiling>(figs);
     638                 :             :             else
     639                 :           1 :                 return num.template convert<RoundType::ceiling>(new_denom);
     640                 :       65337 :         case RoundType::truncate:
     641                 :       65337 :             if (sigfigs)
     642                 :           0 :                 return num.template convert_sigfigs<RoundType::truncate>(figs);
     643                 :             :             else
     644                 :       65337 :                 return num.template convert<RoundType::truncate>(new_denom);
     645                 :           0 :         case RoundType::promote:
     646                 :           0 :             if (sigfigs)
     647                 :           0 :                 return num.template convert_sigfigs<RoundType::promote>(figs);
     648                 :             :             else
     649                 :           0 :                 return num.template convert<RoundType::promote>(new_denom);
     650                 :           0 :         case RoundType::half_down:
     651                 :           0 :             if (sigfigs)
     652                 :           0 :                 return num.template convert_sigfigs<RoundType::half_down>(figs);
     653                 :             :             else
     654                 :           0 :                 return num.template convert<RoundType::half_down>(new_denom);
     655                 :       42595 :         case RoundType::half_up:
     656                 :       42595 :             if (sigfigs)
     657                 :          25 :                 return num.template convert_sigfigs<RoundType::half_up>(figs);
     658                 :             :             else
     659                 :       42570 :                 return num.template convert<RoundType::half_up>(new_denom);
     660                 :       80587 :         case RoundType::bankers:
     661                 :       80587 :             if (sigfigs)
     662                 :           8 :                 return num.template convert_sigfigs<RoundType::bankers>(figs);
     663                 :             :             else
     664                 :       80579 :                 return num.template convert<RoundType::bankers>(new_denom);
     665                 :      314128 :         case RoundType::never:
     666                 :      314128 :             if (sigfigs)
     667                 :           0 :                 return num.template convert_sigfigs<RoundType::never>(figs);
     668                 :             :             else
     669                 :      314128 :                 return num.template convert<RoundType::never>(new_denom);
     670                 :        5477 :         default:
     671                 :             :             /* round-truncate just returns the numerator unchanged. The old
     672                 :             :              * gnc-numeric convert had no "default" behavior at rounding that
     673                 :             :              * had the same result, but we need to make it explicit here to
     674                 :             :              * run the rest of the conversion code.
     675                 :             :              */
     676                 :        5477 :             if (sigfigs)
     677                 :           0 :                 return num.template convert_sigfigs<RoundType::truncate>(figs);
     678                 :             :             else
     679                 :        5477 :                 return num.template convert<RoundType::truncate>(new_denom);
     680                 :             : 
     681                 :             :     }
     682                 :             : }
     683                 :             : 
     684                 :             : /* =============================================================== */
     685                 :             : /* This function is small, simple, and used everywhere below,
     686                 :             :  * lets try to inline it.
     687                 :             :  */
     688                 :             : GNCNumericErrorCode
     689                 :     1759285 : gnc_numeric_check(gnc_numeric in)
     690                 :             : {
     691                 :     1759285 :     if (G_LIKELY(in.denom != 0))
     692                 :             :     {
     693                 :     1759115 :         return GNC_ERROR_OK;
     694                 :             :     }
     695                 :         170 :     else if (in.num)
     696                 :             :     {
     697                 :         129 :         if ((0 < in.num) || (-4 > in.num))
     698                 :             :         {
     699                 :           6 :             in.num = (gint64) GNC_ERROR_OVERFLOW;
     700                 :             :         }
     701                 :         129 :         return (GNCNumericErrorCode) in.num;
     702                 :             :     }
     703                 :             :     else
     704                 :             :     {
     705                 :          41 :         return GNC_ERROR_ARG;
     706                 :             :     }
     707                 :             : }
     708                 :             : 
     709                 :             : 
     710                 :             : /* *******************************************************************
     711                 :             :  *  gnc_numeric_zero_p
     712                 :             :  ********************************************************************/
     713                 :             : 
     714                 :             : gboolean
     715                 :      186951 : gnc_numeric_zero_p(gnc_numeric a)
     716                 :             : {
     717                 :      186951 :     if (gnc_numeric_check(a))
     718                 :             :     {
     719                 :           1 :         return 0;
     720                 :             :     }
     721                 :             :     else
     722                 :             :     {
     723                 :      186950 :         if ((a.num == 0) && (a.denom != 0))
     724                 :             :         {
     725                 :      127440 :             return 1;
     726                 :             :         }
     727                 :             :         else
     728                 :             :         {
     729                 :       59510 :             return 0;
     730                 :             :         }
     731                 :             :     }
     732                 :             : }
     733                 :             : 
     734                 :             : /* *******************************************************************
     735                 :             :  *  gnc_numeric_negative_p
     736                 :             :  ********************************************************************/
     737                 :             : 
     738                 :             : gboolean
     739                 :      213966 : gnc_numeric_negative_p(gnc_numeric a)
     740                 :             : {
     741                 :      213966 :     if (gnc_numeric_check(a))
     742                 :             :     {
     743                 :           0 :         return 0;
     744                 :             :     }
     745                 :             :     else
     746                 :             :     {
     747                 :      213966 :         if ((a.num < 0) && (a.denom != 0))
     748                 :             :         {
     749                 :       41144 :             return 1;
     750                 :             :         }
     751                 :             :         else
     752                 :             :         {
     753                 :      172822 :             return 0;
     754                 :             :         }
     755                 :             :     }
     756                 :             : }
     757                 :             : 
     758                 :             : /* *******************************************************************
     759                 :             :  *  gnc_numeric_positive_p
     760                 :             :  ********************************************************************/
     761                 :             : 
     762                 :             : gboolean
     763                 :        2220 : gnc_numeric_positive_p(gnc_numeric a)
     764                 :             : {
     765                 :        2220 :     if (gnc_numeric_check(a))
     766                 :             :     {
     767                 :           0 :         return 0;
     768                 :             :     }
     769                 :             :     else
     770                 :             :     {
     771                 :        2220 :         if ((a.num > 0) && (a.denom != 0))
     772                 :             :         {
     773                 :        1352 :             return 1;
     774                 :             :         }
     775                 :             :         else
     776                 :             :         {
     777                 :         868 :             return 0;
     778                 :             :         }
     779                 :             :     }
     780                 :             : }
     781                 :             : 
     782                 :             : 
     783                 :             : /* *******************************************************************
     784                 :             :  *  gnc_numeric_compare
     785                 :             :  *  returns 1 if a>b, -1 if b>a, 0 if a == b
     786                 :             :  ********************************************************************/
     787                 :             : 
     788                 :             : int
     789                 :       24137 : gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
     790                 :             : {
     791                 :       24137 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     792                 :             :     {
     793                 :           0 :         return 0;
     794                 :             :     }
     795                 :             : 
     796                 :       24137 :     if (a.denom == b.denom)
     797                 :             :     {
     798                 :       21467 :         if (a.num == b.num) return 0;
     799                 :       10335 :         if (a.num > b.num) return 1;
     800                 :        4453 :         return -1;
     801                 :             :     }
     802                 :             : 
     803                 :        2670 :     GncNumeric an (a), bn (b);
     804                 :             : 
     805                 :        2670 :     return an.cmp(bn);
     806                 :             : }
     807                 :             : 
     808                 :             : 
     809                 :             : /* *******************************************************************
     810                 :             :  *  gnc_numeric_eq
     811                 :             :  ********************************************************************/
     812                 :             : 
     813                 :             : gboolean
     814                 :        3397 : gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
     815                 :             : {
     816                 :        3397 :     return ((a.num == b.num) && (a.denom == b.denom));
     817                 :             : }
     818                 :             : 
     819                 :             : 
     820                 :             : /* *******************************************************************
     821                 :             :  *  gnc_numeric_equal
     822                 :             :  ********************************************************************/
     823                 :             : 
     824                 :             : gboolean
     825                 :       11573 : gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
     826                 :             : {
     827                 :       11573 :     if (gnc_numeric_check(a))
     828                 :             :     {
     829                 :             :         /* a is not a valid number, check b */
     830                 :           0 :         if (gnc_numeric_check(b))
     831                 :             :             /* Both invalid, consider them equal */
     832                 :           0 :             return TRUE;
     833                 :             :         else
     834                 :             :             /* a invalid, b valid */
     835                 :           0 :             return FALSE;
     836                 :             :     }
     837                 :       11573 :     if (gnc_numeric_check(b))
     838                 :             :         /* a valid, b invalid */
     839                 :           0 :         return FALSE;
     840                 :             : 
     841                 :       11573 :     return gnc_numeric_compare (a, b) == 0;
     842                 :             : }
     843                 :             : 
     844                 :             : 
     845                 :             : /* *******************************************************************
     846                 :             :  *  gnc_numeric_same
     847                 :             :  *  would a and b be equal() if they were both converted to the same
     848                 :             :  *  denominator?
     849                 :             :  ********************************************************************/
     850                 :             : 
     851                 :             : int
     852                 :        4951 : gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
     853                 :             :                  gint how)
     854                 :             : {
     855                 :             :     gnc_numeric aconv, bconv;
     856                 :             : 
     857                 :        4951 :     aconv = gnc_numeric_convert(a, denom, how);
     858                 :        4951 :     bconv = gnc_numeric_convert(b, denom, how);
     859                 :             : 
     860                 :        9902 :     return(gnc_numeric_equal(aconv, bconv));
     861                 :             : }
     862                 :             : 
     863                 :             : static int64_t
     864                 :      339629 : denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
     865                 :             : {
     866                 :      339629 :     if (denom == GNC_DENOM_AUTO &&
     867                 :      330843 :         (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
     868                 :             :     {
     869                 :         734 :         GncInt128 ad(a.denom), bd(b.denom);
     870                 :         734 :         denom = static_cast<int64_t>(ad.lcm(bd));
     871                 :             :     }
     872                 :      339629 :     return denom;
     873                 :             : }
     874                 :             : 
     875                 :             : /* *******************************************************************
     876                 :             :  *  gnc_numeric_add
     877                 :             :  ********************************************************************/
     878                 :             : 
     879                 :             : gnc_numeric
     880                 :      259009 : gnc_numeric_add(gnc_numeric a, gnc_numeric b,
     881                 :             :                 gint64 denom, gint how)
     882                 :             : {
     883                 :      259009 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     884                 :             :     {
     885                 :           3 :         return gnc_numeric_error(GNC_ERROR_ARG);
     886                 :             :     }
     887                 :             :     try
     888                 :             :     {
     889                 :      259006 :         denom = denom_lcd(a, b, denom, how);
     890                 :      259006 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
     891                 :             :         {
     892                 :      252004 :             GncNumeric an (a), bn (b);
     893                 :      252004 :             GncNumeric sum = an + bn;
     894                 :      252004 :             return static_cast<gnc_numeric>(convert(sum, denom, how));
     895                 :             :         }
     896                 :        7002 :         GncRational ar(a), br(b);
     897                 :        7002 :         auto sum = ar + br;
     898                 :        7002 :         if (denom == GNC_DENOM_AUTO &&
     899                 :        6428 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
     900                 :        6428 :             return static_cast<gnc_numeric>(sum.round_to_numeric());
     901                 :         574 :         sum = convert(sum, denom, how);
     902                 :         574 :         if (sum.is_big() || !sum.valid())
     903                 :           0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     904                 :         574 :         return static_cast<gnc_numeric>(sum);
     905                 :             :     }
     906                 :           0 :     catch (const std::overflow_error& err)
     907                 :             :     {
     908                 :           0 :         PWARN("%s", err.what());
     909                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     910                 :           0 :     }
     911                 :           0 :     catch (const std::invalid_argument& err)
     912                 :             :     {
     913                 :           0 :         PWARN("%s", err.what());
     914                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
     915                 :           0 :     }
     916                 :           0 :     catch (const std::underflow_error& err)
     917                 :             :     {
     918                 :           0 :         PWARN("%s", err.what());
     919                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     920                 :           0 :     }
     921                 :           0 :     catch (const std::domain_error& err)
     922                 :             :     {
     923                 :           0 :         PWARN("%s", err.what());
     924                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
     925                 :           0 :     }
     926                 :             : }
     927                 :             : 
     928                 :             : /* *******************************************************************
     929                 :             :  *  gnc_numeric_sub
     930                 :             :  ********************************************************************/
     931                 :             : 
     932                 :             : gnc_numeric
     933                 :       70549 : gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
     934                 :             :                 gint64 denom, gint how)
     935                 :             : {
     936                 :       70549 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     937                 :             :     {
     938                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
     939                 :             :     }
     940                 :             :     try
     941                 :             :     {
     942                 :       70549 :         denom = denom_lcd(a, b, denom, how);
     943                 :       70549 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
     944                 :             :         {
     945                 :       70542 :             GncNumeric an (a), bn (b);
     946                 :       70542 :             auto sum = an - bn;
     947                 :       70542 :             return static_cast<gnc_numeric>(convert(sum, denom, how));
     948                 :             :         }
     949                 :           7 :         GncRational ar(a), br(b);
     950                 :           7 :         auto sum = ar - br;
     951                 :           7 :         if (denom == GNC_DENOM_AUTO &&
     952                 :           7 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
     953                 :           7 :             return static_cast<gnc_numeric>(sum.round_to_numeric());
     954                 :           0 :         sum = convert(sum, denom, how);
     955                 :           0 :         if (sum.is_big() || !sum.valid())
     956                 :           0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     957                 :           0 :         return static_cast<gnc_numeric>(sum);
     958                 :             :     }
     959                 :           0 :     catch (const std::overflow_error& err)
     960                 :             :     {
     961                 :           0 :         PWARN("%s", err.what());
     962                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     963                 :           0 :     }
     964                 :           0 :     catch (const std::invalid_argument& err)
     965                 :             :     {
     966                 :           0 :         PWARN("%s", err.what());
     967                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
     968                 :           0 :     }
     969                 :           0 :     catch (const std::underflow_error& err)
     970                 :             :     {
     971                 :           0 :         PWARN("%s", err.what());
     972                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     973                 :           0 :     }
     974                 :           0 :     catch (const std::domain_error& err)
     975                 :             :     {
     976                 :           0 :         PWARN("%s", err.what());
     977                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
     978                 :           0 :     }
     979                 :             : }
     980                 :             : 
     981                 :             : /* *******************************************************************
     982                 :             :  *  gnc_numeric_mul
     983                 :             :  ********************************************************************/
     984                 :             : 
     985                 :             : gnc_numeric
     986                 :        8293 : gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
     987                 :             :                 gint64 denom, gint how)
     988                 :             : {
     989                 :        8293 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     990                 :             :     {
     991                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
     992                 :             :     }
     993                 :             : 
     994                 :             :     try
     995                 :             :     {
     996                 :        8293 :         denom = denom_lcd(a, b, denom, how);
     997                 :        8293 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
     998                 :             :         {
     999                 :        4282 :             GncNumeric an (a), bn (b);
    1000                 :        4282 :             auto prod = an * bn;
    1001                 :        4254 :             return static_cast<gnc_numeric>(convert(prod, denom, how));
    1002                 :             :         }
    1003                 :        4011 :         GncRational ar(a), br(b);
    1004                 :        4011 :         auto prod = ar * br;
    1005                 :        4011 :         if (denom == GNC_DENOM_AUTO &&
    1006                 :          24 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
    1007                 :          16 :             return static_cast<gnc_numeric>(prod.round_to_numeric());
    1008                 :        3995 :         prod = convert(prod, denom, how);
    1009                 :        3995 :         if (prod.is_big() || !prod.valid())
    1010                 :           0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1011                 :        3995 :         return static_cast<gnc_numeric>(prod);
    1012                 :             :      }
    1013                 :          43 :     catch (const std::overflow_error& err)
    1014                 :             :     {
    1015                 :          43 :         PWARN("%s", err.what());
    1016                 :          43 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1017                 :          43 :     }
    1018                 :           0 :     catch (const std::invalid_argument& err)
    1019                 :             :     {
    1020                 :           0 :         PWARN("%s", err.what());
    1021                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1022                 :           0 :     }
    1023                 :           0 :     catch (const std::underflow_error& err)
    1024                 :             :     {
    1025                 :           0 :         PWARN("%s", err.what());
    1026                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1027                 :           0 :     }
    1028                 :           0 :     catch (const std::domain_error& err)
    1029                 :             :     {
    1030                 :           0 :         PWARN("%s", err.what());
    1031                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1032                 :           0 :     }
    1033                 :             : }
    1034                 :             : 
    1035                 :             : 
    1036                 :             : /* *******************************************************************
    1037                 :             :  *  gnc_numeric_div
    1038                 :             :  ********************************************************************/
    1039                 :             : 
    1040                 :             : gnc_numeric
    1041                 :        1785 : gnc_numeric_div(gnc_numeric a, gnc_numeric b,
    1042                 :             :                 gint64 denom, gint how)
    1043                 :             : {
    1044                 :        1785 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
    1045                 :             :     {
    1046                 :           4 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1047                 :             :     }
    1048                 :             :     try
    1049                 :             :     {
    1050                 :        1781 :         denom = denom_lcd(a, b, denom, how);
    1051                 :        1781 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
    1052                 :             :         {
    1053                 :        1298 :             GncNumeric an (a), bn (b);
    1054                 :        1298 :             auto quot = an / bn;
    1055                 :        1297 :             return static_cast<gnc_numeric>(convert(quot, denom, how));
    1056                 :             :         }
    1057                 :         483 :         GncRational ar(a), br(b);
    1058                 :         483 :         auto quot = ar / br;
    1059                 :         482 :         if (denom == GNC_DENOM_AUTO &&
    1060                 :         482 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
    1061                 :          47 :             return static_cast<gnc_numeric>(quot.round_to_numeric());
    1062                 :         435 :         quot =  static_cast<gnc_numeric>(convert(quot, denom, how));
    1063                 :         435 :         if (quot.is_big() || !quot.valid())
    1064                 :           0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1065                 :         435 :         return static_cast<gnc_numeric>(quot);
    1066                 :             :     }
    1067                 :           2 :     catch (const std::overflow_error& err)
    1068                 :             :     {
    1069                 :           0 :         PWARN("%s", err.what());
    1070                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1071                 :           0 :     }
    1072                 :           0 :     catch (const std::invalid_argument& err)
    1073                 :             :     {
    1074                 :           0 :         PWARN("%s", err.what());
    1075                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1076                 :           0 :     }
    1077                 :           2 :     catch (const std::underflow_error& err) //Divide by zero
    1078                 :             :     {
    1079                 :           2 :         PWARN("%s", err.what());
    1080                 :           2 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1081                 :           2 :     }
    1082                 :           0 :     catch (const std::domain_error& err)
    1083                 :             :     {
    1084                 :           0 :         PWARN("%s", err.what());
    1085                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1086                 :           0 :     }
    1087                 :             : }
    1088                 :             : 
    1089                 :             : /* *******************************************************************
    1090                 :             :  *  gnc_numeric_neg
    1091                 :             :  *  negate the argument
    1092                 :             :  ********************************************************************/
    1093                 :             : 
    1094                 :             : gnc_numeric
    1095                 :        2000 : gnc_numeric_neg(gnc_numeric a)
    1096                 :             : {
    1097                 :        2000 :     if (gnc_numeric_check(a))
    1098                 :             :     {
    1099                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1100                 :             :     }
    1101                 :        2000 :     return gnc_numeric_create(- a.num, a.denom);
    1102                 :             : }
    1103                 :             : 
    1104                 :             : /* *******************************************************************
    1105                 :             :  *  gnc_numeric_abs
    1106                 :             :  *  return the absolute value of the argument
    1107                 :             :  ********************************************************************/
    1108                 :             : 
    1109                 :             : gnc_numeric
    1110                 :       66821 : gnc_numeric_abs(gnc_numeric a)
    1111                 :             : {
    1112                 :       66821 :     if (gnc_numeric_check(a))
    1113                 :             :     {
    1114                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1115                 :             :     }
    1116                 :       66821 :     return gnc_numeric_create(ABS(a.num), a.denom);
    1117                 :             : }
    1118                 :             : 
    1119                 :             : 
    1120                 :             : /* *******************************************************************
    1121                 :             :  *  gnc_numeric_convert
    1122                 :             :  ********************************************************************/
    1123                 :             : 
    1124                 :             : gnc_numeric
    1125                 :      174675 : gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
    1126                 :             : {
    1127                 :      174675 :     if (gnc_numeric_check(in))
    1128                 :           0 :         return in;
    1129                 :             :     try
    1130                 :             :     {
    1131                 :      174675 :         return convert(GncNumeric(in), denom, how);
    1132                 :             :     }
    1133                 :           0 :     catch (const std::invalid_argument& err)
    1134                 :             :     {
    1135                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1136                 :           0 :     }
    1137                 :           0 :     catch (const std::overflow_error& err)
    1138                 :             :     {
    1139                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1140                 :           0 :     }
    1141                 :           0 :     catch (const std::underflow_error& err)
    1142                 :             :     {
    1143                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1144                 :           0 :     }
    1145                 :           0 :     catch (const std::domain_error& err)
    1146                 :             :     {
    1147                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1148                 :           0 :     }
    1149                 :             : }
    1150                 :             : 
    1151                 :             : 
    1152                 :             : /* *******************************************************************
    1153                 :             :  *  reduce a fraction by GCF elimination.  This is NOT done as a
    1154                 :             :  *  part of the arithmetic API unless GNC_HOW_DENOM_REDUCE is specified
    1155                 :             :  *  as the output denominator.
    1156                 :             :  ********************************************************************/
    1157                 :             : 
    1158                 :             : gnc_numeric
    1159                 :        3291 : gnc_numeric_reduce(gnc_numeric in)
    1160                 :             : {
    1161                 :        3291 :     if (gnc_numeric_check(in))
    1162                 :             :     {
    1163                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1164                 :             :     }
    1165                 :             : 
    1166                 :        3291 :     if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
    1167                 :           0 :         return in;
    1168                 :             :     try
    1169                 :             :     {
    1170                 :        3291 :         GncNumeric an (in);
    1171                 :        3291 :         return static_cast<gnc_numeric>(an.reduce());
    1172                 :             :     }
    1173                 :           0 :     catch (const std::overflow_error& err)
    1174                 :             :     {
    1175                 :           0 :         PWARN("%s", err.what());
    1176                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1177                 :           0 :     }
    1178                 :           0 :     catch (const std::invalid_argument& err)
    1179                 :             :     {
    1180                 :           0 :         PWARN("%s", err.what());
    1181                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1182                 :           0 :     }
    1183                 :           0 :     catch (const std::underflow_error& err)
    1184                 :             :     {
    1185                 :           0 :         PWARN("%s", err.what());
    1186                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1187                 :           0 :     }
    1188                 :           0 :     catch (const std::domain_error& err)
    1189                 :             :     {
    1190                 :           0 :         PWARN("%s", err.what());
    1191                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1192                 :           0 :     }
    1193                 :             : }
    1194                 :             : 
    1195                 :             : 
    1196                 :             : /* *******************************************************************
    1197                 :             :  * gnc_numeric_to_decimal
    1198                 :             :  *
    1199                 :             :  * Attempt to convert the denominator to an exact power of ten without
    1200                 :             :  * rounding. TRUE is returned if 'a' has been converted or was already
    1201                 :             :  * decimal. Otherwise, FALSE is returned and 'a' remains unchanged.
    1202                 :             :  * The 'max_decimal_places' parameter may be NULL.
    1203                 :             :  ********************************************************************/
    1204                 :             : 
    1205                 :             : gboolean
    1206                 :      129626 : gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
    1207                 :             : {
    1208                 :      129626 :     int max_places =  max_decimal_places == NULL ? max_leg_digits :
    1209                 :             :         *max_decimal_places;
    1210                 :      129626 :     if (a->num == 0) return TRUE;
    1211                 :             :     try
    1212                 :             :     {
    1213                 :       32061 :         GncNumeric an (*a);
    1214                 :       32061 :         auto bn = an.to_decimal(max_places);
    1215                 :       31975 :         *a = static_cast<gnc_numeric>(bn);
    1216                 :       31975 :         return TRUE;
    1217                 :             :     }
    1218                 :          86 :     catch (const std::exception& err)
    1219                 :             :     {
    1220                 :          86 :         PINFO ("%s", err.what());
    1221                 :          86 :         return FALSE;
    1222                 :          86 :     }
    1223                 :             : }
    1224                 :             : 
    1225                 :             : 
    1226                 :             : gnc_numeric
    1227                 :         708 : gnc_numeric_invert(gnc_numeric num)
    1228                 :             : {
    1229                 :         708 :     if (num.num == 0)
    1230                 :           0 :         return gnc_numeric_zero();
    1231                 :             :     try
    1232                 :             :     {
    1233                 :         708 :         return static_cast<gnc_numeric>(GncNumeric(num).inv());
    1234                 :             :     }
    1235                 :           0 :     catch (const std::overflow_error& err)
    1236                 :             :     {
    1237                 :           0 :         PWARN("%s", err.what());
    1238                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1239                 :           0 :     }
    1240                 :           0 :     catch (const std::invalid_argument& err)
    1241                 :             :     {
    1242                 :           0 :         PWARN("%s", err.what());
    1243                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1244                 :           0 :     }
    1245                 :           0 :     catch (const std::underflow_error& err)
    1246                 :             :     {
    1247                 :           0 :         PWARN("%s", err.what());
    1248                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1249                 :           0 :     }
    1250                 :           0 :     catch (const std::domain_error& err)
    1251                 :             :     {
    1252                 :           0 :         PWARN("%s", err.what());
    1253                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1254                 :           0 :     }
    1255                 :             : }
    1256                 :             : 
    1257                 :             : /* *******************************************************************
    1258                 :             :  *  double_to_gnc_numeric
    1259                 :             :  ********************************************************************/
    1260                 :             : 
    1261                 :             : #ifdef _MSC_VER
    1262                 :             : # define rint /* */
    1263                 :             : #endif
    1264                 :             : gnc_numeric
    1265                 :         350 : double_to_gnc_numeric(double in, gint64 denom, gint how)
    1266                 :             : {
    1267                 :             :     try
    1268                 :             :     {
    1269                 :         350 :         GncNumeric an(in);
    1270                 :         350 :         return convert(an, denom, how);
    1271                 :             :     }
    1272                 :           0 :     catch (const std::overflow_error& err)
    1273                 :             :     {
    1274                 :           0 :         PWARN("%s", err.what());
    1275                 :           0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1276                 :           0 :     }
    1277                 :           0 :     catch (const std::invalid_argument& err)
    1278                 :             :     {
    1279                 :           0 :         PWARN("%s", err.what());
    1280                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1281                 :           0 :     }
    1282                 :           0 :     catch (const std::underflow_error& err)
    1283                 :             :     {
    1284                 :           0 :         PWARN("%s", err.what());
    1285                 :           0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1286                 :           0 :     }
    1287                 :           0 :     catch (const std::domain_error& err)
    1288                 :             :     {
    1289                 :           0 :         PWARN("%s", err.what());
    1290                 :           0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1291                 :           0 :     }
    1292                 :             : }
    1293                 :             : 
    1294                 :             : /* *******************************************************************
    1295                 :             :  *  gnc_numeric_to_double
    1296                 :             :  ********************************************************************/
    1297                 :             : 
    1298                 :             : double
    1299                 :         156 : gnc_numeric_to_double(gnc_numeric in)
    1300                 :             : {
    1301                 :         156 :     if (in.denom > 0)
    1302                 :             :     {
    1303                 :         133 :         return (double)in.num / (double)in.denom;
    1304                 :             :     }
    1305                 :             :     else
    1306                 :             :     {
    1307                 :          23 :         return (double)(in.num * -in.denom);
    1308                 :             :     }
    1309                 :             : }
    1310                 :             : 
    1311                 :             : /* *******************************************************************
    1312                 :             :  *  gnc_numeric_error
    1313                 :             :  ********************************************************************/
    1314                 :             : 
    1315                 :             : gnc_numeric
    1316                 :         323 : gnc_numeric_error(GNCNumericErrorCode error_code)
    1317                 :             : {
    1318                 :         323 :     return gnc_numeric_create(error_code, 0LL);
    1319                 :             : }
    1320                 :             : 
    1321                 :             : 
    1322                 :             : 
    1323                 :             : /* *******************************************************************
    1324                 :             :  *  gnc_numeric text IO
    1325                 :             :  ********************************************************************/
    1326                 :             : 
    1327                 :             : gchar *
    1328                 :        8138 : gnc_numeric_to_string(gnc_numeric n)
    1329                 :             : {
    1330                 :             :     char *result;
    1331                 :        8138 :     int64_t tmpnum = n.num;
    1332                 :        8138 :     int64_t tmpdenom = n.denom;
    1333                 :             : 
    1334                 :        8138 :     result = g_strdup_printf("%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
    1335                 :             : 
    1336                 :        8138 :     return result;
    1337                 :             : }
    1338                 :             : 
    1339                 :             : gchar *
    1340                 :          36 : gnc_num_dbg_to_string(gnc_numeric n)
    1341                 :             : {
    1342                 :             :     static char buff[1000];
    1343                 :             :     static char *p = buff;
    1344                 :             :     static const size_t size = 50;
    1345                 :          36 :     int64_t tmpnum = n.num;
    1346                 :          36 :     int64_t tmpdenom = n.denom;
    1347                 :             : 
    1348                 :          36 :     p += size;
    1349                 :          36 :     if ((size_t)(p - buff) > (sizeof(buff) - size))
    1350                 :           1 :         p = buff;
    1351                 :             : 
    1352                 :          36 :     snprintf(p, size, "%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
    1353                 :             : 
    1354                 :          36 :     return p;
    1355                 :             : }
    1356                 :             : 
    1357                 :             : gnc_numeric
    1358                 :        5144 : gnc_numeric_from_string (const gchar* str)
    1359                 :             : {
    1360                 :        5144 :     if (!str)
    1361                 :           0 :         return gnc_numeric_error (GNC_ERROR_ARG);
    1362                 :             : 
    1363                 :             :     // the default gnc_numeric string format is "num/denom", whereby
    1364                 :             :     // the denom must be >= 1. this speedily parses it. this also
    1365                 :             :     // parses "num" as num/1.
    1366                 :        5144 :     if (auto res = fast_numeral_rational (str))
    1367                 :        5135 :         return *res;
    1368                 :             : 
    1369                 :             :     try
    1370                 :             :     {
    1371                 :          18 :         return GncNumeric (str);
    1372                 :             :     }
    1373                 :           2 :     catch (const std::exception& err)
    1374                 :             :     {
    1375                 :           2 :         PWARN("%s", err.what());
    1376                 :           2 :         return gnc_numeric_error (GNC_ERROR_ARG);
    1377                 :           2 :     }
    1378                 :             : }
    1379                 :             : 
    1380                 :             : /* *******************************************************************
    1381                 :             :  *  GValue handling
    1382                 :             :  ********************************************************************/
    1383                 :             : static gnc_numeric*
    1384                 :         513 : gnc_numeric_boxed_copy_func( gnc_numeric *in_gnc_numeric )
    1385                 :             : {
    1386                 :         513 :     if (!in_gnc_numeric)
    1387                 :           0 :         return nullptr;
    1388                 :             : 
    1389                 :             :     /* newvalue will be passed to g_free so we must allocate with g_malloc. */
    1390                 :         513 :     auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
    1391                 :         513 :     *newvalue = *in_gnc_numeric;
    1392                 :             : 
    1393                 :         513 :     return newvalue;
    1394                 :             : }
    1395                 :             : 
    1396                 :             : static void
    1397                 :         461 : gnc_numeric_boxed_free_func( gnc_numeric *in_gnc_numeric )
    1398                 :             : {
    1399                 :         461 :     g_free( in_gnc_numeric );
    1400                 :         461 : }
    1401                 :             : 
    1402                 :        4845 : G_DEFINE_BOXED_TYPE (gnc_numeric, gnc_numeric, gnc_numeric_boxed_copy_func, gnc_numeric_boxed_free_func)
    1403                 :             : 
    1404                 :             : /* *******************************************************************
    1405                 :             :  *  gnc_numeric misc testing
    1406                 :             :  ********************************************************************/
    1407                 :             : #ifdef _GNC_NUMERIC_TEST
    1408                 :             : 
    1409                 :             : static char *
    1410                 :             : gnc_numeric_print(gnc_numeric in)
    1411                 :             : {
    1412                 :             :     char * retval;
    1413                 :             :     if (gnc_numeric_check(in))
    1414                 :             :     {
    1415                 :             :         retval = g_strdup_printf("<ERROR> [%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
    1416                 :             :                                  in.num,
    1417                 :             :                                  in.denom);
    1418                 :             :     }
    1419                 :             :     else
    1420                 :             :     {
    1421                 :             :         retval = g_strdup_printf("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
    1422                 :             :                                  in.num,
    1423                 :             :                                  in.denom);
    1424                 :             :     }
    1425                 :             :     return retval;
    1426                 :             : }
    1427                 :             : 
    1428                 :             : int
    1429                 :             : main(int argc, char ** argv)
    1430                 :             : {
    1431                 :             :     gnc_numeric a = gnc_numeric_create(1, 3);
    1432                 :             :     gnc_numeric b = gnc_numeric_create(1, 4);
    1433                 :             :     gnc_numeric c;
    1434                 :             : 
    1435                 :             :     gnc_numeric err;
    1436                 :             : 
    1437                 :             : 
    1438                 :             :     printf("multiply (EXACT): %s * %s = %s\n",
    1439                 :             :            gnc_numeric_print(a), gnc_numeric_print(b),
    1440                 :             :            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT)));
    1441                 :             : 
    1442                 :             :     printf("multiply (REDUCE): %s * %s = %s\n",
    1443                 :             :            gnc_numeric_print(a), gnc_numeric_print(b),
    1444                 :             :            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE)));
    1445                 :             : 
    1446                 :             : 
    1447                 :             :     return 0;
    1448                 :             : }
    1449                 :             : #endif
    1450                 :             : 
    1451                 :             : 
    1452                 :             : std::ostream&
    1453                 :         284 : operator<<(std::ostream& s, GncNumeric n)
    1454                 :             : {
    1455                 :             :     using boost::locale::conv::utf_to_utf;
    1456                 :         284 :     std::basic_ostringstream<wchar_t> ss;
    1457                 :         284 :     ss.imbue(s.getloc());
    1458                 :         284 :     ss << n;
    1459                 :         284 :     s << utf_to_utf<char>(ss.str());
    1460                 :         284 :     return s;
    1461                 :         284 : }
    1462                 :             : 
    1463                 :           6 : const char* gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
    1464                 :             : {
    1465                 :           6 :     switch (error_code)
    1466                 :             :     {
    1467                 :           6 :     case GNC_ERROR_OK:
    1468                 :           6 :         return "GNC_ERROR_OK";
    1469                 :           0 :     case GNC_ERROR_ARG:
    1470                 :           0 :         return "GNC_ERROR_ARG";
    1471                 :           0 :     case GNC_ERROR_OVERFLOW:
    1472                 :           0 :         return "GNC_ERROR_OVERFLOW";
    1473                 :           0 :     case GNC_ERROR_DENOM_DIFF:
    1474                 :           0 :         return "GNC_ERROR_DENOM_DIFF";
    1475                 :           0 :     case GNC_ERROR_REMAINDER:
    1476                 :           0 :         return "GNC_ERROR_REMAINDER";
    1477                 :           0 :     default:
    1478                 :           0 :         return "<unknown>";
    1479                 :             :     }
    1480                 :             : }
    1481                 :             : 
    1482                 :             : /* ======================== END OF FILE =================== */
        

Generated by: LCOV version 2.0-1