1 /*
2  * Copyright 2003-2012  Thomas Baumgart <tbaumgart@kde.org>
3  * Copyright 2017-2018  Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "mymoneyfinancialcalculator.h"
20 #include "mymoneyfinancialcalculator_p.h"
21 
22 #include <qglobal.h>
23 
24 // ----------------------------------------------------------------------------
25 // QT Includes
26 
27 // ----------------------------------------------------------------------------
28 // KDE Includes
29 
30 // ----------------------------------------------------------------------------
31 // Project Includes
32 
33 #include "mymoneyexception.h"
34 
dabs(const double x)35 static inline double dabs(const double x)
36 {
37   return (x >= 0.0) ? x : -x;
38 }
39 
MyMoneyFinancialCalculator()40 MyMoneyFinancialCalculator::MyMoneyFinancialCalculator() :
41   d_ptr(new MyMoneyFinancialCalculatorPrivate)
42 {
43   Q_D(MyMoneyFinancialCalculator);
44   setPrec();
45   setPF();
46   setCF();
47   setBep();
48   setDisc();
49 
50   setNpp(0.0);
51   setIr(0.0);
52   setPv(0.0);
53   setPmt(0.0);
54   setFv(0.0);
55 
56   // clear the mask
57   d->m_mask = 0;
58 }
59 
~MyMoneyFinancialCalculator()60 MyMoneyFinancialCalculator::~MyMoneyFinancialCalculator()
61 {
62   Q_D(MyMoneyFinancialCalculator);
63   delete d;
64 }
65 
setPrec(const unsigned short prec)66 void MyMoneyFinancialCalculator::setPrec(const unsigned short prec)
67 {
68   Q_D(MyMoneyFinancialCalculator);
69   d->m_prec = prec;
70 }
71 
setPF(const unsigned short PF)72 void MyMoneyFinancialCalculator::setPF(const unsigned short PF)
73 {
74   Q_D(MyMoneyFinancialCalculator);
75   d->m_PF = PF;
76 }
77 
setCF(const unsigned short CF)78 void MyMoneyFinancialCalculator::setCF(const unsigned short CF)
79 {
80   Q_D(MyMoneyFinancialCalculator);
81   d->m_CF = CF;
82 }
83 
setBep(const bool bep)84 void MyMoneyFinancialCalculator::setBep(const bool bep)
85 {
86   Q_D(MyMoneyFinancialCalculator);
87   d->m_bep = bep;
88 }
89 
setDisc(const bool disc)90 void MyMoneyFinancialCalculator::setDisc(const bool disc)
91 {
92   Q_D(MyMoneyFinancialCalculator);
93   d->m_disc = disc;
94 }
95 
setIr(const double ir)96 void MyMoneyFinancialCalculator::setIr(const double ir)
97 {
98   Q_D(MyMoneyFinancialCalculator);
99   d->m_ir = ir;
100   d->m_mask |= IR_SET;
101 }
102 
ir() const103 double MyMoneyFinancialCalculator::ir() const
104 {
105   Q_D(const MyMoneyFinancialCalculator);
106   return d->m_ir;
107 }
108 
setPv(const double pv)109 void MyMoneyFinancialCalculator::setPv(const double pv)
110 {
111   Q_D(MyMoneyFinancialCalculator);
112   d->m_pv = pv;
113   d->m_mask |= PV_SET;
114 }
115 
pv() const116 double MyMoneyFinancialCalculator::pv() const
117 {
118   Q_D(const MyMoneyFinancialCalculator);
119   return d->m_pv;
120 }
121 
setPmt(const double pmt)122 void MyMoneyFinancialCalculator::setPmt(const double pmt)
123 {
124   Q_D(MyMoneyFinancialCalculator);
125   d->m_pmt = pmt;
126   d->m_mask |= PMT_SET;
127 }
128 
pmt() const129 double MyMoneyFinancialCalculator::pmt() const
130 {
131   Q_D(const MyMoneyFinancialCalculator);
132   return d->m_pmt;
133 }
134 
setNpp(const double npp)135 void MyMoneyFinancialCalculator::setNpp(const double npp)
136 {
137   Q_D(MyMoneyFinancialCalculator);
138   d->m_npp = npp;
139   d->m_mask |= NPP_SET;
140 }
141 
npp() const142 double MyMoneyFinancialCalculator::npp() const
143 {
144   Q_D(const MyMoneyFinancialCalculator);
145   return d->m_npp;
146 }
147 
setFv(const double fv)148 void MyMoneyFinancialCalculator::setFv(const double fv)
149 {
150   Q_D(MyMoneyFinancialCalculator);
151   d->m_fv = fv;
152   d->m_mask |= FV_SET;
153 }
154 
fv() const155 double MyMoneyFinancialCalculator::fv() const
156 {
157   Q_D(const MyMoneyFinancialCalculator);
158   return d->m_fv;
159 }
160 
numPayments()161 double MyMoneyFinancialCalculator::numPayments()
162 {
163   Q_D(MyMoneyFinancialCalculator);
164   const unsigned short mask = PV_SET | IR_SET | PMT_SET | FV_SET;
165 
166   if ((d->m_mask & mask) != mask)
167     throw MYMONEYEXCEPTION_CSTRING("Not all parameters set for calculation of numPayments");
168 
169   double eint = d->eff_int();
170 
171   //add exception for zero interest
172   if (eint == 0.0) {
173     d->m_npp = -(d->m_pv / d->m_pmt);
174 
175   } else {
176     double CC = d->_Cx(eint);
177 
178     CC = (CC - d->m_fv) / (CC + d->m_pv);
179     d->m_npp = (CC > 0.0) ? log(CC) / log(eint + 1.0) : 0.0;
180 
181     d->m_mask |= NPP_SET;
182   }
183   return d->m_npp;
184 }
185 
payment()186 double MyMoneyFinancialCalculator::payment()
187 {
188   Q_D(MyMoneyFinancialCalculator);
189   const unsigned short mask = PV_SET | IR_SET | NPP_SET | FV_SET;
190 
191   if ((d->m_mask & mask) != mask)
192     throw MYMONEYEXCEPTION_CSTRING("Not all parameters set for calculation of payment");
193 
194   double eint = d->eff_int();
195 
196   //add exception for zero interest
197   if (eint == 0.0) {
198     d->m_pmt = -(d->m_pv / d->m_npp);
199   } else {
200     double AA = d->_Ax(eint);
201     double BB = d->_Bx(eint);
202 
203     d->m_pmt = -d->rnd((d->m_fv + d->m_pv * (AA + 1.0)) / (AA * BB));
204   }
205 
206   d->m_mask |= PMT_SET;
207   return d->m_pmt;
208 }
209 
presentValue()210 double MyMoneyFinancialCalculator::presentValue()
211 {
212   Q_D(MyMoneyFinancialCalculator);
213   const unsigned short mask = PMT_SET | IR_SET | NPP_SET | FV_SET;
214 
215   if ((d->m_mask & mask) != mask)
216     throw MYMONEYEXCEPTION_CSTRING("Not all parameters set for calculation of payment");
217 
218   double eint = d->eff_int();
219 
220   //add exception for zero interest
221   if (eint == 0.0) {
222     d->m_pv = -(d->m_fv + (d->m_npp * d->m_pmt));
223   } else {
224     double AA = d->_Ax(eint);
225     double CC = d->_Cx(eint);
226 
227     d->m_pv = d->rnd(-(d->m_fv + (AA * CC)) / (AA + 1.0));
228 
229   }
230 
231   d->m_mask |= PV_SET;
232   return d->m_pv;
233 }
234 
futureValue()235 double MyMoneyFinancialCalculator::futureValue()
236 {
237   Q_D(MyMoneyFinancialCalculator);
238   const unsigned short mask = PMT_SET | IR_SET | NPP_SET | PV_SET;
239 
240   if ((d->m_mask & mask) != mask)
241     throw MYMONEYEXCEPTION_CSTRING("Not all parameters set for calculation of payment");
242 
243   double eint = d->eff_int();
244 
245   //add exception for zero interest
246   if (eint == 0.0) {
247     d->m_fv = d->rnd(-(d->m_pv + (d->m_npp * d->m_pmt)));
248   } else {
249     double AA = d->_Ax(eint);
250     double CC = d->_Cx(eint);
251     d->m_fv = d->rnd(-(d->m_pv + AA * (d->m_pv + CC)));
252   }
253 
254   d->m_mask |= FV_SET;
255   return d->m_fv;
256 }
257 
interestRate()258 double MyMoneyFinancialCalculator::interestRate()
259 {
260   Q_D(MyMoneyFinancialCalculator);
261   double eint = 0.0;
262   double a = 0.0;
263   double dik = 0.0;
264 
265   const double ratio = 1e4;
266   int ri;
267 
268   if (d->m_pmt == 0.0) {
269     eint = pow((dabs(d->m_fv) / dabs(d->m_pv)), (1.0 / d->m_npp)) - 1.0;
270   } else {
271     if ((d->m_pmt * d->m_fv) < 0.0) {
272       if (d->m_pv)
273         a = -1.0;
274       else
275         a = 1.0;
276       eint =
277         dabs((d->m_fv + a * d->m_npp * d->m_pmt) /
278              (3.0 *
279               ((d->m_npp - 1.0) * (d->m_npp - 1.0) * d->m_pmt + d->m_pv -
280                d->m_fv)));
281     } else {
282       if ((d->m_pv * d->m_pmt) < 0.0) {
283         eint = dabs((d->m_npp * d->m_pmt + d->m_pv + d->m_fv) / (d->m_npp * d->m_pv));
284       } else {
285         a = dabs(d->m_pmt / (dabs(d->m_pv) + dabs(d->m_fv)));
286         eint = a + 1.0 / (a * d->m_npp * d->m_npp * d->m_npp);
287       }
288     }
289     do {
290       try {
291         dik = d->_fi(eint) / d->_fip(eint);
292         eint -= dik;
293       } catch (const MyMoneyException &) {
294         eint = 0;
295       }
296       (void) modf(ratio *(dik / eint), &a);
297       ri = static_cast<unsigned>(a);
298     } while (ri);
299   }
300   d->m_mask |= IR_SET;
301   d->m_ir = d->rnd(d->nom_int(eint) * 100.0);
302   return d->m_ir;
303 }
304 
interestDue() const305 double MyMoneyFinancialCalculator::interestDue() const
306 {
307   Q_D(const MyMoneyFinancialCalculator);
308   double eint = d->eff_int();
309 
310   return (d->m_pv + (d->m_bep ? d->m_pmt : 0.0)) * eint;
311 }
312 
313