The usual formula is the one that calculates the “PpP” as a function of the other 3 variables: PpP = i * C / ( 1 - (1+i)^(-n) ).
If we want to calculate any of the other variables, the formula must be rewritten with the required variable at the left side of the equation.
For instance, if one want to know the attainable principal sum, given a possible PpP, the formula may be easily rewritten.
If the number of periods has to be calculated, given the other 3 variables, it is already somewhat more cumbersome to rewrite the equation, because the “n” appears as a negative power in the original formula.
Finally, it is impossible to rewrite the equation as an explicit calculation of the interest rate, because “i” appears twice in the right part of the equation in two “incompatible” forms.
Hence we run in two difficulties/inconveniences when calculations have to be made in systems with many varables: the equation has to be rewitten (and coded) for every variable, and moreover: in many cases it is impossible to do so.
Both problems may be eliminated by the following technique.
If “PhP” in the original formula is moved to the other side of the equation, we get the following base equation:
i * C / ( 1 - (1+i)^(-n) ) - PpP = 0
The left side of this equation may be thought of as a new function:
z = i * C / ( 1 - (1+i)^(-n) ) - PpP
In this function all variables are of equal significance.
If the values of the 4 variables cause z to be zero (z=>0), we have a valid solution of our base equation.
If the calculation of z leads to a non-zero result, we may select one of the 4 variables and use a nice algoritm to push that variable to a value which causes z to become zero.
That algorithm is the “Newton-Raphson” iteration (google to find nice explanations of it), wich is widely used to find “zero’s” of functions.
In the program, two ‘yellow sections’ indicate the spots where the model data (variables and calculation) of the user are inserted.
For the annuity model, the 4 variable definitions are:
data 1, "C", "A1",1,100000,0,0,0
data 2, "%", "A2",1,5,0,0,4
data 3, "n", "A3",1,30,0,0,0
data 4, "PpP", "B2",1,8000,0,0,2
For each varable, the data are:
- the variable index number. The values of the variables are kept in an array. In the calculation definition the variables are adressed by their index number in that array.
- a short description of the variable, which will be placed in the upper button
- The position of the variable on the screen. Here an Excel-like scheme is used. Capitals for columns, numbers for the lines.
- Selectability (1=selectable, 0=non selectable). In this model, all variables are selectable
- Initial (estimated) value for the variable (appears in lower button)
- Lower and upper limits on input (two zeros mean: no limitations)
- Number of decimals in the output in the lower buttons
This variable definition results in the following sceen layout:
The calculation rule(s) is/are defined in the second “yellow section”, in the function “func()”.
In this case it is simply the base equation: “ z = i * C / ( 1 - (1+i)^(-n) ) - PpP”,
with the varablenames translated into their respective element in the value array .c(). The fact that the index numbers are mentioned in the read/data block is helpfully with this translation.
def func()
z = .c(2)/100* .c(1) / (1 - (1+.c(2)/100)^(-.c(3))) - .c(4)
return z
end
The buttons are placed in a grid. The user has to specify a column number and a rownumber, using the “Excel” method.
The size of the buttons may be adapted in line 6 of the code. The present size is 125 x 30 pixels.
The orientation may be set to landscape by uncommenting line 2 of the code.
The code is split in two parts, the “main” program, and a library which is appended at the end of the main program using the {} brackets.
The library may contain functions that are not used in this simple annuity loan example, but in other applications of this framework.
The code follows:
“Main program”:
Code: Select all
graphics ! graphics clear .8,.8,.8 ! draw color 0,0,0
' set orientation landscape
set toolbar off
'*** define item location grid ***
get screen size sw,sh
but_w=125 ! but_h=30 ' button size
dbx=but_w+20 ! dby=2*but_h+20 ' button spacing
nbx=floor(sw/dbx) ! nby=floor(sh/dby) ' max # of buttons
'*** init small numkey tool ***
init_numpad(450,200,60,.3,.3,0,1)
'*** define stop button ***
xo=sw-but_w-20 ! yo=sh-but_h-20
button "stop" text "Stop" at xo,yo size but_w,but_h
while data_exist() '*** count the data items ***
ne+=1 ! read dum,dum$,dum$,dum,dum,dum,dum,dum
end while
dim c(ne+1),cs(ne+1),permit(ne+1)
dim cmin(ne+1),cmax(ne+1),dec(ne+1)
'*** read data items & create buttons
restore ! set buttons custom
for k=1 to ne
read i,c$,pos$,permit(i),c(i),cmin(i),cmax(i),dec(i)
entity(c$,k,pos$,permit(i),c(i))
next k
disp()
'y'
' var. nr, name$,column&row,permit,init.value,min,max,decimals
data 1, "C", "A1",1,100000,0,0,0
data 2, "%", "A2",1,5,0,0,4
data 3, "n", "A3",1,30,0,0,0
data 4, "PpP", "B2",1,8000,0,0,2
''
do slowdown
if bp("stop") then break
for i=1 to ne
if bp("fld"&i) then break
next i
if i<=ne then
c(i)=numpad(cmin(i),cmax(i)) ! button "fld"&i text c(i)
end if
for j=1 to ne
if bp("but"&j) then break
next j
if j<=ne and permit(j)=1 then
steps=0
do
steps+=1
if steps>100 then
c(j)=-999 ! break
end if
eps=func() ! if eps=0 then break
dx=eps/diff(j) ! c(j)-=dx
until abs(dx)<0.0001 or bp("stop")
disp()
end if
until forever
set orientation portrait
end
def func()
'y'
rate=.c(2)/100
z = rate * .c(1) / (1 - (1+rate)^(-.c(3))) - .c(4)
return z
''
end def
def disp()
for i=1 to .ne
b$="fld"&i ! d=10^.dec(i) ! v=int(d*.c(i))/d
button b$ text v
next i
end def
def diff(k)
delta=.001*.c(k) ! if delta=0 then delta=0.01
save()
fo=func() ! back()
.c(k)+=delta ! fd=func() ! back()
return (fd-fo)/delta
end def
def save()
for i=1 to .ne ! .cs(i)=.c(i) ! next i
end def
def back()
for i=1 to .ne ! .c(i)=.cs(i) ! next i
end def
{Equation.libr}
“library” (the filename as mentioned in the main program is: “Equation.libr”)
Code: Select all
def entity(name$,k,pos$,p,init)
if p=1 then fill color 0,1,0 else fill color 1,1,1
nc=asc(left$(pos$,1))-asc("A")+1
nr=val(right$(pos$,len(pos$)-1))
xo=20+(nc-1)*.dbx ! yo=20+(nr-1)*.dby
button "but"&k text name$ at xo,yo size .but_w,.but_h
fill color .9,.9,.9
button "fld"&k text init at xo,yo+30 size .but_w,.but_h
end def
def tus(x)
if init=0 then
init=1 ! window("tussen",190,650,200,120,1)
end if
page "tussen" show ! page "tussen" set
button "tussen_parm" text x
do slowdown
if bp("tussen_db") then db
if bp("tussen_stop") then stop
until bp("tussen_quit")
page "tussen" hide ! page "" set
end def
' paged window met optionele close button
' close=1 : window met close button, anders 0
'
def window(name$,xo,yo,ww,wh,close)
page name$ set
page name$ frame xo,yo,ww,wh
set buttons custom ! fill color 1,1,.9
set buttons font size 24
button name$&"win1" text "" at 0,0 size ww,wh
button name$&"win2" text "" at 1,1 size ww-2,wh-2
if close then cx=30 else cx=0
button name$&"title1" text "" at 0,0 size ww-cx,30
button name$&"title2" text "Inspect" at 1,1 size ww-cx-2,28
if close then
button name$&"quit1" text "" at ww-32,0 size 30,30
button name$&"_quit" text chr$(10060) at ww-31,1 size 28,28
end if
' window specifics
'
button name$&"_parm" text "" at 10,40 size ww-20,30
button name$&"_db" text "Debug" at 10,80 size 80,30
button name$&"_stop" text "Stop" at 110,80 size 80,30
fill color .8,.8,.8
end def
' NASA standard atmosphere
'
def dens(h)
to=273.1
if h<11000 then
t=to+15.04-0.00649*h ! p=101.29*(t/288.08)^5.256
else
if h<25000 then
t=to-56.46 ! p=22.65*exp(1.73-0.000157*h)
else
t=to-131.21+0.00299*h ! p=2.488*(t/216.6)^-11.388
end if
end if
d=p/0.2869/t ! t-=to
return d
end def
' numerical keypad object
'
' produce a simple keypad to quickly enter a number in an app
' upon entry, the keypad disappears
' initialize once, multiple use after
' left upper corner is placed at "xtop,ytop"
' "bs" is the button size (keypad becomes 4.3 times larger)
' size of number is accepted between "minval" and "maxval"
' if both "minval" and "maxval" are zero, then no restrictions
' max number of tokens in the number is 10 (minus and dot included)
' works for option base 0 and 1
'
def init_numpad(xtop,ytop,bs,R,G,B,alpha)
name$="numpad" ! cn=10
page name$ set
page name$ frame xtop,ytop,0,0
set buttons custom
if bs<20 then bs=20
sp=4 ! th=.5*bs+4 ! ww=4*bs+5*sp ! hh=th+4*bs+6*sp
fsize=.5*bs
draw font size fsize ! set buttons font size fsize
draw color 1,1,1 ! fill color .5,.5,.5
button "rec" title "" at 0,0 size ww,hh
button "res" title "" at 0,0 size ww,th+4
fill color R,G,B ! fill alpha alpha
button "0" title "0" at sp,th+3*bs+5*sp size bs,bs
for k=1 to 9
x=(k-1)%3 ! y=2-floor((k-1)/3)
button k title k at (x+1)*sp+x*bs,th+y*bs+(y+2)*sp size bs,bs
next k
button "-" title "-" at 2*sp+bs,th+3*bs+5*sp size bs,bs
button "." title "." at 3*sp+2*bs,th+3*bs+5*sp size bs,bs
button "Cl" title "C" at 4*sp+3*bs,th+2*sp size bs,bs
button "del" title "<-" at 4*sp+3*bs,th+bs+3*sp size bs,bs
button "ok" title "ok" at 4*sp+3*bs,th+2*bs+4*sp size bs,2*bs+sp
page name$ hide
page name$ frame xtop,ytop,ww,hh
set buttons default ! set buttons font size 20
draw font size 20 ! draw color 0,0,0
page "" set
end def
def numpad(minval,maxval)
page "numpad" set ! page "numpad" show
a$="" ! pflag=0 ! sflag=0 ! ob=1-option_base()
nump1:
if bp("ok") then
number=val(a$) ! a$="" ! button "res" text ""
if minval<>0 or maxval<>0 then
if number<minval or number>maxval then
button "res" text "range error"
pflag=0 ! a$="" ! pause 1
button "res" text ""
goto nump1
end if
end if
page "numpad" hide ! page "" set
return number
end if
if bp("Cl") then
a$ = "" ! pflag=0 ! sflag=0 ! goto nump3
end if
if bp("del") and len(a$) then
ll=len(a$) ! if substr$(a$,ll-ob,ll-ob)="." then pflag=0
a$ = left$(a$,ll-1) ! sflag=0 ! goto nump3
end if
if bp("-") then
a$ = "-" ! pflag=0 ! sflag=0 ! goto nump3
end if
if bp(".") and not pflag and not sflag then
a$ &= "." ! pflag=1 ! goto nump3
end if
for k=0 to 9
t$=k
if bp(t$) and not sflag then
a$ &= t$ ! goto nump3
end if
next k
goto nump1
nump3:
if len(a$)>10 then ! sflag=1 ! goto nump1 ! end if
button "res" text a$
goto nump1
end def
def db ! debug pause ! end def
def bp(a$) = button_pressed(a$)