1 /*
2  * Copyright 2000-2004  Michael Edwardes <mte@users.sourceforge.net>
3  * Copyright 2001-2017  Thomas Baumgart <tbaumgart@kde.org>
4  * Copyright 2001-2002  Felix Rodriguez <frodriguez@users.sourceforge.net>
5  * Copyright 2002-2004  Kevin Tambascio <ktambascio@users.sourceforge.net>
6  * Copyright 2005       Ace Jones <acejones@users.sourceforge.net>
7  * Copyright 2007-2010  Alvaro Soliverez <asoliverez@gmail.com>
8  * Copyright 2011       Carlos Eduardo da Silva <kaduardo@gmail.com>
9  * Copyright 2017-2018  Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 // make sure, that this is defined before we even include any other header file
26 #ifndef __STDC_LIMIT_MACROS
27 #define __STDC_LIMIT_MACROS         // force definition of min and max values
28 #endif
29 
30 #include "mymoneymoney.h"
31 
32 #include <stdint.h>
33 #include <gmpxx.h>
34 
35 // ----------------------------------------------------------------------------
36 // QT Includes
37 
38 #include <QString>
39 
40 // ----------------------------------------------------------------------------
41 // Project Includes
42 
43 #include "mymoneyexception.h"
44 #include "mymoneyenums.h"
45 
46 const MyMoneyMoney MyMoneyMoney::ONE = MyMoneyMoney(1, 1);
47 const MyMoneyMoney MyMoneyMoney::MINUS_ONE = MyMoneyMoney(-1, 1);
48 namespace eMyMoney
49 {
50   namespace Money {
51 
52     enum fileVersionE : int {
53       FILE_4_BYTE_VALUE = 0,
54       FILE_8_BYTE_VALUE
55     };
56 
57     QChar _thousandSeparator = QLatin1Char(',');
58     QChar _decimalSeparator = QLatin1Char('.');
59     eMyMoney::Money::signPosition _negativeMonetarySignPosition = PreceedQuantityAndSymbol;
60     eMyMoney::Money::signPosition _positiveMonetarySignPosition = PreceedQuantityAndSymbol;
61     bool _negativePrefixCurrencySymbol = false;
62     bool _positivePrefixCurrencySymbol = false;
63     bool _negativeSpaceSeparatesSymbol = true;
64     bool _positiveSpaceSeparatesSymbol = true;
65     eMyMoney::Money::fileVersionE _fileVersion = fileVersionE::FILE_4_BYTE_VALUE;
66   }
67 }
68 
69 MyMoneyMoney MyMoneyMoney::maxValue = MyMoneyMoney(INT64_MAX, 100);
70 MyMoneyMoney MyMoneyMoney::minValue = MyMoneyMoney(INT64_MIN, 100);
71 MyMoneyMoney MyMoneyMoney::autoCalc = MyMoneyMoney(INT64_MIN + 1, 100);
72 
setNegativeSpaceSeparatesSymbol(const bool flag)73 void MyMoneyMoney::setNegativeSpaceSeparatesSymbol(const bool flag)
74 {
75   eMyMoney::Money::_negativeSpaceSeparatesSymbol= flag;
76 }
77 
setPositiveSpaceSeparatesSymbol(const bool flag)78 void MyMoneyMoney::setPositiveSpaceSeparatesSymbol(const bool flag)
79 {
80   eMyMoney::Money::_positiveSpaceSeparatesSymbol= flag;
81 }
82 
setNegativePrefixCurrencySymbol(const bool flag)83 void MyMoneyMoney::setNegativePrefixCurrencySymbol(const bool flag)
84 {
85   eMyMoney::Money::_negativePrefixCurrencySymbol = flag;
86 }
87 
setPositivePrefixCurrencySymbol(const bool flag)88 void MyMoneyMoney::setPositivePrefixCurrencySymbol(const bool flag)
89 {
90   eMyMoney::Money::_positivePrefixCurrencySymbol = flag;
91 }
92 
setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos)93 void MyMoneyMoney::setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos)
94 {
95   eMyMoney::Money::_negativeMonetarySignPosition = pos;
96 }
97 
negativeMonetarySignPosition()98 eMyMoney::Money::signPosition MyMoneyMoney::negativeMonetarySignPosition()
99 {
100   return eMyMoney::Money::_negativeMonetarySignPosition;
101 }
102 
setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos)103 void MyMoneyMoney::setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos)
104 {
105   eMyMoney::Money::_positiveMonetarySignPosition = pos;
106 }
107 
positiveMonetarySignPosition()108 eMyMoney::Money::signPosition MyMoneyMoney::positiveMonetarySignPosition()
109 {
110   return eMyMoney::Money::_positiveMonetarySignPosition;
111 }
112 
setThousandSeparator(const QChar & separator)113 void MyMoneyMoney::setThousandSeparator(const QChar &separator)
114 {
115   if (separator != QLatin1Char(' '))
116     eMyMoney::Money::_thousandSeparator = separator;
117   else
118     eMyMoney::Money::_thousandSeparator = 0;
119 }
120 
thousandSeparator()121 const QChar MyMoneyMoney::thousandSeparator()
122 {
123   return eMyMoney::Money::_thousandSeparator;
124 }
125 
setDecimalSeparator(const QChar & separator)126 void MyMoneyMoney::setDecimalSeparator(const QChar &separator)
127 {
128   if (separator != QLatin1Char(' '))
129     eMyMoney::Money::_decimalSeparator = separator;
130   else
131     eMyMoney::Money::_decimalSeparator = 0;
132 }
133 
decimalSeparator()134 const QChar MyMoneyMoney::decimalSeparator()
135 {
136   return eMyMoney::Money::_decimalSeparator;
137 }
138 
MyMoneyMoney(const QString & pszAmount)139 MyMoneyMoney::MyMoneyMoney(const QString& pszAmount)
140     : AlkValue(pszAmount, eMyMoney::Money::_decimalSeparator)
141 {
142 }
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 //      Name: MyMoneyMoney
146 //   Purpose: Constructor - constructs object from an amount in a signed64 value
147 //   Returns: None
148 //    Throws: Nothing.
149 // Arguments: Amount - signed 64 object containing amount
150 //            denom  - denominator of the object
151 //
152 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(qint64 Amount,const unsigned int denom)153 MyMoneyMoney::MyMoneyMoney(qint64 Amount, const unsigned int denom)
154 {
155   if (denom == 0)
156     throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
157 
158   *this = AlkValue(QString::fromLatin1("%1/%2").arg(Amount).arg(denom), eMyMoney::Money::_decimalSeparator);
159 }
160 
161 ////////////////////////////////////////////////////////////////////////////////
162 //      Name: MyMoneyMoney
163 //   Purpose: Constructor - constructs object from an amount in a integer value
164 //   Returns: None
165 //    Throws: Nothing.
166 // Arguments: iAmount - integer object containing amount
167 //            denom   - denominator of the object
168 //
169 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(const int iAmount,const unsigned int denom)170 MyMoneyMoney::MyMoneyMoney(const int iAmount, const unsigned int denom)
171 {
172   if (denom == 0)
173     throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
174   *this = AlkValue(iAmount, denom);
175 }
176 
177 ////////////////////////////////////////////////////////////////////////////////
178 //      Name: MyMoneyMoney
179 //   Purpose: Constructor - constructs object from an amount in a long integer value
180 //   Returns: None
181 //    Throws: Nothing.
182 // Arguments: iAmount - integer object containing amount
183 //            denom   - denominator of the object
184 //
185 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(const long int iAmount,const unsigned int denom)186 MyMoneyMoney::MyMoneyMoney(const long int iAmount, const unsigned int denom)
187 {
188   if (denom == 0)
189     throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
190   *this = AlkValue(QString::fromLatin1("%1/%2").arg(iAmount).arg(denom), eMyMoney::Money::_decimalSeparator);
191 }
192 
193 
~MyMoneyMoney()194 MyMoneyMoney::~MyMoneyMoney()
195 {
196 }
197 
abs() const198 MyMoneyMoney MyMoneyMoney::abs() const
199 {
200   return static_cast<const MyMoneyMoney>(AlkValue::abs());
201 }
202 
formatMoney(int denom,bool showThousandSeparator) const203 QString MyMoneyMoney::formatMoney(int denom, bool showThousandSeparator) const
204 {
205   return formatMoney(QString(), denomToPrec(denom), showThousandSeparator);
206 }
207 
formatMoney(const QString & currency,const int prec,bool showThousandSeparator) const208 QString MyMoneyMoney::formatMoney(const QString& currency, const int prec, bool showThousandSeparator) const
209 {
210   QString res;
211   QString tmpCurrency = currency;
212   int tmpPrec = prec;
213   mpz_class denom = 1;
214   mpz_class value;
215 
216   // if prec == -1 we want the maximum possible but w/o trailing zeroes
217   if (tmpPrec > -1) {
218     while (tmpPrec--) {
219       denom *= 10;
220     }
221   } else {
222     // fix it to a max of 9 digits on the right side for now
223     denom = 1000000000;
224   }
225 
226   // as long as AlkValue::convertDenominator() does not take an
227   // mpz_class as the denominator, we need to use a signed int
228   // and limit the precision to 9 digits (the max we can
229   // present with 31 bits
230 #if 1
231   // MPIR and GMP use different types for the return value of mpz_get_si()
232   // which causes warnings on some compilers.
233 #ifdef mpir_version     // MPIR is used
234   mpir_si denominator;
235 #else                   // GMP is used
236   long int denominator;
237 #endif
238 
239   if (mpz_fits_sint_p(denom.get_mpz_t())) {
240     denominator = mpz_get_si(denom.get_mpz_t());
241   } else {
242     denominator = 1000000000;
243   }
244   value = static_cast<const MyMoneyMoney>(convertDenominator(denominator)).valueRef().get_num();
245 #else
246   value = static_cast<const MyMoneyMoney>(convertDenominator(denom)).valueRef().get_num();
247 #endif
248 
249   // Once we really support multiple currencies then this method will
250   // be much better than using KLocale::global()->formatMoney.
251   bool bNegative = false;
252   mpz_class left = value / static_cast<MyMoneyMoney>(convertDenominator(denominator)).valueRef().get_den();
253   mpz_class right = mpz_class((valueRef() - mpq_class(left)) * denom);
254 
255   if (right < 0) {
256     right = -right;
257     bNegative = true;
258   }
259   if (left < 0) {
260     left = -left;
261     bNegative = true;
262   }
263 
264   // convert the integer (left) part to a string
265   res.append(left.get_str().c_str());
266 
267   // if requested, insert thousand separators every three digits
268   if (showThousandSeparator) {
269     int pos = res.length();
270     while ((0 < (pos -= 3)) && thousandSeparator() != 0)
271       res.insert(pos, thousandSeparator());
272   }
273 
274   // take care of the fractional part
275   if (prec > 0 || (prec == -1 && right != 0)) {
276     if (decimalSeparator() != 0)
277       res += decimalSeparator();
278 
279     auto rs  = QString::fromLatin1("%1").arg(right.get_str().c_str());
280     if (prec != -1)
281       rs = rs.rightJustified(prec, QLatin1Char('0'), true);
282     else {
283       rs = rs.rightJustified(9, QLatin1Char('0'), true);
284       // no trailing zeroes or decimal separators
285       while (rs.endsWith(QLatin1Char('0')))
286         rs.truncate(rs.length() - 1);
287       while (rs.endsWith(decimalSeparator()))
288         rs.truncate(rs.length() - 1);
289     }
290     res += rs;
291   }
292 
293   eMyMoney::Money::signPosition signpos = bNegative ? eMyMoney::Money::_negativeMonetarySignPosition : eMyMoney::Money::_positiveMonetarySignPosition;
294   auto sign = bNegative ? QString::fromLatin1("-") : QString();
295 
296   switch (signpos) {
297     case eMyMoney::Money::ParensAround:
298       // do nothing here
299       break;
300     case eMyMoney::Money::PreceedQuantityAndSymbol:
301       res.prepend(sign);
302       break;
303     case eMyMoney::Money::SucceedQuantityAndSymbol:
304       res.append(sign);
305       break;
306     case eMyMoney::Money::PreceedSymbol:
307       tmpCurrency.prepend(sign);
308       break;
309     case eMyMoney::Money::SucceedSymbol:
310       tmpCurrency.append(sign);
311       break;
312   }
313   if (!tmpCurrency.isEmpty()) {
314     if (bNegative ? eMyMoney::Money::_negativePrefixCurrencySymbol : eMyMoney::Money::_positivePrefixCurrencySymbol) {
315       if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
316         res.prepend(QLatin1Char(' '));
317       }
318       res.prepend(tmpCurrency);
319     } else {
320       if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
321         res.append(QLatin1Char(' '));
322       }
323       res.append(tmpCurrency);
324     }
325   }
326 
327   if (signpos == eMyMoney::Money::ParensAround) {
328     res.prepend(QLatin1Char('('));
329     res.append(QLatin1Char(')'));
330   }
331 
332   return res;
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 //      Name: operator+
337 //   Purpose: Addition operator - adds the input amount to the object
338 //   Returns: The current object
339 //    Throws: Nothing.
340 // Arguments: b - MyMoneyMoney object to be added
341 //
342 ////////////////////////////////////////////////////////////////////////////////
operator +(const MyMoneyMoney & _b) const343 const MyMoneyMoney MyMoneyMoney::operator+(const MyMoneyMoney& _b) const
344 {
345   return static_cast<const MyMoneyMoney>(AlkValue::operator+(_b));
346 }
347 
348 
349 ////////////////////////////////////////////////////////////////////////////////
350 //      Name: operator-
351 //   Purpose: Addition operator - subtracts the input amount from the object
352 //   Returns: The current object
353 //    Throws: Nothing.
354 // Arguments: AmountInPence - MyMoneyMoney object to be subtracted
355 //
356 ////////////////////////////////////////////////////////////////////////////////
operator -(const MyMoneyMoney & _b) const357 const MyMoneyMoney MyMoneyMoney::operator-(const MyMoneyMoney& _b) const
358 {
359   return static_cast<const MyMoneyMoney>(AlkValue::operator-(_b));
360 }
361 
362 ////////////////////////////////////////////////////////////////////////////////
363 //      Name: operator*
364 //   Purpose: Multiplication operator - multiplies the input amount to the object
365 //   Returns: The current object
366 //    Throws: Nothing.
367 // Arguments: b - MyMoneyMoney object to be multiplied
368 //
369 ////////////////////////////////////////////////////////////////////////////////
operator *(const MyMoneyMoney & _b) const370 const MyMoneyMoney MyMoneyMoney::operator*(const MyMoneyMoney& _b) const
371 {
372   return static_cast<const MyMoneyMoney>(AlkValue::operator*(_b));
373 }
374 
375 ////////////////////////////////////////////////////////////////////////////////
376 //      Name: operator/
377 //   Purpose: Division operator - divides the object by the input amount
378 //   Returns: The current object
379 //    Throws: Nothing.
380 // Arguments: b - MyMoneyMoney object to be used as dividend
381 //
382 ////////////////////////////////////////////////////////////////////////////////
operator /(const MyMoneyMoney & _b) const383 const MyMoneyMoney MyMoneyMoney::operator/(const MyMoneyMoney& _b) const
384 {
385   return static_cast<const MyMoneyMoney>(AlkValue::operator/(_b));
386 }
387 
isNegative() const388 bool MyMoneyMoney::isNegative() const
389 {
390   return (valueRef() < 0) ? true : false;
391 }
392 
isPositive() const393 bool MyMoneyMoney::isPositive() const
394 {
395   return (valueRef() > 0) ? true : false;
396 }
397 
isZero() const398 bool MyMoneyMoney::isZero() const
399 {
400   return valueRef() == 0;
401 }
402 
isAutoCalc() const403 bool MyMoneyMoney::isAutoCalc() const
404 {
405   return (*this == autoCalc);
406 }
407 
convert(const signed64 _denom,const AlkValue::RoundingMethod how) const408 MyMoneyMoney MyMoneyMoney::convert(const signed64 _denom, const AlkValue::RoundingMethod how) const
409 {
410   return static_cast<const MyMoneyMoney>(convertDenominator(_denom, how));
411 }
412 
reduce() const413 MyMoneyMoney MyMoneyMoney::reduce() const
414 {
415   MyMoneyMoney out(*this);
416   out.canonicalize();
417   return out;
418 }
419 
precToDenom(int prec)420 signed64 MyMoneyMoney::precToDenom(int prec)
421 {
422   signed64 denom = 1;
423 
424   while (prec--)
425     denom *= 10;
426 
427   return denom;
428 }
429 
toDouble() const430 double MyMoneyMoney::toDouble() const
431 {
432   return valueRef().get_d();
433 }
434 
denomToPrec(signed64 fract)435 int MyMoneyMoney::denomToPrec(signed64 fract)
436 {
437   int rc = 0;
438   while (fract > 1) {
439     rc++;
440     fract /= 10;
441   }
442   return rc;
443 }
444 
445