Loading [MathJax]/extensions/tex2jax.js
MRFFL: MR Fortran Finance Library 2024-12-28
Computational Tools For Finance
All Namespaces Files Functions Variables
mrffl_tvm.f90
Go to the documentation of this file.
1! -*- Mode:F90; Coding:us-ascii-unix; fill-column:129 -*-
2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.S.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.E.!!
3!>
4!! @file mrffl_tvm.f90
5!! @author Mitch Richling http://www.mitchr.me/
6!! @date 2024-12-19
7!! @brief Time value of money solvers.@EOL
8!! @keywords finance fortran monte carlo inflation cashflow time value of money tvm percentages taxes stock market
9!! @std F2023
10!! @see https://github.com/richmit/FortranFinance
11!! @copyright
12!! @parblock
13!! Copyright (c) 2024, Mitchell Jay Richling <http://www.mitchr.me/> All rights reserved.
14!!
15!! Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
16!! conditions are met:
17!!
18!! 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following
19!! disclaimer.
20!!
21!! 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following
22!! disclaimer in the documentation and/or other materials provided with the distribution.
23!!
24!! 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
25!! derived from this software without specific prior written permission.
26!!
27!! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
28!! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29!! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30!! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31!! USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32!! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33!! OF THE POSSIBILITY OF SUCH DAMAGE.
34!! @endparblock
35!!
36
37!------------------------------------------------------------------------------------------------------------------------------
38!> Solvers for TVM problems involving lump sums, level (fixed) annuities, and geometric (growing) annuities.
39!!
40!! @par Definitions and Notation
41!!
42!! - Annuity in advance, annuity due: Payment at beginning of each period. Ex: Rent and subscription fees.
43!! - Ordinary annuity, annuity in arrears, annuity-immediate: Payment at the end of each period. Ex: Mortgages and car loans.
44!! - Annuity certain, guaranteed annuity: The number of payments is known in advance
45!! - Deferred annuity: An annuity that begins payments only after a number of periods.
46!! - Early end annuity: An annuity that that terminates before the number of periods. This can be because the annuity
47!! was contingent; however, it is more frequently an artificial condition used to base the PV/FV computation on a different
48!! term than that for which the annuity was intended.
49!! - Annuity payment growth:
50!! - fixed, level: all payments are the same
51!! - growing, geometric: payments grow geometrically
52!! - arithmatic: payments grow linearly
53!!
54!! In working with people from different places and from many different fields, I have discovered some of the above terminology is
55!! used differently or becomes confusing depending upon the audience. To avoid such issues, I follow the following rules in this
56!! package:
57!!
58!! - I avoid the adjective "fixed" with regard to annuities because it is confused with the adjectives "certain" & "guaranteed".
59!! As such, "fixed annuities" are called "level annuities" in this package.
60!!
61!! - I avoid the term "annuity-immediate" because many confuse it with annuities that are not "deferred" -- i.e. pay immediately
62!! upon purchase. As such I use the phrase "ordinary annuity".
63!!
64!! - I avoid the adjectives "guaranteed" and "guarantee" with regard to annuities because they are confused with the term
65!! "non-contingent". As such I use the word "certain". In this module all annuities are certain, so it is safe to assume an
66!! annuity is certain when it's not explicity specified.
67!!
68!! - I avoid the adjective "growing" with regard to annuities simply because it is too generic and offends my mathematical sense
69!! of precision. Arithmetic annuities are "growing" too?!? Right?!? As such I use the term "geometric".
70!!
71!! @par Approach
72!!
73!! It's common practice in financial problem solving to produce a single set of TVM equations for a particular scenario, and then
74!! solve the resulting equation(s). For example a loan might be thought of as a single cashflow stream starting with a negative
75!! cashflow for the principal followed by sequence of negative, equal cash flows for the payments with the overall condition that
76!! the cashflow stream has a future value of zero. As an aside, this is the fundamental "TVM Equation" used by modern financial
77!! calculators -- so this approach can lead to single equations that are broadly applicable to many problems.
78!!
79!! Most TVM problems may be broken down into distinct components corresponding to well known, fundamental TVM problems. It is
80!! frequently possible to solve the overall problem by solving these smaller, well known problems in isolation. For example a
81!! loan can be thought of as two cashflow sequences: An ordinary lump sum and an ordinary annuity certain -- we simply require
82!! that each of these two cashflows have equal future values.
83!!
84!! This module encourages this second method of solving TVM problems by providing very generic TVM solvers for some very common
85!! cashflow patterns (various forms of annuities & lump sums). This allows one to mix and match the solvers as required applying
86!! them to the components of a typical TVM problem.
87!!
88!! @par Annuities
89!!
90!! The annuity solvers in this module are uniquely flexible. In most software annuities are defined as a fixed number of periods
91!! with a single payment occurring in each period such that all payments occur either at the beginning (ordinary annuities) or
92!! end (annuities due) of the period. Instead of associating payments with periods, this module ties them directly to the
93!! *boundaries* between periods -- an @f$n@f$ period annuity has @f$n+1@f$ boundaries (one at time zero, one at time @f$n@f$, and
94!! @f$n-1@f$ between periods).
95!!
96!! In this module the number of periods (`n`) and the boundaries at which the payments start (`d`) and end (`e`) are each free
97!! variables in the definition of an annuity. This allows us to handle ordinary annuities, annuities due, differed annuities, and
98!! truncated annuities with just a single solver! In addition we can use this flexibility to base PV/FV computations on a term
99!! different from the natural term of the annuity -- for example we can compute the PV for an annuity due on @f$n-1@f$ periods
100!! instead of @f$n@f$ periods. Using this notation, we can obtain typical annuities like so:
101!!
102!! - An N payment ordinary annuity: n=N, d=1, e=0
103!! - An N payment annuity due: n=N, d=0, e=1
104!! Note that PV/FV are normally computed for N periods for these types of annuities; however, the last payment is at the
105!! beginning of the last period. This leaves an "empty" period at the end. Sometimes it is preferable to base the
106!! computations on N-1 periods instead. To do this, use N-1 as the period count and set e=0.
107!! - For a delayed ordinary annuity that starts paying in period k, set d=k
108!! - For a delayed annuity due that starts paying in period k, set d=k-1
109!!
110!! @par Lump Sums
111!!
112!! Like the annuity solvers, the lump sum solver in this module is also uniquely flexible. Instead of the lump sum always being
113!! paid at the beginning of the first period, this library allows one to specify the payment on any period boundary.
114!!
115!! - For an "ordinary lump sum" use d=0
116!! Note that some packages call this "a lump sum with payment mode `BEGIN`"
117!! Because this is a very common case, a "helper" function exists to make this easier: tvm_lump_sum_solve
118!! - For a "lump sum due" use d=1
119!! Note that some packages call this "a lump sum with payment mode `END`"
120!!
121!! @par Solvers
122!!
123!! First we should define what we mean by a "solver". The TVM problems this module works with are each governed by a pair of
124!! formulas (one for `fv` and one for `pv`) in terms of `n, `i`, `g`, `q`, & `a`. Most of the time we want `pv` & `fv`, so we
125!! just evaluate the formulas. Sometimes we already know pv and fv and want another variable. The "solvers" in this package
126!! provide an interface through which we may ask for any two variables to be found with respect to the others.
127!!
128!! The primary solver functions are:
129!!
130!! - tvm_delayed_lump_sum_solve()
131!! - tvm_delayed_level_annuity_solve()
132!! - tvm_delayed_geometric_annuity_solve()
133!! - tvm_delayed_arithmetic_annuity_solve()
134!!
135!! These functions operate in largely the same manner. The first 5 or 6 arguments are variables in the TVM equations. These
136!! first arguments are all `intent(inout)` arguments -- they hold known values upon entry and hold solved values upon exit.
137!! These arguments are followed by the delay (`d`) argument. For annuities this is followed by the early end argument (`e`). Next
138!! is the `unknown` argument that specifies what variables we wish to solve for. Lastly is a `status` argument used to return
139!! errors.
140!!
141!! delay
142!! | early end
143!! ---- variables ---- | |
144!! tvm_delayed_lump_sum_solve( n, i, pv, fv, a, d, | unknowns, status)
145!! tvm_delayed_level_annuity_solve( n, i, pv, fv, a, d, e, unknowns, status)
146!! tvm_delayed_geometric_annuity_solve( n, i, g, pv, fv, a, d, e, unknowns, status)
147!! tvm_delayed_arithmetic_annuity_solve(n, i, q, pv, fv, a, d, e, unknowns, status)
148!! | |
149!! | Used to return error codes
150!! Specifies unknown variables
151!!
152!! Every one of these solvers works with *two* equations (one for pv and one for fv). As a system of two equations, we can
153!! normally solve for two unknowns. The unknowns we wish to find are specified in the `unknowns` argument using a sum of
154!! variable constants. For example, we would request that PV and FV be found with a sum like this:
155!!
156!! var_pv + var_fv
157!!
158!! For the lump sum solver there are 15 possible combinations of 1 or 2 variables:
159!!
160!! var_n, var_n+var_i, var_n+var_pv, var_n+var_fv, var_n+var_a, var_i, var_i+var_pv, var_i+var_fv,
161!! var_i+var_a, var_pv, var_pv+var_fv, var_pv+var_a, var_fv, var_fv+var_a, var_a
162!!
163!! For the lump sum solver any of the combinations may be solved. For others there are limitations. See the documentation for the
164!! specific solver you are using.
165!!
166!! Note that if we only solve for one variable there is a possibility that the resulting collection of variable values may be
167!! inconsistent with the underlying equations. So if you solve for only one variable, make sure all of the other variables
168!! contain valid values. Each solver subroutine finishes by calling a corresponding consistency checker routine to insure
169!! all the values lead to consistent equations. These routines are exported from the package:
170!!
171!! - tvm_delayed_lump_sum_check()
172!! - tvm_delayed_level_annuity_check()
173!! - tvm_delayed_geometric_annuity_check()
174!! - tvm_delayed_arithmetic_annuity_check()
175!!
176!! Aside from not taking the `unknowns` argument, the consistency checkers follow the same argument pattern as the solvers.
177!!
178!! Solving for n.
179!!
180!! Logically the value for n is the number of periods -- a positive integer. For these routines n is allowed to be any real
181!! value so that we can solve for the variable and produce consistent results. If an integer value is required:
182!! -# First solve for n.
183!! -# Transform n to an integer in an appropriate way (perhaps ceiling for a loan).
184!! -# Using this integer n, now solve for two other variables in the equations (perhaps a & i for a loan).
185!! You will now have a consistent set of equations with an
186!! integer number of periods with the two variables solved for in the last step being adjusted.
187!!
188!! @par Error handling
189!!
190!! This module is intended to be used as a subsystem in larger software packages. As such it never STOPs -- it simply reports
191!! error conditions back to the caller. Subroutines in this module report errors via an integer parameter named "status". If
192!! this parameter is zero after a call, then no error conditions were encountered. The error codes are allocated in blocks to
193!! each subroutine so that no subroutines share common error codes. This is intended to assist the caller in ascertaining where
194!! the problem occurred. In the code each error code is documented with a comment where the status value is set. i.e. search for
195!! the error code, and you will find the description.
196!!
197!! @par Other Approaches & References
198!!
199!! For experience more like a financial calculator, see the module tvm12.
200!!
201!! For cashflow problems, see the module cashflows. Note it is easy to model the kinds of cashflows in this module with the
202!! cashflows module -- for things like amortization tables.
203!!
205 use, intrinsic :: ieee_arithmetic, only: ieee_is_finite, ieee_is_nan
206 use mrffl_config, only: rk=>mrfflrk, ik=>mrfflik, zero_epsilon
211 implicit none
212 private
213
214 real(kind=rk), parameter :: consistent_epsilon = 1.0e-3_rk !< Used to check equation consistency
215
216 ! Primary TVM equation solvers
218
219 ! Primary TVM equation consistency checkers
221
222 ! Helper solvers for common special cases of the primary TVM solvers above
223 public :: tvm_lump_sum_solve
224
225 ! Related computational routines
227
228contains
229
230 !----------------------------------------------------------------------------------------------------------------------------
231 !> compute future value from present value (pv), number of periods (n), and an intrest rate (i).
232 real(kind=rk) pure elemental function fv_from_pv_n_i(pv, n, i)
233 integer(kind=ik), intent(in) :: n
234 real(kind=rk), intent(in) :: pv, i
235 fv_from_pv_n_i = pv * (1+p2f(i)) ** n
236 end function fv_from_pv_n_i
237
238 !----------------------------------------------------------------------------------------------------------------------------
239 !> Sum the payments from a geometric annuity.
240 !! Ex: Sum of inflation adjusted payments.
241 !!
242 !! The payment sequence is the same no matter when it starts. The first non-zero payment is @f$A@f$, and the
243 !! last non-zero payment is @f$ A(1+g)^{n-1}@f$:
244 !!
245 !! @f[ \begin{array}{ll}
246 !! V_1 & = A \\
247 !! V_2 & = A(1+g) \\
248 !! & ... \\
249 !! V_m & = A(1+g)^{m-1} \\
250 !! & ... \\
251 !! V_n & = A(1+g)^{n-1} \\
252 !! \end{array} @f]
253 !!
254 !! Formulas
255 !! @f[ \begin{array}{ll}
256 !! P_{sum} & = P\cdot\left(\frac{(i+1)^{n}-1}{i}\right) \\
257 !! \end{array} @f]
258 !!
259 !! @param n Number of compounding periods (WARNING: Unlike elsewhere in this module, this is an INTEGER not a REAL)
260 !! @param g Payment growth rate as a percentage.
261 !! @param a First payment (Annuity)
262 !!
263 real(kind=rk) pure function tvm_geometric_annuity_sum_a(n, g, a)
264 integer(kind=ik), intent(in) :: n
265 real(kind=rk), intent(in) :: g, a
266 real(kind=rk) :: gq
267 if (n < 1) then
269 else
270 gq = p2f(g)
271 tvm_geometric_annuity_sum_a = a * ((1+gq)**n-1) / gq
272 ! tvm_geometric_annuity_sum_a = 0
273 ! do j=0,n-1
274 ! tvm_geometric_annuity_sum_a = tvm_geometric_annuity_sum_a + a * (1+gq)**j
275 ! end do
276 end if
277 end function tvm_geometric_annuity_sum_a
278
279 !----------------------------------------------------------------------------------------------------------------------------
280 !> Return the number of payments given the period count (n), delay (d), and early end (e).
281 !!
282 !! Example:
283 !! d 0 1 2 3 4 5 6 7 8 9
284 !! period 0 1 2 3 4 5 6 7 8 9
285 !! e 9 8 7 6 5 4 3 2 1 0
286 !! | | |
287 !! d=1 -+ e=2 -+ +- n=9 -> num_payments = 1+n-e-d = 7
288 !!
289 !! @param n Number of periods.
290 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
291 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
292 !!
293 integer(kind=ik) pure function tvm_delayed_annuity_num_payments(n, d, e)
294 integer(kind=ik), intent(in) :: n, d, e
295 tvm_delayed_annuity_num_payments = 1_ik + n - e - d
297
298 !----------------------------------------------------------------------------------------------------------------------------
299 !> Solve for TVM parameters for a lump sum.
300 !!
301 !! This is a simple wrapper that calls See tvm_delayed_lump_sum_solve().
302 !!
303 !! @param n Number of compounding periods
304 !! @param i Annual growth rate as a percentage.
305 !! @param pv Present Value
306 !! @param fv Future Value
307 !! @param unknowns What variable to solve for.
308 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1193-1224.
309 !!
310 subroutine tvm_lump_sum_solve(n, i, pv, fv, unknowns, status)
311 implicit none
312 real(kind=rk), intent(inout) :: n, i, pv, fv
313 integer(kind=ik), intent(in) :: unknowns
314 integer(kind=ik), intent(out) :: status
315 integer(kind=ik), parameter :: allowed_vars = var_n + var_i + var_pv + var_fv
316 real(kind=rk) :: a
317 if (bitset_not_subsetp(unknowns, allowed_vars)) then
318 status = 1193 ! "ERROR(tvm_lump_sum_solve): Unknown unknowns!"
319 return
320 end if
321 if (bitset_size(unknowns) > 1) then
322 status = 1194 ! "ERROR(tvm_lump_sum_solve): Too many unknowns!"
323 return
324 end if
325 a = pv
326 if (bitset_subsetp(var_pv, unknowns)) then
327 call tvm_delayed_lump_sum_solve(n, i, pv, fv, a, 0_ik, var_pv+var_a, status)
328 else
329 call tvm_delayed_lump_sum_solve(n, i, pv, fv, a, 0_ik, unknowns, status)
330 end if
331 end subroutine tvm_lump_sum_solve
332
333 !----------------------------------------------------------------------------------------------------------------------------
334 !> Solve for TVM parameters for a generalized lump sum.
335 !!
336 !! @f[ \begin{array}{cccc}
337 !! t & V_t & \mathrm{pv}_t & \mathrm{fv}_t \\
338 !! 0 & 0 & 0 & 0 \\
339 !! ... & ... & ... & ... \\
340 !! {d-1} & 0 & 0 & 0 \\
341 !! d & a & \frac{a}{(1+i)^{d}} & a(1+i)^{n-d} \\
342 !! {d+1} & 0 & 0 & 0 \\
343 !! ... & ... & ... & ... \\
344 !! n & 0 & 0 & 0 \\
345 !! \end{array} @f]
346 !!
347 !! The equations this solver uses are as follows:
348 !! @f[ \mathrm{fv} = a \left(1+i \right)^{n -d} @f]
349 !! @f[ \mathrm{pv} = a \left(1+i \right)^{-d} @f]
350 !!
351 !! Normally TVM software assumes a lump sum cashflow occurs at the beginning of the first period. This solver allows the
352 !! cashflow to occur at the beginning or end of any period.
353 !!
354 !! This solver can find any combination of 1 or 2 of the following variables: n, i, pv, fv, & a.
355 !!
356 !! @param n Number of compounding periods
357 !! @param i Annual growth rate as a percentage.
358 !! @param pv Present Value
359 !! @param fv Future Value
360 !! @param a The size of the cashflow
361 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
362 !! @param unknowns What variables to solve for.
363 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1161-1192.
364 !!
365 subroutine tvm_delayed_lump_sum_solve(n, i, pv, fv, a, d, unknowns, status)
366 real(kind=rk), intent(inout) :: n, i, pv, fv, a
367 integer(kind=ik), intent(in) :: d, unknowns
368 integer(kind=ik), intent(out) :: status
369 integer(kind=ik), parameter :: allowed_vars = var_n + var_i + var_pv + var_fv + var_a
370 integer :: num_unknowns
371 real(kind=rk) :: iq
372 num_unknowns = bitset_size(unknowns)
373 if (num_unknowns > 2) then
374 status = 1161 ! "ERROR(tvm_delayed_lump_sum_solve): Too many unknowns!"
375 else if (bitset_not_subsetp(unknowns, allowed_vars)) then
376 status = 1162 ! "ERROR(tvm_delayed_lump_sum_solve): Unknown unknowns!"
377 else
378 if (num_unknowns > 0) then
379 if (bitset_subsetp(var_a, unknowns)) then ! a is unknown
380 if (bitset_subsetp(var_i, unknowns)) then ! a & i are unknown
381 i = f2p((pv / fv) ** (-1 / n) - 1)
382 end if
383 iq = p2f(i)
384 if (bitset_subsetp(var_pv, unknowns)) then ! a & pv are unknown
385 pv = fv / (1 + iq) ** n
386 else if (bitset_subsetp(var_fv, unknowns)) then ! a & fv are unknown
387 fv = pv * (1 + iq) ** n
388 else if (bitset_subsetp(var_n, unknowns)) then ! a & n are unknown
389 n = -log(pv / fv) / log(1 + iq)
390 end if
391 a = fv * (1 + iq) ** (d - n)
392 else if (bitset_subsetp(var_n, unknowns)) then ! a is known. n is unknown
393 if (bitset_subsetp(var_i, unknowns)) then ! a is known. n & i are unknown
394 if (abs(d) < zero_epsilon) then
395 status = 1163 ! "ERROR(tvm_delayed_lump_sum_solve): Can not solve for i with n unknown and d=0!"
396 return
397 end if
398 i = f2p((pv / a) ** (-1.0_rk / d) - 1)
399 end if
400 iq = p2f(i)
401 if (bitset_subsetp(var_pv, unknowns)) then ! a is known. n & pv are unknown
402 pv = a * (1 + iq) ** (-d)
403 else if (bitset_subsetp(var_fv, unknowns)) then ! a is known. n & pv are unknown
404 status = 1164 ! "ERROR(tvm_delayed_lump_sum_solve): Can not solve for fv with n unknown and d/=n!"
405 return
406 end if
407 n = (d * log(1 + iq) + log(fv / a)) / log(1 + iq)
408 else if (bitset_subsetp(var_i, unknowns)) then ! a & n are known. i is unknown.
409 if (bitset_subsetp(var_pv, unknowns)) then ! a, n, & fv are known. i & pv are unknown
410 if (abs(d-n) < zero_epsilon) then
411 status = 1165 ! "ERROR(tvm_delayed_lump_sum_solve): Can not solve for pv with i unknown and d=n!"
412 return
413 else
414 pv = a * ((fv / a) ** (-1 / (d - n))) ** (-d)
415 end if
416 else if (bitset_subsetp(var_fv, unknowns)) then ! a, n, & pv are known. i & fv are unknown
417 if (abs(d) < zero_epsilon) then
418 status = 1166 ! "ERROR(tvm_delayed_lump_sum_solve): Can not solve for fv with i unknown and d=0!"
419 return
420 else
421 if (abs(d-n) < zero_epsilon) then
422 fv = a
423 else
424 fv = (pv / a) ** ((d - n) / d) * a
425 end if
426 end if
427 end if
428 if (abs(d-n) < zero_epsilon) then
429 i = f2p(exp(-log(pv / a) / n) - 1)
430 else
431 i = f2p(exp(-log(fv / a) / (d - n)) - 1)
432 end if
433 else
434 iq = p2f(i)
435 if (bitset_subsetp(var_fv, unknowns)) then ! a, i, & n are known. fv is unknown. pv may be unknown.
436 fv = a * (1 + iq) ** (-d + n)
437 end if
438 if (bitset_subsetp(var_pv, unknowns)) then ! a, i, & n are known. pv is unknown. fv may be unknown.
439 pv = a * (1 + iq) ** (-d)
440 end if
441 end if
442 end if
443 call tvm_delayed_lump_sum_check(n, i, pv, fv, a, d, status) ! Return status set
444 end if
445 end subroutine tvm_delayed_lump_sum_solve
446
447 !----------------------------------------------------------------------------------------------------------------------------
448 !> Check the TVM parameters for a generalized lump sum.
449 !!
450 !! See tvm_delayed_lump_sum_solve() for more information regarding generalized lump sums.
451 !!
452 !! @param n Number of compounding periods
453 !! @param i Annual growth rate as a percentage.
454 !! @param pv Present Value
455 !! @param fv Future Value
456 !! @param a The size of the cashflow
457 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
458 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1129-1160.
459 !!
460 subroutine tvm_delayed_lump_sum_check(n, i, pv, fv, a, d, status)
461 real(kind=rk), intent(in) :: n, i, pv, fv, a
462 integer(kind=ik), intent(in) :: d
463 integer(kind=ik), intent(out) :: status
464 real(kind=rk) :: iq
465 if (d < 0) then
466 status = 1129 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (d<0)!"
467 else if (.not. ieee_is_finite(n)) then
468 status = 1130 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (n is infinite)!"
469 else if (ieee_is_nan(n)) then
470 status = 1131 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (n is NaN)!"
471 else if (d > nint(n, ik)) then
472 status = 1132 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (d>n)!"
473 else if (n < zero_epsilon) then
474 status = 1133 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (n<0)!"
475 else if (n < 1) then
476 status = 1134 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (n<1)!"
477 else if (.not. ieee_is_finite(i)) then
478 status = 1135 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (i is infinite)!"
479 else if (ieee_is_nan(i)) then
480 status = 1136 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (i is NaN)!"
481 else if (abs(i) < zero_epsilon) then
482 status = 1137 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (i is 0%)!"
483 else if (abs(100+i) < zero_epsilon) then
484 status = 1138 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (i is -100%)!"
485 else if (.not. ieee_is_finite(pv)) then
486 status = 1139 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (pv is infinite)!"
487 else if (ieee_is_nan(pv)) then
488 status = 1140 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (pv is NaN)!"
489 else if (.not. ieee_is_finite(fv)) then
490 status = 1141 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (fv is infinite)!"
491 else if (ieee_is_nan(fv)) then
492 status = 1142 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (fv is NaN)!"
493 else if (.not. ieee_is_finite(a)) then
494 status = 1143 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (a is infinite)!"
495 else if (ieee_is_nan(a)) then
496 status = 1144 ! "ERROR(tvm_delayed_lump_sum_check): Parameters inconsistent (a is NaN)!"
497 else
498 iq = p2f(i)
499 if (abs(a * (1 + iq) ** (n - d) - fv) > consistent_epsilon) then
500 status = 1145 ! "ERROR(tvm_delayed_lump_sum_check): Inconsistent parameters (fv equation)!"
501 else if (abs(a / (1 + iq) ** d - pv) > consistent_epsilon) then
502 status = 1146 ! "ERROR(tvm_delayed_lump_sum_check): Inconsistent parameters (pv equation)!"
503 else
504 status = 0
505 end if
506 end if
507 end subroutine tvm_delayed_lump_sum_check
508
509 !----------------------------------------------------------------------------------------------------------------------------
510 !> Solve for TVM parameters for a level annuity.
511 !!
512 !! @f[ \begin{array}{cccc}
513 !! t & V_t & \mathrm{pv}_t & \mathrm{fv}_t \\
514 !! 0 & 0 & 0 & 0 \\
515 !! ... & ... & ... & ... \\
516 !! {d-1} & 0 & 0 & 0 \\
517 !! d & a & \frac{a}{(1+i)^{d}} & a(1+i)^{n-d} \\
518 !! {d+1} & a & \frac{a}{(1+i)^{d+1}} & a(1+i)^{n-d-1} \\
519 !! ... & ... & ... & ... \\
520 !! {t} & a & \frac{a}{(1+i)^{t}} & a(1+i)^{n-t} \\
521 !! ... & ... & ... & ... \\
522 !! {n-e-1} & a & \frac{a}{(1+i)^{n-e-1}} & a(1+i)^{e+1} \\
523 !! {n-e} & a & \frac{a}{(1+i)^{n-e}} & a(1+i)^{e} \\
524 !! {n-e+1} & 0 & 0 & 0 \\
525 !! ... & ... & ... & ... \\
526 !! n & 0 & 0 & 0 \\
527 !! \end{array} @f]
528 !!
529 !! Total pv & fv are given by:
530 !! @f[ \mathrm{fv} = \sum_{t=d}^{n-e} a(1+i)^{n-t} @f]
531 !! @f[ \mathrm{pv} = \sum_{t=d}^{n-e} \frac{a}{(1+i)^{t}} @f]
532 !!
533 !! The equations this solver uses are as follows:
534 !! @f[ \mathrm{fv} = a\frac{\left(1+i \right)^{n +1-d}-\left(1+i \right)^{d} }{i} @f]
535 !! @f[ \mathrm{pv} = a\frac{\left(1+i \right)^{1-d} -\left(1+i \right)^{-n+d}}{i} @f]
536 !!
537 !! This routine is capable of searching for any combination of 1 or 2 of the following variables: n, i, pv, fv, & a
538 !!
539 !! @par Solving for i
540 !! When possible a closed for solution is used. In some cases (var_i+var_fv, var_i+var_pv, var_i+var_n when d or e is greater
541 !! than 1) bisection is used to numerically solve for i. When bisection is used, this routine will search for values for i
542 !! that are within the following intervals (in the order listed):
543 !! [zero_epsilon, 99999], [-100+zero_epsilon, -zero_epsilon], and [-99999, -100-zero_epsilon]
544 !! In other words, it can find values of i that are between -99999 and 99999 and at least zero_epsilon away from 0% and 100%.
545 !! If the value for i is outside these ranges, then no solution will be found. It is also possible that even if a solution
546 !! exists inside these ranges that the search algorithm may fail. If solutions exist in more than one range, then the first
547 !! one found is used.
548 !!
549 !! @warning Solutions are unreliable when i is less than -100%.
550 !!
551 !! @param n Number of compounding periods
552 !! @param i Annual growth rate as a percentage. When solving for this value only POSITIVE values are found.
553 !! @param pv Present Value
554 !! @param fv Future Value
555 !! @param a Payment (Annuity)
556 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
557 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
558 !! @param unknowns What variable(s) to solve for. Should be a sum of var_? constants.
559 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1097-1128.
560 !!
561 subroutine tvm_delayed_level_annuity_solve(n, i, pv, fv, a, d, e, unknowns, status)
562 real(kind=rk), intent(inout) :: n, i, pv, fv, a
563 integer(kind=ik), intent(in) :: d, e
564 integer(kind=ik), intent(in) :: unknowns
565 integer(kind=ik), intent(out) :: status
566 integer(kind=ik), parameter :: allowed_vars = var_n + var_i + var_pv + var_fv + var_a
567 integer :: num_unknowns
568 real(kind=rk) :: islvivl0(3), islvivl1(3), iq
569 num_unknowns = bitset_size(unknowns)
570 if (num_unknowns > 2) then
571 status = 1097 ! "ERROR(tvm_delayed_level_annuity_solve): Too many unknowns!"
572 else if (bitset_not_subsetp(unknowns, allowed_vars)) then
573 status = 1098 ! "ERROR(tvm_delayed_level_annuity_solve): Unknown unknowns!"
574 else
575 if (num_unknowns > 0) then
576 islvivl0 = [ 0.0_rk+zero_epsilon, -100.0_rk+zero_epsilon, -99999.0_rk]
577 islvivl1 = [ 99999.0_rk, 0.0_rk-zero_epsilon, -100.0_rk-zero_epsilon]
578 if (bitset_subsetp(var_a, unknowns)) then ! a is unknown
579 if (bitset_subsetp(var_i, unknowns)) then ! a & i are unknown
580 i = f2p((fv / pv) ** (1 / n) - 1)
581 end if
582 iq = p2f(i)
583 if (bitset_subsetp(var_pv, unknowns)) then ! a & pv are unknown
584 pv = (1 + iq) ** (-n) * fv
585 else if (bitset_subsetp(var_fv, unknowns)) then ! a & fv are unknown
586 fv = pv * (1 + iq) ** n
587 else if (bitset_subsetp(var_n, unknowns)) then ! a & n are unknown
588 n = -log(pv / fv) / log(1 + iq)
589 end if
590 a = -fv * iq / ((1 + iq) ** e - (1 + iq) ** (1 + n - d))
591 else if (bitset_subsetp(var_n, unknowns)) then ! a is known. n is unknown
592 if (bitset_subsetp(var_i, unknowns)) then ! a is known. n & i are unknown
593 if ((d==0) .and. (e==0)) then
594 i = f2p(-(fv - pv) * a / (a - pv) / fv)
595 else if ((d==0) .and. (e==1)) then
596 i = f2p(-(fv - pv) * a / (a * fv - a * pv - fv * pv))
597 else if ((d==1) .and. (e==0)) then
598 i = f2p(a * (fv - pv) / fv / pv)
599 else if ((d==1) .and. (e==1)) then
600 i = f2p((fv - pv) * a / pv / (a + fv))
601 else
602 call multi_bisection(i, islvivl0, islvivl1, sf_i_no_n, 1.0e-5_rk, 1.0e-5_rk, 1000_ik, status, .false.)
603 if (status /= 0) then
604 status = 1099 ! "ERROR(tvm_delayed_level_annuity_solve): i solver failed for unknown n case!"
605 return
606 end if
607 end if
608 end if
609 iq = p2f(i)
610 if (bitset_subsetp(var_pv, unknowns)) then ! a is known. n & pv are unknown
611 pv = (1 + iq) ** (1 - d) * a * fv / (a * (1 + iq) ** e + fv * iq)
612 else if (bitset_subsetp(var_fv, unknowns)) then ! a is known. n & pv are unknown
613 fv = (1 + iq) ** e * a * pv / ((1 + iq) ** (1.0_rk - d) * a - iq * pv)
614 end if
615 n = (log(1 + iq) * d - log(1 + iq) + log((a * exp(log(1 + iq) * e) + fv * iq) / a)) / log(1 + iq)
616 else
617 if (bitset_subsetp(var_i, unknowns)) then ! a & n are known. i is unknown
618 if (bitset_subsetp(var_fv, unknowns)) then ! a, n, & pv are known. i & fv are unknown
619 call multi_bisection(i, islvivl0, islvivl1, sf_i_no_fv, 1.0e-7_rk, 1.0e-7_rk, 1000_ik, status, .false.)
620 if (status /= 0) then
621 status = 1100 ! "ERROR(tvm_delayed_level_annuity_solve): i solver failed for unknown fv case!"
622 return
623 end if
624 else if (bitset_subsetp(var_pv, unknowns)) then ! a, n, & fv are known. i & pv are be unknown.
625 call multi_bisection(i, islvivl0, islvivl1, sf_i_no_pv, 1.0e-7_rk, 1.0e-7_rk, 1000_ik, status, .false.)
626 if (status /= 0) then
627 status = 1101 ! "ERROR(tvm_delayed_level_annuity_solve): i solver failed for unknown pv case!"
628 return
629 end if
630 else ! a, n, pv, & fv are known. i is unknown.
631 i = f2p((fv / pv) ** (1 / n) - 1)
632 end if
633 end if
634 iq = p2f(i)
635 if (bitset_subsetp(var_fv, unknowns)) then ! a, i, & n are known. fv is unknown
636 fv = 1 / iq * (-(1 + iq) ** e + (1 + iq) ** (1 + n - d)) * a
637 end if
638 if (bitset_subsetp(var_pv, unknowns)) then ! a, i, & n are known. pv is unknown
639 pv = (-(1 / (1 + iq)) ** (n - e + 1) * (1 + iq) / iq + (1 / (1 + iq)) ** d * (1 + iq) / iq) * a
640 end if
641 end if
642 end if
643 call tvm_delayed_level_annuity_check(n, i, pv, fv, a, d, e, status) ! Return status set
644 end if
645 contains
646 real(kind=rk) function sf_i_no_n(i)
647 implicit none
648 real(kind=rk), intent(in) :: i
649 real(kind=rk) :: iq
650 iq = p2f(i)
651 sf_i_no_n = (a * (1 + iq) ** e + fv * iq) * pv - (1 + iq) ** (1 - d) * a * fv
652 end function sf_i_no_n
653 real(kind=rk) function sf_i_no_fv(i)
654 implicit none
655 real(kind=rk), intent(in) :: i
656 real(kind=rk) :: iq
657 iq = p2f(i)
658 sf_i_no_fv = (-(1 / (1 + iq)) ** (n - e + 1) * (1 + iq) / iq + (1 / (1 + iq)) ** d * (1 + iq) / iq) * a - pv
659 end function sf_i_no_fv
660 real(kind=rk) function sf_i_no_pv(i)
661 implicit none
662 real(kind=rk), intent(in) :: i
663 real(kind=rk) :: iq
664 iq = p2f(i)
665 sf_i_no_pv = 1 / iq * (-(1 + iq) ** e + (1 + iq) ** (1 + n - d)) * a - fv
666 end function sf_i_no_pv
668
669 !----------------------------------------------------------------------------------------------------------------------------
670 !> Check TVM parameters for a level annuity.
671 !! @param n Number of compounding periods
672 !! @param i Annual growth rate as a percentage. When solving for this value only POSITIVE values are found.
673 !! @param pv Present Value
674 !! @param fv Future Value
675 !! @param a Payment (Annuity)
676 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
677 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
678 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1065-1096.
679 !!
680 subroutine tvm_delayed_level_annuity_check(n, i, pv, fv, a, d, e, status)
681 real(kind=rk), intent(in) :: n, i, pv, fv, a
682 integer(kind=ik), intent(in) :: d, e
683 integer(kind=ik), intent(out) :: status
684 real(kind=rk) :: iq
685 if (d < 0) then
686 status = 1065 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (d<0)!"
687 else if (e < 0) then
688 status = 1066 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (e<0)!"
689 else if (.not. ieee_is_finite(n)) then
690 status = 1067 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (n is infinite)!"
691 else if (ieee_is_nan(n)) then
692 status = 1068 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (n is NaN)!"
693 else if (d > nint(n, ik)) then
694 status = 1069 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (d>n)!"
695 else if (e > nint(n, ik)) then
696 status = 1070 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (e>n)!"
697 else if ((d+e) > nint(n, ik)) then
698 status = 1071 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (d+e>n)!"
699 else if (n < zero_epsilon) then
700 status = 1072 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (n<0)!"
701 else if (n < 1) then
702 status = 1073 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (n<1)!"
703 else if (.not. ieee_is_finite(i)) then
704 status = 1074 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (i is infinite)!"
705 else if (ieee_is_nan(i)) then
706 status = 1075 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (i is NaN)!"
707 else if (abs(i) < zero_epsilon) then
708 status = 1076 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (i is 0%)!"
709 else if (abs(100+i) < zero_epsilon) then
710 status = 1077 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (i is -100%)!"
711 else if (.not. ieee_is_finite(pv)) then
712 status = 1078 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (pv is infinite)!"
713 else if (ieee_is_nan(pv)) then
714 status = 1079 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (pv is NaN)!"
715 else if (.not. ieee_is_finite(fv)) then
716 status = 1080 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (fv is infinite)!"
717 else if (ieee_is_nan(fv)) then
718 status = 1081 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (fv is NaN)!"
719 else if (.not. ieee_is_finite(a)) then
720 status = 1082 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (a is infinite)!"
721 else if (ieee_is_nan(a)) then
722 status = 1083 ! "ERROR(tvm_delayed_level_annuity_check): Parameters inconsistent (a is NaN)!"
723 else
724 iq = p2f(i)
725 if (abs(1 / iq * (-(1 + iq) ** e + (1 + iq) ** (1 + n - d)) * a - fv) > consistent_epsilon) then
726 status = 1084 ! "ERROR(tvm_delayed_level_annuity_solve): Inconsistent parameters (fv equation)!"
727 else if (abs((-(1 / (1 + iq)) ** (n - e + 1) * (1 + iq) / iq + (1 / (1 + iq)) ** d * (1 + iq) / iq) * a - pv) > consistent_epsilon) then
728 status = 1085 ! "ERROR(tvm_delayed_level_annuity_solve): Inconsistent parameters (pv equation)!"
729 else
730 status = 0
731 end if
732 end if
734
735 !----------------------------------------------------------------------------------------------------------------------------
736 !> Solve for TVM parameters for a geometric annuity certain.
737 !!
738 !! @f[ \begin{array}{cccc}
739 !! t & V_t & \mathrm{pv}_t & \mathrm{fv}_t \\
740 !! 0 & 0 & 0 & 0 \\
741 !! ... & ... & ... & ... \\
742 !! {d-1} & 0 & 0 & 0 \\
743 !! d & a(1+g)^{0} & a\frac{(1+g)^{0}}{(1+i)^{d}} & a(1+g)^{0}(1+i)^{n-d} \\
744 !! {d+1} & a(1+g)^{1} & a\frac{(1+g)^{1}}{(1+i)^{d+1}} & a(1+g)^{1}(1+i)^{n-d-1} \\
745 !! ... & ... & ... & ... \\
746 !! {t} & a(1+g)^{t-d} & a\frac{(1+g)^{t-d}}{(1+i)^{t}} & a(1+g)^{t-d}(1+i)^{n-t} \\
747 !! ... & ... & ... & ... \\
748 !! {n-e-1} & a(1+g)^{n-e-1-d} & a\frac{(1+g)^{n-e-1-d}}{(1+i)^{n-e-1}} & a(1+g)^{n-e-d-1}(1+i)^{e+1} \\
749 !! {n-e} & a(1+g)^{n-e-d} & a\frac{(1+g)^{n-e-d}}{(1+i)^{n-e}} & a(1+g)^{n-e-d}(1+i)^{e} \\
750 !! {n-e+1} & 0 & 0 & 0 \\
751 !! ... & ... & ... & ... \\
752 !! n & 0 & 0 & 0 \\
753 !! \end{array} @f]
754 !!
755 !! Total pv & fv are given by:
756 !! @f[ \mathrm{fv} = \sum_{t=d}^{n-e} a(1+g)^{t-d}(1+i)^{n-t} @f]
757 !! @f[ \mathrm{pv} = \sum_{t=d}^{n-e} a\frac{(1+g)^{t-d}}{(1+i)^{t}} @f]
758 !!
759 !! When @f$i\ne g@f$, the total pv & fv are given by:
760 !! @f[ \mathrm{fv} = a \frac{\left(1+g \right)^{1+n-e-d} \left(1+i \right)^{e}-\left(1+i\right)^{1+n-d}}{g -i} @f]
761 !! @f[ \mathrm{pv} = a \frac{\left(1+g \right) \left(\frac{1+g}{1+i}\right)^{n -e}-\left(\frac{1+g}{1+i}\right)^{d} \left(1+i \right)}{(g-i)(1+g)^{-d}} @f]
762 !!
763 !! When @f$i=g@f$, the total pv & fv are given by:
764 !! @f[ \mathrm{fv} = a \left(1+n-e-d\right) \left(1+i\right)^{n-d} @f]
765 !! @f[ \mathrm{pv} = a \left(1+n-e-d\right) \left(1+i\right)^{-d} @f]
766 !!
767 !! This routine can solve for `var_n`, `var_n+var_fv`, and any combination of two or fewer of the following: `var_a`, `var_fv`, `var_pv`
768 !!
769 !! @param n Number of compounding periods
770 !! @param i Discount rate as a percentage.
771 !! @param g Payment growth rate as a percentage.
772 !! @param pv Present Value
773 !! @param fv Future Value
774 !! @param a First payment (Annuity)
775 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
776 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
777 !! @param unknowns What variables to solve for.
778 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1033-1064.
779 !!
780 subroutine tvm_delayed_geometric_annuity_solve(n, i, g, pv, fv, a, d, e, unknowns, status)
781 real(kind=rk), intent(inout) :: n, i, g, pv, fv, a
782 integer(kind=ik), intent(in) :: d, e
783 integer(kind=ik), intent(in) :: unknowns
784 integer(kind=ik), intent(out) :: status
785 integer(kind=ik), parameter :: allowed_vars = var_n + var_pv + var_fv + var_a
786 integer :: num_unknowns
787 real(kind=rk) :: iq, gq, iq1, gq1, giq
788 num_unknowns = bitset_size(unknowns)
789 if (num_unknowns > 2) then
790 status = 1033 ! "ERROR(tvm_delayed_geometric_annuity_solve): Too many unknowns!"
791 else if (bitset_not_subsetp(unknowns, allowed_vars)) then
792 status = 1034 ! "ERROR(tvm_delayed_geometric_annuity_solve): Unknown unknowns!"
793 else
794 iq = p2f(i)
795 gq = p2f(g)
796 iq1 = 1+iq
797 gq1 = 1+gq
798 giq = gq-iq
799 if (num_unknowns > 0) then
800 if (abs(i-g) < zero_epsilon) then ! i=g case
801 if (bitset_subsetp(var_a, unknowns)) then ! a is unknown
802 if (bitset_subsetp(var_pv, unknowns)) then ! a & pv are unknown
803 pv = fv * iq1 ** (-n)
804 else if (bitset_subsetp(var_fv, unknowns)) then ! a & fv are unknown
805 fv = pv * iq1 ** n
806 else if (bitset_subsetp(var_n, unknowns)) then ! a & gq are unknown
807 status = 1035 ! "ERROR(tvm_delayed_geometric_annuity_solve): Unsupported value for unknown (var_a+var_n)!"
808 end if
809 a = -fv * iq1 ** (d - n) / (d - n + e - 1)
810 else if (bitset_subsetp(var_n, unknowns)) then ! n is unknown
811 if (bitset_subsetp(var_fv, unknowns)) then ! n & fv are unknown
812 fv = pv * iq1 ** ((iq1 ** d * pv + a * (-1 + d + e)) / a)
813 ! else if (bitset_subsetp(var_i, unknowns)) then ! n & i are unknown
814 ! ! MJR TODO NOTE <2024-12-19T15:50:59-0600> tvm_delayed_geometric_annuity_solve: Add iterative solver for i here.
815 else if (num_unknowns > 1) then ! n & something otherthan fv are unknown
816 status = 1036 ! "ERROR(tvm_delayed_geometric_annuity_solve): Unsupported value for unknown (only var_fv supported with var_n)!"
817 end if
818 n = ((iq1) ** (-d) * a * d + iq1 ** (-d) * a * e - a * iq1 ** (-d) + pv) / iq1 ** (-d) / a
819 else ! pv and/or fv might be unknown
820 if (bitset_subsetp(var_fv, unknowns)) then ! fv unknown. pv might be unknown
821 fv = a * iq1 ** (-d + n) * (-d + n - e + 1)
822 end if
823 if (bitset_subsetp(var_pv, unknowns)) then ! pv unknown. fv might be unknown
824 pv = (-d + n - e + 1) * iq1 ** (-d) * a
825 end if
826 end if
827 else ! i!=g case
828 if (bitset_subsetp(var_a, unknowns)) then ! a is unknown
829 if (bitset_subsetp(var_pv, unknowns)) then ! a & pv are unknown
830 pv = fv * iq1 ** d * gq1 ** e * (-(gq1) ** (n - e + 1) * iq1 ** (-n + e) + iq1 ** (1 - d) * gq1 ** d) / (iq1 ** n * iq1 * gq1 ** (d + e) - gq1 ** n * iq1 ** (d + e) * gq1)
831 else if (bitset_subsetp(var_fv, unknowns)) then ! a & fv are unknown
832 fv = (-(gq1) ** n * gq1 * iq1 ** (d + e + n) + gq1 ** (d + e) * iq1 ** (2 * n) * iq1) * pv / (iq1 ** n * iq1 * gq1 ** (d + e) - gq1 ** n * iq1 ** (d + e) * gq1)
833 else if (bitset_subsetp(var_n, unknowns)) then ! a & gq are unknown
834 status = 1037 ! "ERROR(tvm_delayed_geometric_annuity_solve): Unsupported value for unknown (var_a+var_n)!"
835 end if
836 a = fv * giq / (gq1 ** (-d + n - e + 1) * iq1 ** e - iq1 ** (1 + n - d))
837 else if (bitset_subsetp(var_n, unknowns)) then ! n is unknown
838 if (bitset_subsetp(var_fv, unknowns)) then ! n & fv are unknown
839 fv = 1 / giq * (gq1 ** (1 / (log(gq1) - log(iq1)) * (d * log(iq1) + log((a * iq1 * iq1 ** (-d) + giq * pv) / a) - log(iq1))) * iq1 ** e - iq1 ** ((log((a * iq1 * iq1 ** (-d) + giq * pv) / a) + (d - e - 1) * log(iq1) + e * log(gq1)) / (log(gq1) - log(iq1)))) * a
840 else if (num_unknowns > 1) then ! n & something otherthan fv are unknown
841 status = 1038 ! "ERROR(tvm_delayed_geometric_annuity_solve): Unsupported value for unknown (only var_fv supported with var_n)!"
842 end if
843 n = ((log(gq1) - log(iq1)) * e + (d - 1) * log(gq1) + log(1 / a * (a * iq * iq1 ** (-d) + a * iq1 ** (-d) + pv * gq - pv * iq))) / (log(gq1) - log(iq1))
844 else ! pv and/or fv might be unknown
845 if (bitset_subsetp(var_fv, unknowns)) then ! fv unknown. pv might be unknown
846 fv = 1 / giq * (gq1 ** (-d + n - e + 1) * iq1 ** e - iq1 ** (1 + n - d)) * a
847 end if
848 if (bitset_subsetp(var_pv, unknowns)) then ! pv unknown. fv might be unknown
849 pv = (gq1 * (gq1 / iq1) ** (n - e) - (gq1 / iq1) ** d * iq1) * gq1 ** (-d) * a / giq
850 end if
851 end if
852 end if
853 end if
854 call tvm_delayed_geometric_annuity_check(n, i, g, pv, fv, a, d, e, status)
855 end if
857
858 !----------------------------------------------------------------------------------------------------------------------------
859 !> Check TVM parameters for a geometric annuity certain.
860 !!
861 !! @param n Number of compounding periods
862 !! @param i Discount rate as a percentage.
863 !! @param g Payment growth rate as a percentage.
864 !! @param pv Present Value
865 !! @param fv Future Value
866 !! @param a First payment (Annuity)
867 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
868 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
869 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 1001-1032.
870 !!
871 subroutine tvm_delayed_geometric_annuity_check(n, i, g, pv, fv, a, d, e, status)
872 real(kind=rk), intent(in) :: n, i, g, pv, fv, a
873 integer(kind=ik), intent(in) :: d, e
874 integer(kind=ik), intent(out) :: status
875 real(kind=rk) :: iq, gq
876 if (d < 0) then
877 status = 1001 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (d<0)!"
878 else if (e < 0) then
879 status = 1002 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (e<0)!"
880 else if (.not. ieee_is_finite(n)) then
881 status = 1003 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (n is infinite)!"
882 else if (ieee_is_nan(n)) then
883 status = 1004 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (n is NaN)!"
884 else if (d > nint(n, ik)) then
885 status = 1005 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (d>n)!"
886 else if (e > nint(n, ik)) then
887 status = 1006 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (e>n)!"
888 else if ((d+e) > nint(n, ik)) then
889 status = 1007 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (d+e>n)!"
890 else if (n < zero_epsilon) then
891 status = 1008 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (n<0)!"
892 else if (n < 1) then
893 status = 1009 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (n<1)!"
894 else if (.not. ieee_is_finite(i)) then
895 status = 1010 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (i is infinite)!"
896 else if (ieee_is_nan(i)) then
897 status = 1011 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (i is NaN)!"
898 else if (abs(i) < zero_epsilon) then
899 status = 1012 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (i is 0%)!"
900 else if (abs(100+i) < zero_epsilon) then
901 status = 1013 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (i is -100%)!"
902 else if (.not. ieee_is_finite(g)) then
903 status = 1014 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (g is infinite)!"
904 else if (ieee_is_nan(g)) then
905 status = 1015 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (g is NaN)!"
906 else if (abs(g) < zero_epsilon) then
907 status = 1016 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (g is 0%)!"
908 else if (abs(100+g) < zero_epsilon) then
909 status = 1017 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (g is -100%)!"
910 else if (.not. ieee_is_finite(pv)) then
911 status = 1018 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (pv is infinite)!"
912 else if (.not. ieee_is_finite(fv)) then
913 status = 1019 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (fv is infinite)!"
914 else if (ieee_is_nan(pv)) then
915 status = 1020 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (pv is NaN)!"
916 else if (ieee_is_nan(fv)) then
917 status = 1021 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (fv is NaN)!"
918 else if (.not. ieee_is_finite(a)) then
919 status = 1022 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (a is infinite)!"
920 else if (ieee_is_nan(a)) then
921 status = 1023 ! "ERROR(tvm_delayed_geometric_annuity_check): Parameters inconsistent (a is NaN)!"
922 else
923 iq = p2f(i)
924 gq = p2f(g)
925 if (abs(i-g) < zero_epsilon) then ! i=g case
926 if (abs(a * (1 + iq) ** (-dble(d) + dble(n)) * (-d + n - e + 1) - fv) > consistent_epsilon) then
927 status = 1024 ! "ERROR(tvm_delayed_geometric_annuity_check): Inconsistent parameters (i=g & fv equation)!"
928 else if (abs((-d + n - e + 1) * (1 + iq) ** (-d) * a - pv) > consistent_epsilon) then
929 status = 1025 ! "ERROR(tvm_delayed_geometric_annuity_check): Inconsistent parameters (i=g & pv equation)!"
930 else
931 status = 0
932 end if
933 else ! i!=g case
934 if (abs(1 / (gq - iq) * ((1 + gq) ** (-d + n - e + 1) * (1 + iq) ** e - (1 + iq) ** (1 + n - d)) * a - fv) > consistent_epsilon) then
935 status = 1026 ! "ERROR(tvm_delayed_geometric_annuity_check): Inconsistent parameters (i!=g & fv equation)!"
936 else if (abs(((1 + gq) * ((1 + gq) / (1 + iq)) ** (n - e) - ((1 + gq) / (1 + iq)) ** d * (1 + iq)) * (1 + gq) ** (-d) * a / (gq - iq) - pv) > consistent_epsilon) then
937 status = 1027 ! "ERROR(tvm_delayed_geometric_annuity_check): Inconsistent parameters (i!=g & pv equation)!"
938 else
939 status = 0
940 end if
941 end if
942 end if
944
945 !----------------------------------------------------------------------------------------------------------------------------
946 !> Solve for TVM parameters for a arithmetic annuity certain.
947 !!
948 !! @f[ \begin{array}{cccc}
949 !! t & V_t & \mathrm{pv}_t & \mathrm{fv}_t \\
950 !! 0 & 0 & 0 & 0 \\
951 !! ... & ... & ... & ... \\
952 !! {d-1} & 0 & 0 & 0 \\
953 !! d & a+q\cdot 0 & \frac{a+q\cdot 0}{(1+i)^{d}} & (a+q\cdot 0)(1+i)^{n-d} \\
954 !! {d+1} & a+q\cdot 1 & \frac{a+q\cdot 1}{(1+i)^{d+1}} & (a+q\cdot 1)(1+i)^{n-d-1} \\
955 !! ... & ... & ... & ... \\
956 !! {t} & a+q\cdot (t-d) & \frac{a+q\cdot (t-d)}{(1+i)^{t}} & (a+q\cdot (t-d))(1+i)^{n-t} \\
957 !! ... & ... & ... & ... \\
958 !! {n-e-1} & a+q\cdot (n-e-1-d) & \frac{a+q\cdot (n-e-1-d)}{(1+i)^{n-e-1}} & (a+q\cdot (n-e-1-d))(1+i)^{e+1} \\
959 !! {n-e} & a+q\cdot (n-e-d) & \frac{a+q\cdot (n-e-d)}{(1+i)^{n-e}} & (a+q\cdot (n-e-d))(1+i)^{e} \\
960 !! {n-e+1} & 0 & 0 & 0 \\
961 !! ... & ... & ... & ... \\
962 !! n & 0 & 0 & 0 \\
963 !! \end{array} @f]
964 !!
965 !! Total pv & fv are given by:
966 !! @f[ \mathrm{fv} = \sum_{t=d}^{n-e} (a+q t-q d)(1+i)^{n-t} @f]
967 !! @f[ \mathrm{pv} = \sum_{t=d}^{n-e} \frac{a+q t-q d}{(1+i)^{t}} @f]
968 !!
969 !! The equations this solver uses are as follows:
970 !! @f[ \mathrm{fv} = \frac{\left(1+i \right)^{1+n -d} \left(a i +q \right)-\left(1+i \right)^{e} \left(\left(\left(1+n -e -d \right) q +a \right) i +q \right)}{i^{2}} @f]
971 !! @f[ \mathrm{pv} = \frac{\left(\left(\left(d -1-n +e \right) q -a \right) i -q \right) \left(\frac{1}{1+i}\right)^{n -e}+\left(a i +q \right) \left(1+i \right) \left(\frac{1}{1+i}\right)^{d}}{i^{2}} @f]
972 !!
973 !! This routine can solve one or two variables except for the following combinations:
974 !! `var_n+var_i`, `var_n+var_q`, `var_n+var_pv`, `var_n+var_fv`, `var_i+var_pv`, `var_i+var_fv`, `var_q+var_a`
975 !!
976 !! @param n Number of compounding periods
977 !! @param i Discount rate as a percentage.
978 !! @param q Payment growth rate (added at each payment)
979 !! @param pv Present Value
980 !! @param fv Future Value
981 !! @param a First payment (Annuity)
982 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
983 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
984 !! @param unknowns What variables to solve for.
985 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 4129-4160.
986 !!
987 subroutine tvm_delayed_arithmetic_annuity_solve(n, i, q, pv, fv, a, d, e, unknowns, status)
988 real(kind=rk), intent(inout) :: n, i, q, pv, fv, a
989 integer(kind=ik), intent(in) :: d, e
990 integer(kind=ik), intent(in) :: unknowns
991 integer(kind=ik), intent(out) :: status
992 integer(kind=ik) :: cur_unk, lst_unk
993 real(kind=rk) :: iq, iq1, epd, n1
994 cur_unk = unknowns
995 lst_unk = cur_unk
996 iq = p2f(i)
997 iq1 = 1+iq
998 epd = e + d
999 n1 = n + 1
1000 do
1001 if (bitset_subsetp(var_i, cur_unk) .and. bitset_not_intersectp(cur_unk, var_fv+var_pv+var_n)) then ! U=i, K=fv+pv+n ?=q+a
1002 iq = (fv / pv) ** (1 / n) - 1
1003 i = f2p(iq)
1004 iq1 = 1+iq
1005 cur_unk = bitset_minus(cur_unk, var_i)
1006 end if
1007 if (bitset_subsetp(var_n, cur_unk) .and. bitset_not_intersectp(cur_unk, var_fv+var_pv+var_i)) then ! U=n, K=fv+pv+i ?=q+a
1008 n = log(fv / pv) / log(iq1)
1009 n1 = n + 1
1010 cur_unk = bitset_minus(cur_unk, var_n)
1011 end if
1012 if (bitset_subsetp(var_pv, cur_unk) .and. bitset_not_intersectp(cur_unk, var_fv+var_i+var_n)) then ! U=pv, K=fv+i+n ?=q+a
1013 pv = fv / iq1 ** n
1014 cur_unk = bitset_minus(cur_unk, var_pv)
1015 end if
1016 if (bitset_subsetp(var_fv, cur_unk) .and. bitset_not_intersectp(cur_unk, var_pv+var_i+var_n)) then ! U=fv, K=pv+i+n ?=q+a
1017 fv = pv * iq1 ** n
1018 cur_unk = bitset_minus(cur_unk, var_fv)
1019 end if
1020 if (bitset_subsetp(var_a, cur_unk) .and. bitset_not_intersectp(cur_unk, var_i+var_n+var_q+var_fv)) then ! U=a, K=i+n+q+fv ?=pv
1021 a = iq1 ** d * (iq1 ** (n1 - d) * q + q * (-1 + (epd - n1) * iq) * iq1 ** e - fv * iq ** 2) / iq / (iq1 ** epd - iq1 ** (n1))
1022 cur_unk = bitset_minus(cur_unk, var_a)
1023 end if
1024 if (bitset_subsetp(var_q, cur_unk) .and. bitset_not_intersectp(cur_unk, var_i+var_a+var_pv+var_n)) then ! U=q, K=i+a+pv+n ?=fv
1025 q = iq * (a * iq1 ** epd + pv * iq * iq1 ** (d + n) - a * iq1 ** n * iq1) / ((-1 + (epd - n1) * iq) * iq1 ** epd + iq1 ** (n1))
1026 cur_unk = bitset_minus(cur_unk, var_q)
1027 end if
1028 if (bitset_subsetp(var_pv, cur_unk) .and. bitset_not_intersectp(cur_unk, var_n+var_q+var_a+var_i)) then ! U=pv, K=n+q+a+i ?=fv
1029 pv = ((((epd - n1) * q - a) * iq - q) * (1 / iq1) ** (n - e) + (a * iq + q) * iq1 * (1 / iq1) ** d) / iq ** 2
1030 cur_unk = bitset_minus(cur_unk, var_pv)
1031 end if
1032 if (bitset_subsetp(var_fv, cur_unk) .and. bitset_not_intersectp(cur_unk, var_n+var_q+var_a+var_i)) then ! U=fv, K=n+q+a+i ?=pv
1033 fv = (iq1 ** (n1 - d) * (a * iq + q) - iq1 ** e * (((n1 - e - d) * q + a) * iq + q)) / iq ** 2
1034 cur_unk = bitset_minus(cur_unk, var_fv)
1035 end if
1036 if (cur_unk == lst_unk) then
1037 exit
1038 end if
1039 lst_unk = cur_unk
1040 end do
1041 if (bitset_size(cur_unk) > 0) then
1042 status = 4129 ! "ERROR(tvm_delayed_arithmetic_annuity_solve): Unable to solve for some variables!"
1043 else
1044 call tvm_delayed_arithmetic_annuity_check(n, i, q, pv, fv, a, d, e, status)
1045 end if
1047
1048 !----------------------------------------------------------------------------------------------------------------------------
1049 !> Check TVM parameters for a arithmatic annuity certain.
1050 !!
1051 !! @param n Number of compounding periods
1052 !! @param i Discount rate as a percentage.
1053 !! @param q Payment growth rate (added at each payment)
1054 !! @param pv Present Value
1055 !! @param fv Future Value
1056 !! @param a First payment (Annuity)
1057 !! @param d Delay from time zero. i.e. d=0 is the beginning of period 1 otherwise d=j is the end if period j.
1058 !! @param e Early end counted from time end (t=n). i.e. e=0 means the last payment is at end of period n.
1059 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 4097-4128.
1060 !!
1061 subroutine tvm_delayed_arithmetic_annuity_check(n, i, q, pv, fv, a, d, e, status)
1062 real(kind=rk), intent(in) :: n, i, q, pv, fv, a
1063 integer(kind=ik), intent(in) :: d, e
1064 integer(kind=ik), intent(out) :: status
1065 real(kind=rk) :: iq
1066 if (d < 0) then
1067 status = 4097 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (d<0)!"
1068 else if (e < 0) then
1069 status = 4098 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (e<0)!"
1070 else if (.not. ieee_is_finite(n)) then
1071 status = 4099 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (n is infinite)!"
1072 else if (ieee_is_nan(n)) then
1073 status = 4100 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (n is NaN)!"
1074 else if (d > nint(n, ik)) then
1075 status = 4101 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (d>n)!"
1076 else if (e > nint(n, ik)) then
1077 status = 4102 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (e>n)!"
1078 else if ((d+e) > nint(n, ik)) then
1079 status = 4103 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (d+e>n)!"
1080 else if (n < zero_epsilon) then
1081 status = 4104 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (n<0)!"
1082 else if (n < 1) then
1083 status = 4105 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (n<1)!"
1084 else if (.not. ieee_is_finite(i)) then
1085 status = 4106 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (i is infinite)!"
1086 else if (ieee_is_nan(i)) then
1087 status = 4107 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (i is NaN)!"
1088 else if (abs(i) < zero_epsilon) then
1089 status = 4108 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (i is 0%)!"
1090 else if (abs(100+i) < zero_epsilon) then
1091 status = 4109 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (i is -100%)!"
1092 else if (.not. ieee_is_finite(q)) then
1093 status = 4110 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (q is infinite)!"
1094 else if (ieee_is_nan(q)) then
1095 status = 4111 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (q is NaN)!"
1096 else if (abs(q) < zero_epsilon) then
1097 status = 4112 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (q is 0%)!"
1098 else if (.not. ieee_is_finite(pv)) then
1099 status = 4113 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (pv is infinite)!"
1100 else if (.not. ieee_is_finite(fv)) then
1101 status = 4114 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (fv is infinite)!"
1102 else if (ieee_is_nan(pv)) then
1103 status = 4115 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (pv is NaN)!"
1104 else if (ieee_is_nan(fv)) then
1105 status = 4116 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (fv is NaN)!"
1106 else if (.not. ieee_is_finite(a)) then
1107 status = 4117 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (a is infinite)!"
1108 else if (ieee_is_nan(a)) then
1109 status = 4118 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Parameters inconsistent (a is NaN)!"
1110 else
1111 iq = p2f(i)
1112 if (abs(((1 + iq) ** (1 + n - d) * (a * iq + q) - (1 + iq) ** e * (((1 + n - e - d) * q + a) * iq + q)) / iq ** 2 - fv) > consistent_epsilon) then
1113 status = 4119 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Inconsistent parameters (i!=g & fv equation)!"
1114 else if (abs(((((d - 1 - n + e) * q - a) * iq - q) * (1 / (1 + iq)) ** (n - e) + (a * iq + q) * (1 + iq) * (1 / (1 + iq)) ** d) / iq ** 2 - pv) > consistent_epsilon) then
1115 status = 4120 ! "ERROR(tvm_delayed_arithmetic_annuity_check): Inconsistent parameters (i!=g & pv equation)!"
1116 else
1117 status = 0
1118 end if
1119 end if
1121
1122end module mrffl_tvm
real(kind=rk) function sf_i_no_pv(i)
real(kind=rk) function sf_i_no_n(i)
real(kind=rk) function sf_i_no_fv(i)
Simple sets (using the bits of an integer to indicate element existence).
integer(kind=ik) pure function, public bitset_size(bitset)
Return the number of elements in bitset.
logical pure function, public bitset_not_intersectp(bitset1, bitset2)
integer(kind=ik) pure function, public bitset_minus(bitset1, bitset2)
Return the set diffrence between bitset1 and bitset2.
logical pure function, public bitset_not_subsetp(bitset1, bitset2)
logical pure function, public bitset_subsetp(bitset1, bitset2)
Configuration for MRFFL (MR Fortran Finance Library).
integer, parameter, public mrfflrk
Real kind used in interfaces.
real(kind=mrfflrk), parameter, public zero_epsilon
Used to test for zero.
integer, parameter, public mrfflik
Integer kinds used in interfaces.
Simple functions for working with percentages.
elemental real(kind=rk) function, public percentage_to_fraction(p)
Convert a percentage to a fraction.
elemental real(kind=rk) function, public fraction_to_percentage(f)
Convert a fraction to a percentage.
Root solvers.
subroutine, public multi_bisection(xc, x0_init, x1_init, f, x_epsilon, y_epsilon, max_itr, status, progress)
Use bisection() to search for a root for the function f in a list of intervals returning the first ro...
Solvers for TVM problems involving lump sums, level (fixed) annuities, and geometric (growing) annuit...
real(kind=rk) pure function, public tvm_geometric_annuity_sum_a(n, g, a)
Sum the payments from a geometric annuity.
subroutine, public tvm_delayed_geometric_annuity_solve(n, i, g, pv, fv, a, d, e, unknowns, status)
Solve for TVM parameters for a geometric annuity certain.
subroutine, public tvm_delayed_arithmetic_annuity_check(n, i, q, pv, fv, a, d, e, status)
Check TVM parameters for a arithmatic annuity certain.
subroutine, public tvm_lump_sum_solve(n, i, pv, fv, unknowns, status)
Solve for TVM parameters for a lump sum.
integer(kind=ik) pure function, public tvm_delayed_annuity_num_payments(n, d, e)
Return the number of payments given the period count (n), delay (d), and early end (e).
subroutine, public tvm_delayed_arithmetic_annuity_solve(n, i, q, pv, fv, a, d, e, unknowns, status)
Solve for TVM parameters for a arithmetic annuity certain.
subroutine, public tvm_delayed_lump_sum_solve(n, i, pv, fv, a, d, unknowns, status)
Solve for TVM parameters for a generalized lump sum.
real(kind=rk), parameter consistent_epsilon
Used to check equation consistency.
subroutine, public tvm_delayed_lump_sum_check(n, i, pv, fv, a, d, status)
Check the TVM parameters for a generalized lump sum.
real(kind=rk) pure elemental function, public fv_from_pv_n_i(pv, n, i)
compute future value from present value (pv), number of periods (n), and an intrest rate (i).
subroutine, public tvm_delayed_level_annuity_check(n, i, pv, fv, a, d, e, status)
Check TVM parameters for a level annuity.
subroutine, public tvm_delayed_level_annuity_solve(n, i, pv, fv, a, d, e, unknowns, status)
Solve for TVM parameters for a level annuity.
subroutine, public tvm_delayed_geometric_annuity_check(n, i, g, pv, fv, a, d, e, status)
Check TVM parameters for a geometric annuity certain.
Constants to to identify TVM variables.
integer(kind=ik), parameter, public var_pv
Present value.
integer(kind=ik), parameter, public var_fv
Future value.
integer(kind=ik), parameter, public var_n
Number of periods.
integer(kind=ik), parameter, public var_a
First annuity payment.
integer(kind=ik), parameter, public var_none
No variables in set.
integer(kind=ik), parameter, public var_i
Interest/rate (First rate for geometric annuity)
integer(kind=ik), parameter, public var_q
Growth rate for arithmatic annuities.
integer(kind=ik), parameter, public var_g
Second Interest/rate (for geometric annuity)
integer(kind=ik), parameter, public var_p
Principal.