File indexing completed on 2024-05-12 16:42:38

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