File indexing completed on 2024-05-12 05:06:41

0001 /*
0002     SPDX-FileCopyrightText: 2000-2004 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2001-2021 Thomas Baumgart <tbaumgart@kde.org>
0004     SPDX-FileCopyrightText: 2001-2002 Felix Rodriguez <frodriguez@users.sourceforge.net>
0005     SPDX-FileCopyrightText: 2002-2004 Kevin Tambascio <ktambascio@users.sourceforge.net>
0006     SPDX-FileCopyrightText: 2005 Ace Jones <acejones@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2007-2010 Alvaro Soliverez <asoliverez@gmail.com>
0008     SPDX-FileCopyrightText: 2011 Carlos Eduardo da Silva <kaduardo@gmail.com>
0009     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 // make sure, that this is defined before we even include any other header file
0014 #ifndef __STDC_LIMIT_MACROS
0015 #define __STDC_LIMIT_MACROS         // force definition of min and max values
0016 #endif
0017 
0018 #include "mymoneymoney.h"
0019 
0020 #include <alkimia/alkversion.h>
0021 #include <gmpxx.h>
0022 #include <stdint.h>
0023 
0024 // ----------------------------------------------------------------------------
0025 // QT Includes
0026 
0027 #include <QDebug>
0028 #include <QString>
0029 
0030 // ----------------------------------------------------------------------------
0031 // Project Includes
0032 
0033 #include "mymoneyexception.h"
0034 #include "mymoneyenums.h"
0035 
0036 const MyMoneyMoney MyMoneyMoney::ONE = MyMoneyMoney(1, 1);
0037 const MyMoneyMoney MyMoneyMoney::MINUS_ONE = MyMoneyMoney(-1, 1);
0038 namespace eMyMoney
0039 {
0040 namespace Money {
0041 
0042 enum fileVersionE : int {
0043     FILE_4_BYTE_VALUE = 0,
0044     FILE_8_BYTE_VALUE,
0045 };
0046 
0047 QString _thousandSeparator = QLatin1String(",");
0048 QString _decimalSeparator = QLatin1String(".");
0049 eMyMoney::Money::signPosition _negativeMonetarySignPosition = PreceedQuantityAndSymbol;
0050 eMyMoney::Money::signPosition _positiveMonetarySignPosition = PreceedQuantityAndSymbol;
0051 bool _negativePrefixCurrencySymbol = false;
0052 bool _positivePrefixCurrencySymbol = false;
0053 bool _negativeSpaceSeparatesSymbol = true;
0054 bool _positiveSpaceSeparatesSymbol = true;
0055 eMyMoney::Money::fileVersionE _fileVersion = fileVersionE::FILE_4_BYTE_VALUE;
0056 }
0057 }
0058 
0059 MyMoneyMoney MyMoneyMoney::maxValue = MyMoneyMoney(INT64_MAX, 100);
0060 MyMoneyMoney MyMoneyMoney::minValue = MyMoneyMoney(INT64_MIN, 100);
0061 MyMoneyMoney MyMoneyMoney::autoCalc = MyMoneyMoney(INT64_MIN + 1, 100);
0062 
0063 void MyMoneyMoney::setNegativeSpaceSeparatesSymbol(const bool flag)
0064 {
0065     eMyMoney::Money::_negativeSpaceSeparatesSymbol= flag;
0066 }
0067 
0068 void MyMoneyMoney::setPositiveSpaceSeparatesSymbol(const bool flag)
0069 {
0070     eMyMoney::Money::_positiveSpaceSeparatesSymbol= flag;
0071 }
0072 
0073 void MyMoneyMoney::setNegativePrefixCurrencySymbol(const bool flag)
0074 {
0075     eMyMoney::Money::_negativePrefixCurrencySymbol = flag;
0076 }
0077 
0078 void MyMoneyMoney::setPositivePrefixCurrencySymbol(const bool flag)
0079 {
0080     eMyMoney::Money::_positivePrefixCurrencySymbol = flag;
0081 }
0082 
0083 void MyMoneyMoney::setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos)
0084 {
0085     eMyMoney::Money::_negativeMonetarySignPosition = pos;
0086 }
0087 
0088 eMyMoney::Money::signPosition MyMoneyMoney::negativeMonetarySignPosition()
0089 {
0090     return eMyMoney::Money::_negativeMonetarySignPosition;
0091 }
0092 
0093 void MyMoneyMoney::setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos)
0094 {
0095     eMyMoney::Money::_positiveMonetarySignPosition = pos;
0096 }
0097 
0098 eMyMoney::Money::signPosition MyMoneyMoney::positiveMonetarySignPosition()
0099 {
0100     return eMyMoney::Money::_positiveMonetarySignPosition;
0101 }
0102 
0103 void MyMoneyMoney::setThousandSeparator(const QChar &separator)
0104 {
0105     if (separator != QLatin1Char(' '))
0106         eMyMoney::Money::_thousandSeparator = separator;
0107     else
0108         eMyMoney::Money::_thousandSeparator = QLatin1Char(0);
0109 }
0110 
0111 void MyMoneyMoney::setThousandSeparator(const QString& separator)
0112 {
0113     if (separator != QLatin1Char(' '))
0114         eMyMoney::Money::_thousandSeparator = separator;
0115     else
0116         eMyMoney::Money::_thousandSeparator = QLatin1Char(0);
0117 }
0118 
0119 const QChar MyMoneyMoney::thousandSeparator()
0120 {
0121     return eMyMoney::Money::_thousandSeparator.isEmpty() ? QLatin1Char(0) : eMyMoney::Money::_thousandSeparator.at(0);
0122 }
0123 
0124 void MyMoneyMoney::setDecimalSeparator(const QChar &separator)
0125 {
0126     if (separator != QLatin1Char(' '))
0127         eMyMoney::Money::_decimalSeparator = separator;
0128     else
0129         eMyMoney::Money::_decimalSeparator = QString();
0130 }
0131 
0132 void MyMoneyMoney::setDecimalSeparator(const QString& separator)
0133 {
0134     if (separator != QLatin1Char(' '))
0135         eMyMoney::Money::_decimalSeparator = separator;
0136     else
0137         eMyMoney::Money::_decimalSeparator = QLatin1Char(0);
0138 }
0139 
0140 const QChar MyMoneyMoney::decimalSeparator()
0141 {
0142     return eMyMoney::Money::_decimalSeparator.isEmpty() ? QLatin1Char(0) : eMyMoney::Money::_decimalSeparator.at(0);
0143 }
0144 
0145 MyMoneyMoney::MyMoneyMoney(const QString& pszAmount)
0146     : AlkValue(pszAmount, decimalSeparator())
0147 {
0148 }
0149 
0150 ////////////////////////////////////////////////////////////////////////////////
0151 //      Name: MyMoneyMoney
0152 //   Purpose: Constructor - constructs object from an amount in a signed64 value
0153 //   Returns: None
0154 //    Throws: Nothing.
0155 // Arguments: Amount - signed 64 object containing amount
0156 //            denom  - denominator of the object
0157 //
0158 ////////////////////////////////////////////////////////////////////////////////
0159 MyMoneyMoney::MyMoneyMoney(qint64 Amount, const unsigned int denom)
0160 {
0161     if (denom == 0)
0162         throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
0163 
0164     *this = AlkValue(QString::fromLatin1("%1/%2").arg(Amount).arg(denom), decimalSeparator());
0165 }
0166 
0167 ////////////////////////////////////////////////////////////////////////////////
0168 //      Name: MyMoneyMoney
0169 //   Purpose: Constructor - constructs object from an amount in a integer value
0170 //   Returns: None
0171 //    Throws: Nothing.
0172 // Arguments: iAmount - integer object containing amount
0173 //            denom   - denominator of the object
0174 //
0175 ////////////////////////////////////////////////////////////////////////////////
0176 MyMoneyMoney::MyMoneyMoney(const int iAmount, const unsigned int denom)
0177 {
0178     if (denom == 0)
0179         throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
0180     *this = AlkValue(iAmount, denom);
0181 }
0182 
0183 ////////////////////////////////////////////////////////////////////////////////
0184 //      Name: MyMoneyMoney
0185 //   Purpose: Constructor - constructs object from an amount in a long integer value
0186 //   Returns: None
0187 //    Throws: Nothing.
0188 // Arguments: iAmount - integer object containing amount
0189 //            denom   - denominator of the object
0190 //
0191 ////////////////////////////////////////////////////////////////////////////////
0192 MyMoneyMoney::MyMoneyMoney(const long int iAmount, const unsigned int denom)
0193 {
0194     if (denom == 0)
0195         throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
0196     *this = AlkValue(QString::fromLatin1("%1/%2").arg(iAmount).arg(denom), decimalSeparator());
0197 }
0198 
0199 
0200 MyMoneyMoney::~MyMoneyMoney()
0201 {
0202 }
0203 
0204 MyMoneyMoney MyMoneyMoney::abs() const
0205 {
0206     return static_cast<const MyMoneyMoney>(AlkValue::abs());
0207 }
0208 
0209 QString MyMoneyMoney::formatMoney(int denom, bool showThousandSeparator) const
0210 {
0211     return formatMoney(QString(), denomToPrec(denom), showThousandSeparator);
0212 }
0213 
0214 QString MyMoneyMoney::formatMoney(const QString& currency, const int prec, bool showThousandSeparator) const
0215 {
0216     QString res;
0217     QString tmpCurrency = currency;
0218     int tmpPrec = prec;
0219     mpz_class denom = 1;
0220     mpz_class value;
0221     const int maxPrecision = 20;
0222 
0223     // if prec == -1 we want the maximum possible but w/o trailing zeroes
0224     if (tmpPrec == -1) {
0225         tmpPrec = maxPrecision;
0226     }
0227     mpz_ui_pow_ui(denom.get_mpz_t(), 10, tmpPrec);
0228 
0229     value = static_cast<const MyMoneyMoney>(convertDenominator(denom)).valueRef().get_num();
0230 
0231     bool bNegative = false;
0232     mpz_class left = value / static_cast<MyMoneyMoney>(convertDenominator(denom)).valueRef().get_den();
0233     mpz_class right = mpz_class((valueRef() - mpq_class(left)) * denom);
0234 
0235     if (right < 0) {
0236         right = -right;
0237         bNegative = true;
0238     }
0239     if (left < 0) {
0240         left = -left;
0241         bNegative = true;
0242     }
0243 
0244     // convert the integer (left) part to a string
0245     res.append(left.get_str().c_str());
0246 
0247     // if requested, insert thousand separators every three digits
0248     if (showThousandSeparator) {
0249         int pos = res.length();
0250         while ((0 < (pos -= 3)) && thousandSeparator() != 0)
0251             res.insert(pos, thousandSeparator());
0252     }
0253 
0254     // take care of the fractional part
0255     if (prec > 0 || (prec == -1 && right != 0)) {
0256         if (decimalSeparator() != 0)
0257             res += decimalSeparator();
0258 
0259         auto rs  = QString::fromLatin1("%1").arg(right.get_str().c_str());
0260         if (prec != -1)
0261             rs = rs.rightJustified(prec, QLatin1Char('0'), true);
0262         else {
0263             rs = rs.rightJustified(maxPrecision, QLatin1Char('0'), true);
0264             // no trailing zeroes or decimal separators
0265             while (rs.endsWith(QLatin1Char('0')))
0266                 rs.truncate(rs.length() - 1);
0267             while (rs.endsWith(decimalSeparator()))
0268                 rs.truncate(rs.length() - 1);
0269         }
0270         res += rs;
0271     }
0272 
0273     eMyMoney::Money::signPosition signpos = bNegative ? eMyMoney::Money::_negativeMonetarySignPosition : eMyMoney::Money::_positiveMonetarySignPosition;
0274     auto sign = bNegative ? QString::fromLatin1("-") : QString();
0275 
0276     switch (signpos) {
0277     case eMyMoney::Money::ParensAround:
0278         // do nothing here
0279         break;
0280     case eMyMoney::Money::PreceedQuantityAndSymbol:
0281         res.prepend(sign);
0282         break;
0283     case eMyMoney::Money::SucceedQuantityAndSymbol:
0284         res.append(sign);
0285         break;
0286     case eMyMoney::Money::PreceedSymbol:
0287         tmpCurrency.prepend(sign);
0288         break;
0289     case eMyMoney::Money::SucceedSymbol:
0290         tmpCurrency.append(sign);
0291         break;
0292     }
0293     if (!tmpCurrency.isEmpty()) {
0294         if (bNegative ? eMyMoney::Money::_negativePrefixCurrencySymbol : eMyMoney::Money::_positivePrefixCurrencySymbol) {
0295             if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
0296                 res.prepend(QLatin1Char(' '));
0297             }
0298             res.prepend(tmpCurrency);
0299         } else {
0300             if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
0301                 res.append(QLatin1Char(' '));
0302             }
0303             res.append(tmpCurrency);
0304         }
0305     }
0306 
0307     if (signpos == eMyMoney::Money::ParensAround) {
0308         res.prepend(QLatin1Char('('));
0309         res.append(QLatin1Char(')'));
0310     }
0311 
0312     return res;
0313 }
0314 
0315 ////////////////////////////////////////////////////////////////////////////////
0316 //      Name: operator+
0317 //   Purpose: Addition operator - adds the input amount to the object
0318 //   Returns: The current object
0319 //    Throws: Nothing.
0320 // Arguments: b - MyMoneyMoney object to be added
0321 //
0322 ////////////////////////////////////////////////////////////////////////////////
0323 const MyMoneyMoney MyMoneyMoney::operator+(const MyMoneyMoney& _b) const
0324 {
0325     return static_cast<const MyMoneyMoney>(AlkValue::operator+(_b));
0326 }
0327 
0328 
0329 ////////////////////////////////////////////////////////////////////////////////
0330 //      Name: operator-
0331 //   Purpose: Addition operator - subtracts the input amount from the object
0332 //   Returns: The current object
0333 //    Throws: Nothing.
0334 // Arguments: AmountInPence - MyMoneyMoney object to be subtracted
0335 //
0336 ////////////////////////////////////////////////////////////////////////////////
0337 const MyMoneyMoney MyMoneyMoney::operator-(const MyMoneyMoney& _b) const
0338 {
0339     return static_cast<const MyMoneyMoney>(AlkValue::operator-(_b));
0340 }
0341 
0342 ////////////////////////////////////////////////////////////////////////////////
0343 //      Name: operator*
0344 //   Purpose: Multiplication operator - multiplies the input amount to the object
0345 //   Returns: The current object
0346 //    Throws: Nothing.
0347 // Arguments: b - MyMoneyMoney object to be multiplied
0348 //
0349 ////////////////////////////////////////////////////////////////////////////////
0350 const MyMoneyMoney MyMoneyMoney::operator*(const MyMoneyMoney& _b) const
0351 {
0352     return static_cast<const MyMoneyMoney>(AlkValue::operator*(_b));
0353 }
0354 
0355 ////////////////////////////////////////////////////////////////////////////////
0356 //      Name: operator/
0357 //   Purpose: Division operator - divides the object by the input amount
0358 //   Returns: The current object
0359 //    Throws: Nothing.
0360 // Arguments: b - MyMoneyMoney object to be used as dividend
0361 //
0362 ////////////////////////////////////////////////////////////////////////////////
0363 const MyMoneyMoney MyMoneyMoney::operator/(const MyMoneyMoney& _b) const
0364 {
0365     return static_cast<const MyMoneyMoney>(AlkValue::operator/(_b));
0366 }
0367 
0368 bool MyMoneyMoney::isNegative() const
0369 {
0370     return (valueRef() < 0) ? true : false;
0371 }
0372 
0373 bool MyMoneyMoney::isPositive() const
0374 {
0375     return (valueRef() > 0) ? true : false;
0376 }
0377 
0378 bool MyMoneyMoney::isZero() const
0379 {
0380     return valueRef() == 0;
0381 }
0382 
0383 bool MyMoneyMoney::isAutoCalc() const
0384 {
0385     return (*this == autoCalc);
0386 }
0387 
0388 MyMoneyMoney MyMoneyMoney::convert(const signed64 _denom, const AlkValue::RoundingMethod how) const
0389 {
0390     return static_cast<const MyMoneyMoney>(AlkValue::convertDenominator(_denom, how));
0391 }
0392 
0393 MyMoneyMoney MyMoneyMoney::reduce() const
0394 {
0395     MyMoneyMoney out(*this);
0396     out.canonicalize();
0397     return out;
0398 }
0399 
0400 signed64 MyMoneyMoney::precToDenom(int prec)
0401 {
0402     signed64 denom = 1;
0403 
0404     while (prec--)
0405         denom *= 10;
0406 
0407     return denom;
0408 }
0409 
0410 double MyMoneyMoney::toDouble() const
0411 {
0412     return valueRef().get_d();
0413 }
0414 
0415 int MyMoneyMoney::denomToPrec(signed64 fract)
0416 {
0417     int rc = 0;
0418     while (fract > 1) {
0419         rc++;
0420         fract /= 10;
0421     }
0422     return rc;
0423 }
0424 
0425 MyMoneyMoney MyMoneyMoney::convertDenominator(mpz_class denom, const AlkValue::RoundingMethod how) const
0426 {
0427 #if ALK_VERSION >= ALK_VERSION_CHECK(8, 1, 71)
0428     return static_cast<const MyMoneyMoney>(AlkValue::convertDenominator(denom, how));
0429 #else
0430     MyMoneyMoney in(*this);
0431     mpz_class in_num(mpq_numref(in.valueRef().get_mpq_t()));
0432 
0433     MyMoneyMoney out; // initialize to zero
0434 
0435     int sign = sgn(in_num);
0436     if (sign != 0) {
0437         // sign is either -1 for negative numbers or +1 in all other cases
0438 
0439         MyMoneyMoney temp;
0440         // only process in case the denominators are different
0441         if (mpz_cmpabs(denom.get_mpz_t(), mpq_denref(valueRef().get_mpq_t())) != 0) {
0442             mpz_class in_denom(mpq_denref(in.valueRef().get_mpq_t()));
0443             mpz_class out_num, out_denom;
0444 
0445             if (sgn(in_denom) == -1) { // my denom is negative
0446                 in_num = in_num * (-in_denom);
0447                 in_num = 1;
0448             }
0449 
0450             mpz_class remainder;
0451             int denom_neg = 0;
0452 
0453             // if the denominator is less than zero, we are to interpret it as
0454             // the reciprocal of its magnitude.
0455             if (sgn(denom) < 0) {
0456                 mpz_class temp_a;
0457                 mpz_class temp_bc;
0458                 denom = -denom;
0459                 denom_neg = 1;
0460                 temp_a = ::abs(in_num);
0461                 temp_bc = in_denom * denom;
0462                 remainder = temp_a % temp_bc;
0463                 out_num = temp_a / temp_bc;
0464                 out_denom = denom;
0465             } else {
0466                 temp = AlkValue(denom, in_denom);
0467                 // the canonicalization required here is part of the ctor
0468                 // temp.d->m_val.canonicalize();
0469 
0470                 out_num = ::abs(in_num * temp.valueRef().get_num());
0471                 remainder = out_num % temp.valueRef().get_den();
0472                 out_num = out_num / temp.valueRef().get_den();
0473                 out_denom = denom;
0474             }
0475 
0476             if (remainder != 0) {
0477                 switch (how) {
0478                 case RoundFloor:
0479                     if (sign < 0) {
0480                         out_num = out_num + 1;
0481                     }
0482                     break;
0483 
0484                 case RoundCeil:
0485                     if (sign > 0) {
0486                         out_num = out_num + 1;
0487                     }
0488                     break;
0489 
0490                 case RoundTruncate:
0491                     break;
0492 
0493                 case RoundPromote:
0494                     out_num = out_num + 1;
0495                     break;
0496 
0497                 case RoundHalfDown:
0498                     if (denom_neg) {
0499                         if ((2 * remainder) > (in_denom * denom)) {
0500                             out_num = out_num + 1;
0501                         }
0502                     } else if ((2 * remainder) > (temp.valueRef().get_den())) {
0503                         out_num = out_num + 1;
0504                     }
0505                     break;
0506 
0507                 case RoundHalfUp:
0508                     if (denom_neg) {
0509                         if ((2 * remainder) >= (in_denom * denom)) {
0510                             out_num = out_num + 1;
0511                         }
0512                     } else if ((2 * remainder) >= temp.valueRef().get_den()) {
0513                         out_num = out_num + 1;
0514                     }
0515                     break;
0516 
0517                 case RoundRound:
0518                     if (denom_neg) {
0519                         if ((remainder * 2) > (in_denom * denom)) {
0520                             out_num = out_num + 1;
0521                         } else if ((2 * remainder) == (in_denom * denom)) {
0522                             if ((out_num % 2) != 0) {
0523                                 out_num = out_num + 1;
0524                             }
0525                         }
0526                     } else {
0527                         if ((remainder * 2) > temp.valueRef().get_den()) {
0528                             out_num = out_num + 1;
0529                         } else if ((2 * remainder) == temp.valueRef().get_den()) {
0530                             if ((out_num % 2) != 0) {
0531                                 out_num = out_num + 1;
0532                             }
0533                         }
0534                     }
0535                     break;
0536 
0537                 case RoundNever:
0538                     qWarning() << "AlkValue: have remainder" << toString() << "->convertDenominator()";
0539                     break;
0540                 }
0541             }
0542 
0543             // construct the new output value
0544             out = AlkValue(out_num * sign, out_denom);
0545         } else {
0546             out = *this;
0547         }
0548     }
0549     return out;
0550 #endif
0551 }