summaryrefslogtreecommitdiffstats
path: root/kspread/kspread_functions_financial.cpp
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2021-05-23 20:48:35 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2021-05-29 15:16:28 +0900
commit8b78a8791bc539bcffe7159f9d9714d577cb3d7d (patch)
tree1328291f966f19a22d7b13657d3f01a588eb1083 /kspread/kspread_functions_financial.cpp
parent95834e2bdc5e01ae1bd21ac0dfa4fa1d2417fae9 (diff)
downloadkoffice-8b78a8791bc539bcffe7159f9d9714d577cb3d7d.tar.gz
koffice-8b78a8791bc539bcffe7159f9d9714d577cb3d7d.zip
Renaming of files in preparation for code style tools.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'kspread/kspread_functions_financial.cpp')
-rw-r--r--kspread/kspread_functions_financial.cpp907
1 files changed, 907 insertions, 0 deletions
diff --git a/kspread/kspread_functions_financial.cpp b/kspread/kspread_functions_financial.cpp
new file mode 100644
index 00000000..daccb640
--- /dev/null
+++ b/kspread/kspread_functions_financial.cpp
@@ -0,0 +1,907 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998-2002 The KSpread Team
+ www.koffice.org/kspread
+ Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+// built-in financial functions
+
+#include <math.h>
+
+#include "functions.h"
+#include "kspread_functions_helper.h"
+#include "valuecalc.h"
+#include "valueconverter.h"
+
+#include <tdelocale.h>
+#include <kcalendarsystem.h>
+
+using namespace KSpread;
+
+// prototypes (sorted)
+Value func_accrint (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_accrintm (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_compound (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_continuous (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_coupnum (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_db (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_ddb (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_disc (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_dollarde (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_dollarfr (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_duration (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_effective (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_euro (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_fv (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_fv_annuity (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_intrate (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_ipmt (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_ispmt (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_level_coupon (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_nominal (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_nper (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_pmt (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_ppmt (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_pv (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_pv_annuity (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_received (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_sln (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_syd (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_tbilleq (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_tbillprice (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_tbillyield (valVector args, ValueCalc *calc, FuncExtra *);
+Value func_zero_coupon (valVector args, ValueCalc *calc, FuncExtra *);
+
+// registers all financial functions
+void RegisterFinancialFunctions()
+{
+ FunctionRepository* repo = FunctionRepository::self();
+ Function *f;
+
+ f = new Function ("ACCRINT", func_accrint);
+ f->setParamCount (6, 7);
+ repo->add (f);
+ f = new Function ("ACCRINTM", func_accrintm);
+ f->setParamCount (3, 5);
+ repo->add (f);
+ f = new Function ("COMPOUND", func_compound);
+ f->setParamCount (4);
+ repo->add (f);
+ f = new Function ("CONTINUOUS", func_continuous);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("COUPNUM", func_coupnum);
+ f->setParamCount (3, 5);
+ repo->add (f);
+ f = new Function ("DB", func_db);
+ f->setParamCount (4, 5);
+ repo->add (f);
+ f = new Function ("DDB", func_ddb);
+ f->setParamCount (4, 5);
+ repo->add (f);
+ f = new Function ("DISC", func_disc);
+ f->setParamCount (4, 5);
+ repo->add (f);
+ f = new Function ("DOLLARDE", func_dollarde);
+ f->setParamCount (2);
+ repo->add (f);
+ f = new Function ("DOLLARFR", func_dollarfr);
+ f->setParamCount (2);
+ repo->add (f);
+ f = new Function ("DURATION", func_duration);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("EFFECT", func_effective);
+ f->setParamCount (2);
+ repo->add (f);
+ f = new Function ("EFFECTIVE", func_effective);
+ f->setParamCount (2);
+ repo->add (f);
+ f = new Function ("EURO", func_euro); // KSpread-specific, Gnumeric-compatible
+ f->setParamCount (1);
+ repo->add (f);
+ f = new Function ("FV", func_fv);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("FV_ANNUITY", func_fv_annuity);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("INTRATE", func_intrate);
+ f->setParamCount (4, 5);
+ repo->add (f);
+ f = new Function ("IPMT", func_ipmt);
+ f->setParamCount (4, 6);
+ repo->add (f);
+ f = new Function ("ISPMT", func_ispmt);
+ f->setParamCount (4);
+ repo->add (f);
+ f = new Function ("LEVEL_COUPON", func_level_coupon);
+ f->setParamCount (5);
+ repo->add (f);
+ f = new Function ("NOMINAL", func_nominal);
+ f->setParamCount (2);
+ repo->add (f);
+ f = new Function ("NPER", func_nper);
+ f->setParamCount (3, 5);
+ repo->add (f);
+ f = new Function ("PMT", func_pmt);
+ f->setParamCount (3, 5);
+ repo->add (f);
+ f = new Function ("PPMT", func_ppmt);
+ f->setParamCount (4, 6);
+ repo->add (f);
+ f = new Function ("PV", func_pv);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("PV_ANNUITY", func_pv_annuity);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("RECEIVED", func_received);
+ f->setParamCount (4, 5);
+ repo->add (f);
+ f = new Function ("SLN", func_sln);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("SYD", func_syd);
+ f->setParamCount (4);
+ repo->add (f);
+ f = new Function ("TBILLEQ", func_tbilleq);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("TBILLPRICE", func_tbillprice);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("TBILLYIELD", func_tbillyield);
+ f->setParamCount (3);
+ repo->add (f);
+ f = new Function ("ZERO_COUPON", func_zero_coupon);
+ f->setParamCount (3);
+ repo->add (f);
+}
+
+static Value getPay (ValueCalc *calc, Value rate,
+ Value nper, Value pv, Value fv, Value type)
+{
+ Value pvif, fvifa;
+
+ if (calc->isZero (rate)) return Value::errorVALUE();
+
+ //pvif = pow( 1 + rate, nper );
+ //fvifa = ( pvif - 1 ) / rate;
+ pvif = calc->pow (calc->add (rate, 1), nper);
+ fvifa = calc->div (calc->sub (pvif, 1), rate);
+
+ // ( -pv * pvif - fv ) / ( ( 1.0 + rate * type ) * fvifa );
+ Value val1 = calc->sub (calc->mul (calc->mul (-1, pv), pvif), fv);
+ Value val2 = calc->mul (calc->add (1.0, calc->mul (rate, type)),
+ fvifa);
+ return calc->div (val1, val2);
+}
+
+static Value getPrinc (ValueCalc *calc, Value start,
+ Value pay, Value rate, Value period)
+{
+ // val1 = pow( 1 + rate, period )
+ Value val1 = calc->pow (calc->add (rate, 1), period);
+ // val2 = start * val1
+ Value val2 = calc->mul (start, val1);
+ // val3 = pay * ( ( val1 - 1 ) / rate )
+ Value val3 = calc->mul (pay, calc->div (calc->sub (val1, 1), rate));
+ // result = val2 + val3
+ return calc->add (val2, val3);
+}
+
+// Function: COUPNUM - taken from GNUMERIC
+Value func_coupnum (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // dates and integers only - don't need high-precision for this
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+ int frequency = calc->conv()->asInteger (args[2]).asInteger();
+ int basis = 0;
+ bool eom = true;
+ if (args.count() > 3)
+ basis = calc->conv()->asInteger (args[3]).asInteger();
+ if (args.count() == 5)
+ eom = calc->conv()->asBoolean (args[4]).asBoolean();
+
+ if (basis < 0 || basis > 5 || ( frequency == 0 ) || ( 12 % frequency != 0 )
+ || settlement.daysTo( maturity ) <= 0)
+ return Value::errorVALUE();
+
+ double result;
+ TQDate cDate( maturity );
+
+ int months = maturity.month() - settlement.month()
+ + 12 * ( maturity.year() - settlement.year() );
+
+ cDate = calc->conv()->locale()->calendar()->addMonths (cDate, -months);
+
+ if ( eom && maturity.daysInMonth() == maturity.day() )
+ {
+ while( cDate.daysInMonth() != cDate.day() )
+ cDate.addDays( 1 );
+ }
+
+ if ( settlement.day() >= cDate.day() )
+ --months;
+
+ result = ( 1 + months / ( 12 / frequency ) );
+
+ return Value (result);
+}
+
+// Function: ACCRINT
+Value func_accrint (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate maturity = calc->conv()->asDate (args[0]).asDate();
+ TQDate firstInterest = calc->conv()->asDate (args[1]).asDate();
+ TQDate settlement = calc->conv()->asDate (args[2]).asDate();
+
+ Value rate = args[3];
+ Value par = args[4];
+ int frequency = calc->conv()->asInteger (args[5]).asInteger();
+
+ int basis = 0;
+ if (args.count() == 7)
+ basis = calc->conv()->asInteger (args[6]).asInteger();
+
+ if ( basis < 0 || basis > 4 || (calc->isZero (frequency)) ||
+ (12 % frequency != 0))
+ return Value::errorVALUE();
+
+ if ( ( settlement.daysTo( firstInterest ) < 0 )
+ || ( firstInterest.daysTo( maturity ) > 0 ) )
+ return Value::errorVALUE();
+
+ double d = daysBetweenDates (maturity, settlement, basis);
+ double y = daysPerYear (maturity, basis);
+
+ if ( d < 0 || y <= 0 || calc->lower (par, 0) || calc->lower (rate, 0) ||
+ calc->isZero (rate))
+ return Value::errorVALUE();
+
+ Value coeff = calc->div (calc->mul (par, rate), frequency);
+ double n = d / y;
+
+ return calc->mul (coeff, n * frequency);
+}
+
+// Function: ACCRINTM
+Value func_accrintm (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate issue = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+ Value rate = args[2];
+
+ Value par = 1000;
+ int basis = 0;
+ if (args.count() > 3)
+ par = args[3];
+ if (args.count() == 5)
+ basis = calc->conv()->asInteger (args[4]).asInteger ();
+
+ double d = daysBetweenDates (issue, maturity, basis);
+ double y = daysPerYear (issue, basis);
+
+ if (d < 0 || y <= 0 || calc->isZero (par) || calc->isZero (rate) ||
+ calc->lower (par, 0) || calc->lower (rate, 0) || basis < 0 || basis > 4)
+ return Value::errorVALUE();
+
+ // par*date * d/y
+ return calc->mul (calc->mul (par, rate), d / y);
+}
+
+// Function: DISC
+Value func_disc (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value par = args[2];
+ Value redemp = args[3];
+
+ int basis = 0;
+ if (args.count() == 5)
+ basis = calc->conv()->asInteger (args[4]).asInteger();
+
+ double y = daysPerYear (settlement, basis);
+ double d = daysBetweenDates (settlement, maturity, basis);
+
+ if ( y <= 0 || d <= 0 || basis < 0 || basis > 4 || calc->isZero (redemp) )
+ return false;
+
+ // (redemp - par) / redemp * (y / d)
+ return calc->mul (calc->div (calc->sub (redemp, par), redemp), y / d);
+}
+
+
+// Function: TBILLPRICE
+Value func_tbillprice (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value discount = args[2];
+
+ double days = settlement.daysTo( maturity );
+
+ if (settlement > maturity || calc->lower (discount, 0) || days > 265)
+ return Value::errorVALUE();
+
+ // (discount * days) / 360.0
+ Value val = calc->div (calc->mul (discount, days), 360.0);
+ // 100 * (1.0 - val);
+ return calc->mul (calc->sub (1.0, val), 100);
+}
+
+// Function: TBILLYIELD
+Value func_tbillyield (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value rate = args[2];
+
+ double days = settlement.daysTo( maturity );
+
+ if (settlement > maturity || calc->isZero (rate) || calc->lower (rate, 0)
+ || days > 265)
+ return Value::errorVALUE();
+
+ // (100.0 - rate) / rate * (360.0 / days);
+ return calc->mul (calc->div (calc->sub (100.0, rate), rate), 360.0 / days);
+}
+
+// Function: TBILLEQ
+Value func_tbilleq (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value discount = args[2];
+
+ double days = settlement.daysTo( maturity );
+
+ if (settlement > maturity || calc->lower (discount, 0) || days > 265)
+ return Value::errorVALUE();
+
+ // 360 - discount*days
+ Value divisor = calc->sub (360.0, calc->mul (discount, days));
+ if (calc->isZero (divisor))
+ return Value::errorVALUE();
+
+ // 365.0 * discount / divisor
+ return calc->mul (calc->div (discount, divisor), 356.0);
+}
+
+// Function: RECEIVED
+Value func_received (valVector args, ValueCalc *calc, FuncExtra *)
+{
+
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value investment = args[2];
+ Value discount = args[3];
+
+ int basis = 0;
+ if (args.count() == 5)
+ basis = calc->conv()->asInteger (args[4]).asInteger();
+
+ double d = daysBetweenDates( settlement, maturity, basis );
+ double y = daysPerYear( settlement, basis );
+
+ if ( d <= 0 || y <= 0 || basis < 0 || basis > 4 )
+ return false;
+
+ // 1.0 - ( discount * d / y )
+ Value x = calc->sub (1.0, (calc->mul (discount, d / y)));
+
+ if (calc->isZero (x))
+ return Value::errorVALUE();
+ return calc->div (investment, x);
+}
+
+// Function: DOLLARDE
+Value func_dollarde (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value d = args[0];
+ Value f = args[1];
+
+ if (!calc->greater (f, 0))
+ return Value::errorVALUE();
+
+ Value tmp = f;
+ int n = 0;
+ while (calc->greater (tmp, 0))
+ {
+ tmp = calc->div (tmp, 10);
+ ++n;
+ }
+
+ Value fl = calc->roundDown (d);
+ Value r = calc->sub (d, fl);
+
+ // fl + (r * pow(10.0, n) / f)
+ return calc->add (fl, calc->div (calc->mul (r, pow (10.0, n)), f));
+}
+
+// Function: DOLLARFR
+Value func_dollarfr (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value d = args[0];
+ Value f = args[1];
+
+ if (!calc->greater (f, 0))
+ return Value::errorVALUE();
+
+ Value tmp = f;
+ int n = 0;
+ while (calc->greater (tmp, 0))
+ {
+ tmp = calc->div (tmp, 10);
+ ++n;
+ }
+
+ Value fl = calc->roundDown (d);
+ Value r = calc->sub (d, fl);
+
+ // fl + ((r * f) / pow (10.0, n));
+ return calc->add (fl, calc->div (calc->mul (r, f), pow (10.0, n)));
+}
+
+/// *** TODO continue here ***
+
+// Function: INTRATE
+Value func_intrate (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQDate settlement = calc->conv()->asDate (args[0]).asDate();
+ TQDate maturity = calc->conv()->asDate (args[1]).asDate();
+
+ Value invest = args[2];
+ Value redemption = args[3];
+
+ int basis = 0;
+ if (args.count() == 5)
+ basis = calc->conv()->asInteger (args[4]).asInteger();
+
+ double d = daysBetweenDates (settlement, maturity, basis);
+ double y = daysPerYear (settlement, basis);
+
+ if ( d <= 0 || y <= 0 || calc->isZero (invest) || basis < 0 || basis > 4 )
+ return Value::errorVALUE();
+
+ // (redemption - invest) / invest * (y / d)
+ return calc->mul (calc->div (calc->sub (redemption, invest), invest), y/d);
+}
+
+
+// Function: DURATION
+Value func_duration (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value rate = args[0];
+ Value pv = args[1];
+ Value fv = args[2];
+
+ if (!calc->greater (rate, 0.0))
+ return Value::errorVALUE();
+ if (calc->isZero (fv) || calc->isZero (pv))
+ return Value::errorDIV0();
+
+ if (calc->lower (calc->div (fv, pv), 0))
+ return Value::errorVALUE();
+
+ // log(fv / pv) / log(1.0 + rate)
+ return calc->div (calc->ln (calc->div (fv, pv)),
+ calc->ln (calc->add (rate, 1.0)));
+}
+
+// Function: PMT
+Value func_pmt (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value rate = args[0];
+ Value nper = args[1];
+ Value pv = args[2];
+ Value fv = 0.0;
+ Value type = 0;
+ if (args.count() > 3) fv = args[3];
+ if (args.count() == 5) type = args[4];
+
+ return getPay (calc, rate, nper, pv, fv, type);
+}
+
+// Function: NPER
+Value func_nper (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value rate = args[0];
+ Value pmt = args[1];
+ Value pv = args[2];
+ Value fv = 0.0;
+ Value type = 0;
+ if (args.count() > 3) fv = args[3];
+ if (args.count() == 5) type = args[4];
+
+ if (!calc->greater (rate, 0.0))
+ return Value::errorVALUE();
+
+ // taken from Gnumeric
+ // v = 1.0 + rate * type
+ // d1 = pmt * v - fv * rate
+ // d2 = pmt * v - pv * rate
+ // res = d1 / d2;
+ Value v = calc->add (calc->mul (rate, type), 1.0);
+ Value d1 = calc->sub (calc->mul (pmt, v), calc->mul (fv, rate));
+ Value d2 = calc->add (calc->mul (pmt, v), calc->mul (pv, rate));
+ Value res = calc->div (d1, d2);
+
+ if (!calc->greater (res, 0.0)) // res must be >0
+ return Value::errorVALUE();
+
+ // ln (res) / ln (rate + 1.0)
+ return calc->div (calc->ln (res), calc->ln (calc->add (rate, 1.0)));
+}
+
+// Function: ISPMT
+Value func_ispmt (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value rate = args[0];
+ Value per = args[1];
+ Value nper = args[2];
+ Value pv = args[3];
+
+ if (calc->lower (per, 1) || calc->greater (per, nper))
+ return Value::errorVALUE();
+
+ // d = -pv * rate
+ Value d = calc->mul (calc->mul (pv, -1), rate);
+
+ // d - (d / nper * per)
+ return calc->sub (d, calc->mul (calc->div (d, nper), per));
+}
+
+// Function: IPMT
+Value func_ipmt (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value rate = args[0];
+ Value per = args[1];
+ Value nper = args[2];
+ Value pv = args[3];
+
+ Value fv = 0.0;
+ Value type = 0;
+ if (args.count() > 4) fv = args[4];
+ if (args.count() == 6) type = args[5];
+
+ Value payment = getPay (calc, rate, nper, pv, fv, type);
+ Value ineg = getPrinc (calc, pv, payment, rate, calc->sub (per, 1));
+
+ // -ineg * rate
+ return calc->mul (calc->mul (ineg, -1), rate);
+}
+
+// Function: PPMT
+// Uses IPMT.
+Value func_ppmt (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ /*
+Docs partly copied from OO.
+Syntax
+PPMT(Rate;Period;NPER;PV;FV;Type)
+
+Rate is the periodic interest rate.
+Period is the amortizement period. P=1 for the first and P=NPER for the last period.
+NPER is the total number of periods during which annuity is paid.
+PV is the present value in the sequence of payments.
+FV (optional) is the desired (future) value.
+Type (optional) defines the due date. F=1 for payment at the beginning of a period and F=0 for payment at the end of a period.
+ */
+
+ Value rate = args[0];
+ Value per = args[1];
+ Value nper = args[2];
+ Value pv = args[3];
+ Value fv = 0.0;
+ Value type = 0;
+ if (args.count() > 4) fv = args[4];
+ if (args.count() == 6) type = args[5];
+
+ Value pay = getPay (calc, rate, nper, pv, fv, type);
+ Value ipmt = func_ipmt (args, calc, 0);
+ return calc->sub (pay, ipmt);
+}
+
+// Function: FV
+/* Returns future value, given current value, interest rate and time */
+Value func_fv (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value present = args[0];
+ Value interest = args[1];
+ Value periods = args[2];
+
+ // present * pow (1 + interest, periods)
+ return calc->mul (present, calc->pow (calc->add (interest, 1), periods));
+}
+
+// Function: compound
+/* Returns value after compounded interest, given principal, rate, periods
+per year and year */
+ Value func_compound (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value principal = args[0];
+ Value interest = args[1];
+ Value periods = args[2];
+ Value years = args[3];
+
+ // principal * pow(1+ (interest / periods), periods*years);
+ Value base = calc->add (calc->div (interest, periods), 1);
+ return calc->mul (principal, calc->pow (base, calc->mul (periods, years)));
+}
+
+// Function: continuous
+/* Returns value after continuous compounding of interest, given principal,
+rate and years */
+Value func_continuous (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // If you still don't understand this, let me know! ;-) jsinger@leeta.net
+ Value principal = args[0];
+ Value interest = args[1];
+ Value years = args[2];
+
+ // principal * exp(interest * years)
+ return calc->mul (principal, calc->exp (calc->mul (interest, years)));
+}
+
+// Function: PV
+Value func_pv (valVector args, ValueCalc *calc, FuncExtra *)
+{
+/* Returns presnt value, given future value, interest rate and years */
+ Value future = args[0];
+ Value interest = args[1];
+ Value periods = args[2];
+
+ // future / pow(1+interest, periods)
+ return calc->div (future, calc->pow (calc->add (interest, 1), periods));
+}
+
+// Function: PV_annuity
+Value func_pv_annuity (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value amount = args[0];
+ Value interest = args[1];
+ Value periods = args[2];
+
+ // recpow = 1 / pow (1 + interest, periods)
+ // result = amount * (1 - recpow) / interest;
+ Value recpow;
+ recpow = calc->div (1, calc->pow (calc->add (interest, 1), periods));
+ return calc->mul (amount, calc->div (calc->sub (1, recpow), interest));
+}
+
+// Function: FV_annnuity
+Value func_fv_annuity (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ /* Returns future value of an annuity or cash flow, given payment, interest
+ rate and periods */
+
+ Value amount = args[0];
+ Value interest = args[1];
+ Value periods = args[2];
+
+ // pw = pow (1 + interest, periods)
+ // result = amount * ((pw - 1) / interest)
+ Value pw = calc->pow (calc->add (interest, 1), periods);
+ return calc->mul (amount, calc->div (calc->sub (pw, 1), interest));
+}
+
+// Function: effective
+Value func_effective (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // Returns effective interest rate given nominal rate and periods per year
+
+ Value nominal = args[0];
+ Value periods = args[1];
+
+ // base = 1 + (nominal / periods)
+ // result = pow (base, periods) - 1
+ Value base = calc->add (calc->div (nominal, periods), 1);
+ return calc->sub (calc->pow (base, periods), 1);
+}
+
+// Function: zero_coupon
+Value func_zero_coupon (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // Returns effective interest rate given nominal rate and periods per year
+
+ Value face = args[0];
+ Value rate = args[1];
+ Value years = args[2];
+
+ // face / pow(1 + rate, years)
+ return calc->div (face, calc->pow (calc->add (rate, 1), years));
+}
+
+// Function: level_coupon
+Value func_level_coupon (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // Returns effective interest rate given nominal rate and periods per year
+ Value face = args[0];
+ Value coupon_rate = args[1];
+ Value coupon_year = args[2];
+ Value years = args[3];
+ Value market_rate = args[4];
+
+ Value coupon, interest, pw, pv_annuity;
+ // coupon = coupon_rate * face / coupon_year
+ // interest = market_rate / coupon_year
+ // pw = pow(1 + interest, years * coupon_year)
+ // pv_annuity = (1 - 1 / pw) / interest
+ // result = coupon * pv_annuity + face / pw
+ coupon = calc->mul (coupon_rate, calc->div (face, coupon_year));
+ interest = calc->div (market_rate, coupon_year);
+ pw = calc->pow (calc->add (interest, 1), calc->mul (years, coupon_year));
+ pv_annuity = calc->div (calc->sub (1, calc->div (1, pw)), interest);
+ return calc->add (calc->mul (coupon, pv_annuity), calc->div (face, pw));
+}
+
+// Function: nominal
+Value func_nominal (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value effective = args[0];
+ Value periods = args[1];
+
+ if (calc->isZero (periods)) // Check null
+ return Value::errorDIV0();
+
+ // pw = pow (effective + 1, 1 / periods)
+ // result = periods * (pw - 1);
+ Value pw;
+ pw = calc->pow (calc->add (effective, 1), calc->div (1, periods));
+ return calc->mul (periods, calc->sub (pw, 1));
+}
+
+// Function: SLN
+/* straight-line depreciation for a single period */
+Value func_sln (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value cost = args[0];
+ Value salvage_value = args[1];
+ Value life = args[2];
+
+ // sentinel check
+ if (!calc->greater (life, 0.0))
+ return Value::errorVALUE();
+
+ // (cost - salvage_value) / life
+ return calc->div (calc->sub (cost, salvage_value), life);
+}
+
+// Function: SYD
+/* sum-of-years digits depreciation */
+Value func_syd (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ Value cost = args[0];
+ Value salvage_value = args[1];
+ Value life = args[2];
+ Value period = args[3];
+
+ // sentinel check
+ if (!calc->greater (life, 0.0))
+ return Value::errorVALUE();
+
+ // v1 = cost - salvage_value
+ // v2 = life - period + 1
+ // v3 = life * (life + 1.0)
+ // result = (v1 * v2 * 2) / v3
+ Value v1, v2, v3;
+ v1 = calc->sub (cost, salvage_value);
+ v2 = calc->add (calc->sub (life, period), 1);
+ v3 = calc->mul (life, calc->add (life, 1.0));
+ return calc->div (calc->mul (calc->mul (v1, v2), 2), v3);
+}
+
+// Function: DB
+/* fixed-declining depreciation */
+Value func_db (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ // This function doesn't support extended datatypes, it simply
+ // converts everything to double - because it does quite a bit
+ // of computing, and, well, I'm lazy to convert it all (Tomas)
+ double cost = calc->conv()->asFloat (args[0]).asFloat();
+ double salvage = calc->conv()->asFloat (args[1]).asFloat();
+ double life = calc->conv()->asFloat (args[2]).asFloat();
+ double period = calc->conv()->asFloat (args[3]).asFloat();
+ double month = 12;
+ if (args.count() == 5)
+ month = calc->conv()->asFloat (args[4]).asFloat();
+
+ // sentinel check
+ if (cost == 0 || life <= 0.0)
+ return Value::errorVALUE ();
+
+ if (calc->lower (calc->div (salvage, cost), 0))
+ return Value::errorVALUE ();
+
+ double rate = 1000 * (1 - pow( (salvage/cost), (1/life) ));
+ rate = floor( rate + 0.5 ) / 1000;
+
+ double total = cost * rate * month / 12;
+
+ if( period == 1 )
+ return Value (total);
+
+ for (int i = 1; i < life; ++i)
+ if (i == period - 1)
+ return Value (rate * (cost-total));
+ else total += rate * (cost-total);
+
+ return Value ((cost-total) * rate * (12-month)/12);
+}
+
+// Function: DDB
+/* depreciation per period */
+Value func_ddb (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ double cost = calc->conv()->asFloat (args[0]).asFloat();
+ double salvage = calc->conv()->asFloat (args[1]).asFloat();
+ double life = calc->conv()->asFloat (args[2]).asFloat();
+ double period = calc->conv()->asFloat (args[3]).asFloat();
+ double factor = 2;
+ if (args.count() == 5)
+ factor = calc->conv()->asFloat (args[4]).asFloat();
+
+ double total = 0.0;
+
+ if ( cost < 0.0 || salvage < 0.0 || life <= 0.0 || period < 0.0 || factor < 0.0 )
+ return Value::errorVALUE();
+
+ for( int i = 0; i < life-1; ++i )
+ {
+ double periodDep = ( cost - total ) * ( factor / life );
+ if ( i == period - 1 )
+ return Value (periodDep);
+ else
+ total += periodDep;
+ }
+
+ return Value (cost - total - salvage);
+}
+
+// Function: EURO
+Value func_euro (valVector args, ValueCalc *calc, FuncExtra *)
+{
+ TQString currency = calc->conv()->asString (args[0]).asString().upper();
+ double result = -1;
+
+ if( currency == "ATS" ) result = 13.7603; // Austria
+ else if( currency == "BEF" ) result = 40.3399; // Belgium
+ else if( currency == "DEM" ) result = 1.95583; // Germany
+ else if( currency == "ESP" ) result = 166.386; // Spain
+ else if( currency == "FIM" ) result = 5.94573; // Finland
+ else if( currency == "FRF" ) result = 6.55957; // France
+ else if( currency == "GRD" ) result = 340.75; // Greece
+ else if( currency == "IEP" ) result = 0.787564; // Ireland
+ else if( currency == "ITL" ) result = 1936.27; // Italy
+ else if( currency == "LUX" ) result = 40.3399; // Luxemburg
+ else if( currency == "NLG" ) result = 2.20371; // Nederland
+ else if( currency == "PTE" ) result = 200.482; // Portugal
+ else
+ return Value::errorVALUE();
+
+ return Value (result);
+}