A flexible calculation tool (part 2)

Post Reply
Henko
Posts: 814
Joined: Tue Apr 09, 2013 12:23 pm
My devices: iPhone,iPad
Windows
Location: Groningen, Netherlands
Flag: Netherlands

A flexible calculation tool (part 2)

Post by Henko »

Calculations around an annuity loan requires 4 variables: the principal sum “C”, the interest rate “i”, the amount of periods “n”, and the payment per period “PpP”.
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:
IMG_1241.jpeg
IMG_1241.jpeg (216.76 KiB) Viewed 601 times

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$)

Post Reply