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 }