Clean out unused financial calculation stand-alone programs

No-one even knew about them!
 


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@22846 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
John Ralls 2013-03-29 22:54:41 +00:00
parent 2fd588dd32
commit 9b02886804
12 changed files with 0 additions and 5522 deletions

View File

@ -1,49 +0,0 @@
# qtgrep makefile
# created by Terry Boldt 3-16-2000
#
GLIB_CFLAGS = `glib-config --cflags`
CFLAGS = -O3 $(GLIB_CFLAGS)
#CFLAGS = -g
CC = gcc
all: financial fini
HDRS = finproto.h \
fin_static_proto.h\
fin_spl_protos.h\
finvar.h
OBJSM = fin.o\
amort_prt.o\
fin-main.o
OBJSI = fin.o \
expression_parser.o\
numeric_ops.o\
amort_prt.o\
amort_opt.o\
fin-interactive.o
financial: $(OBJSM)
@echo "linking"
$(CC) `glib-config --libs` -o $@ $(OBJSM)
fini: $(OBJSI)
@echo "linking"
$(CC) `glib-config --libs` -o $@ $(OBJSI)
fin.o: fin.c $(HDRS)
expression_parser.o: expression_parser.c $(HDRS)
fin-interactive.o: fin-interactive.c $(HDRS)
fin-main.o: fin-main.c $(HDRS)
numeric_ops.o: numeric_ops.c $(HDRS)
amort_prt.o: amort_prt.c $(HDRS)
amort_opt.o: amort_opt.c $(HDRS)

View File

@ -1,989 +0,0 @@
Dave - the attached file contains the following files:
drwx------ 4096 Jul 4 16:20 ./
drwxr-xr-x 4096 Jun 19 17:20 ../
-rw-r--r-- 211 Jun 23 20:24 .kdbgrc.financial #kdebug file
-rw-r--r-- 259 Jul 4 15:24 .kdbgrc.fini #kdebug file
-rw-r--r-- 732 Jul 4 15:24 Makefile
-rw-r--r-- 5711 Jul 3 20:02 amort_opt.c # display/set amortization options
-rw-r--r-- 4392 Jul 4 15:25 amort_opt.o
-rw-r--r-- 13020 Jul 4 16:16 amort_prt.c # print amortization schedule(s)
-rw-r--r-- 7256 Jul 4 16:16 amort_prt.o
-rw-r--r-- 14778 Jul 3 20:09 expression_parser.c # expression parser/evaluator
-rw-r--r-- 5148 Jul 4 15:24 expression_parser.o
-rw-r--r-- 19225 Jul 3 20:03 fin-interactive.c # demo
-rw-r--r-- 12964 Jul 4 15:25 fin-interactive.o
-rw-r--r-- 6102 Jul 4 16:01 fin-main.c # fixed examples
-rw-r--r-- 5008 Jul 4 16:02 fin-main.o
-rwxr-xr-x 1431 Jul 2 19:21 fin-proto.sh* # shell script for making 'h' files
-rw-r--r-- 83854 Jul 4 16:34 fin.c # financial and amortization functions
-rw-r--r-- 23880 Jul 4 16:16 fin.o
-rw-r--r-- 1063 Jun 28 16:28 fin_spl_protos.h # function prototypes
-rw-r--r-- 4631 Jul 3 20:07 fin_static_proto.h # function prototypes
-rwxr-xr-x 41011 Jul 4 16:16 financial* # executable demo - from fin-main.c
-rwxr-xr-x 54720 Jul 4 16:16 fini* # executable demo - from fin-interactive.c
-rw-r--r-- 90 Jun 22 20:37 fini-input
-rw-r--r-- 2624 Jun 22 20:24 fini-output
-rw-r--r-- 7407 Jul 3 20:07 finproto.h # function prototypes
-rw-r--r-- 8152 Jul 4 15:23 finvar.h # define global structures used
drwx------ 4096 Jun 27 20:24 html/ # html documentation
-rw-r--r-- 6938 Jul 3 20:04 numeric_ops.c # numeric functions - for expression parser
-rw-r--r-- 2204 Jul 4 15:24 numeric_ops.o
-rw-r--r-- 11814 Jun 20 19:58 readme # this file
-rw-r--r-- 131016 Jul 4 16:20 sample # output of financial executable
financial-equations/html:
total 164
drwx------ 4096 Jun 27 20:24 ./
drwx------ 4096 Jul 4 16:20 ../
-rw-r--r-- 17430 Jun 20 15:29 amorta.html # amortization schedule
-rw-r--r-- 26222 Jun 20 15:29 amortp.html # amortization schedule
-rw-r--r-- 2361 Jun 27 20:38 constderv.html # equations derivation
-rw-r--r-- 11290 Jun 27 20:25 finderv.html # equations derivation
-rw-r--r-- 82204 Jun 27 20:36 finutil.html # basic use of financial calculator
This 'readme' file is probably somewhat rambling - it is coming off
the top of my head with very little organization. Note also that much
of the following "explanation" has been duplicated as comments at the
top of the "fin.c" and the "expression-parser.c" files. Thus, anyone
desiring to use the functions contained therein will have some ready
documentation.
Look into the 'sample' file. It contains sample output from the
routines - both from the calculator and the amortization routines. The
sample file was created with the "financial" executable. If running
Linux, you should be able to execute this program without compiling to
recreate 'sample' as "./financial >sample"
You may want to view the file "finutil.html" in a browser - it
explains in detail the calculator functions and variables and concepts
and amortization schedule options.
The file 'finvar.h' contains the structures needed by functions
calling the financial functions and amortization functions and the
expression parser/evaluator and should be included by any file calling
any financial function and and the amortization functions. The two
prototype files:
finproto.h
fin_spl_protos.h
should be "included" by any files containing functions which call
either the financial calculator functions (including the amortization
functions) or the expression parser/evaluator.
There are no "global variables". My first iteration on "porting"
the calculator contained global variables for the basic financial
variables. I decided against doing this for reasons of conflict with
other variables used in other modules and also to insure that ANY
module in gnucash could call the financial functions and have their
own set of financial variables. Thus any gnucash module may use the
financial functions without concern of interaction with any other
module also using the functions concurrently.
There are 9 "financial variables". Of the nine, four are ALWAYS set by
the "user", i.e., calling function. Of the remaining five, four are
set and the fifth computed by the appropriate financial calculator
function listed below. The 9 "financial variables" are: (if more than
one name is used for a particular financial variable, all are listed)
n or npp == number of payment periods in the transaction under
consideration
i, nint or ir == nominal interest rate for the transaction.
Use the interest rate as used by humans,
i.e., 9.5, 8.25, etc. and not 0.095, 0.0825
pv == present value, i.e., the value of the transaction at the
present moment in time. This would be the amount of the
loan/mortgage/Certificate of Deposit/bank account/etc.
pmt == periodic payment
fv == future value, i.e., the value of the transaction at some
future time after n payment periods.
Of the five values above, four are set by the "user" and the fifth
computed. The remaining financial variables are:
CF == Compounding Frequency, number of compounding periods in one year
PF == Payment Frequency, number of payment periods in one year
disc == TRUE, 1, for discrete compounding (used by most financial
institutions in US)
FALSE, 0, for continuous compounding (used by some banks
in my area for CDs)
bep == TRUE for beginning of periods payments
FALSE for end of period payments (the norm in the US at least)
The function 'set_default(fi_ptr fi)' will set the variables in the
'fi' structure to the default values:
n == 0
i = 0.0
pv = 0.0
pmt = 0.0
fv = 0.0
CF = 12
PF = 12
disc = 1
bep = 0
(Maybe this could/should be made locale specific or configurable ??? )
In addition, there is one other "financial variable" which is not
really a financial variable, but which should be specific to any
particular financial computation and under the control of the user:
prec == numeric precesion used for round-off of numeric values, i.e.,
number of digits past the radix point.
I currently have a static function defined in the "fin.c" file:
static double rnd(
double x,
unsigned places)
{
double r;
unsigned char buf[50]; /* make buffer large enough */
if ( places >= 0 ) {
sprintf(buf,"%.*f",places,x);
sscanf(buf,"%lf",&r);
} else r = x;
return r;
} /* rnd */
The function returns x rounded to the specified number of decimal
digits past the radix point. I assume that gnucash currently has such
a function. I will need the name of this function, since it is used
extensively in the financial computation and the computation of
amortization schedules. The function 'rnd' that I have included is
pretty simple - it simply writes the double value to a string using
'sprintf' to print the value and round the value. It then uses
'sscanf' to read the value into a temporary double variable and
returns that value. I have written many rounding functions in the past
and could never find one that everybody agreed with. Not every one
agrees with the rounding rules used by the C 'printf' functions, but
at least everybody writing C and C++ uses them and is used to them.
The financial functions use a structure (defined in 'finvar.h'):
/* structure used by financial computation routines to store
* financial variables */
typedef struct financial_info *fi_ptr;
typedef struct financial_info {
double ir; /* interest rate */
double pv; /* present value */
double pmt; /* periodic payment */
double fv; /* future value */
unsigned npp; /* number of payment periods */
unsigned CF; /* Compounding frequency */
unsigned PF; /* payment frequency */
unsigned bep; /* beginning/end of period payment flag */
/* TRUE == beginning of period */
/* FALSE == end of period */
unsigned disc; /* discrete/continuous compounding flag */
/* TRUE == discrete compounding */
/* FALSE == continuous compounding */
/* precision of roundoff for pv, pmt and fv.
* i, Interest not rounded
* n, number of periods rounded to integer value,
* implicit value of zero, 0
*
* 2 for US Dollars
*/
unsigned prec;
} financial_info;
to contain all information used in the financial calculations. The
structure is created by the calling function and the variables set by
the calling function. The appropriate financial function is called to
compute the desired quantity with a pointer to the structure.
The following functions are contained in the financial calculator:
unsigned N(fi_ptr fi) -- solves for number of payment periods rounded to an
integer value. Sets the variable 'npp' in the passed
structure and returns the same value.
double I(fi_ptr fi) -- solves for the nominal interest rate rounded to
the precision specified in the structure passed.
Sets the variable 'ir' in the passed structure
and returns the same value.
double PV(fi_ptr fi) -- solves for the present value rounded to the precision
specified. Sets the variable 'pv' in the passed
structure and returns the same value.
double PMT(fi_ptr fi) -- solves for the periodic payment rounded to the
precision specified. Sets the variable 'pmt' in
the passed structure and returns the same value.
double FV(fi_ptr fi) -- solves for the future value rounded to the precision
specified. Sets the variable 'fv' in the passed
structure and returns the same value.
The above functions use the following functions for the calculation
and round the answer as indicated. The above functions are the
"normal" functions called to calculate the corresponding values. I
left the following functions as "global" in case there is a need for
unrounded, full floating point accuracy answers. They could be
converted to "static" functions if there is any conflict or the full
accuracy is not needed elsewhere in gnucash. Also these function do
not use the financial calculator structure, but rather the variables
must be passed as arguments with the desired value returned.
double _N(ir,pv,pmt,fv,CF,PF,disc,bep) - compute number of payment periods
and return value
double _I(npp,pv,pmt,fv,CF,PF,disc,bep) - compute nominal interest rate and
return value
double _PV(npp,ir,pmt,fv,CF,PF,disc,bep) - compute present value and return
value
double _PMT(npp,ir,pv,fv,CF,PF,disc,bep) - compute periodic payment and
return value
double _FV(npp,ir,pv,pmt,CF,PF,disc,bep) - compute future value and return
value
Note, the return value of '_N' should always be rounded (or truncated,
probably rounded) since a fractional payment period is a practical
impossibility.
The financial calculator functions do no "value checking". That is,
there is no checking for erroneous values or any "reasonable value
testing". The only values that absolutely have to be checked by the
calling functions are:
1: nominal interest rate when calculating one of the other variables.
If this value is zero a "divide by zero" error may occur. If the
divide by zero error does not occur, then the value calculated
will more than likely be returned as 'nan'. Always check this value
for non-zero if it is not the value to be computed. A non-zero value
is only common sense also, since a zero interest rate is probably
meaningless.
2: number of payment periods. When calculating the nominal interest rate,
if the number of payment periods is zero, again a "divide by zero" error
may occur. Always check this value for non-zero if it is not the value to
be computed. Again, a zero number of payment periods is meaningless.
At least one payment must be made or one party to the transaction is
making a free gift to the other party - in which case the calculator
is not needed.
Note: I have violated both of these rules in the two demo executables
provided. For the 'financial' executable, such a check is not
necessary since all of the examples are set statically and it is known
that neither interest or number of periods is zero. For the 'fini'
executable, they should be checked, but for demo purposes I didn't
think it necessary.
A good user interface would check that the values of CF, PF, disc and
bep are set (at least to the default values) and that at least 4 of
the remaining variables have been set and the user asked for the fifth
value to be computed. Leaving the value of 'fv' to zero would be the
normal in most cases.
I decided that the financial functions should do no value checking,
since they are far enough down the call chain that returning in the
case of erroneous values is problamatic and adds excessive structure
to otherwise extremely simple functions. Also since the value checking
that does need to be accomplished is minimal and should optimally be
done as close to the user interface as possible.
My first take on a possible user interface would look something like
(or as close as I can get in an ASCII text file):
NOTE: ==xxxxxxxxx== means a GUI box for displaying/entering a value
((compute)) means a GUI button with the title "compute"
<<Monthly>> means a GUI box with dropdown values that the user selects
there is a name for such a box in GUI nomenclature,
but I forget the name
|| means a GUI check box button (either on or off).
The Title for the button would change to reflect
the on/off, TRUE/FALSE value
|**********| means a single line text box that the user could
use to enter a string. This box could be used by
the user to enter expressions, with named variables,
to be evaluated. This box is probably he same kind
of GUI box as I have indicated above with == xxxx==
Possible Financial Calculator GUI:
|-------------------------------------------------------------------------|
Number of
Payment Periods ((Compute))
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
Interest Rate ((Compute))
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
Present Value ((Compute))
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
Periodic Payment ((Compute))
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
Future Value ((Compute))
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
Compounding Payment || End of Period Payments
Frequency Frequency
<<Monthly>> <<Monthly>> || Discrete Compounding
Expression
|************************************************************|
User Defined Variables
Some kind of GUI box displaying both the names and values of
variables that the user has defined using the expression box
above or the financial variables entry boxes. The variables
are used to store values for future use/reference
Effective Date: ==mm/dd/yyyy==
Initial Payment date: ==mm/dd/yyyy==
|------------------------------------------------------------------------|
Note: the drop down boxes for Compounding Frequency and Payment
Frequency should contain the following selectable values:
annual 1
semi-annual 2
tri-annual 3
quarterly 4
bi-monthly 6
monthly 12
semi-monthly 24
bi-weekly 26
weekly 52
daily (360) 360
daily (365) 365
with the corresponding values used for CF and PF as indicated.
Clicking on "Discrete Compounding" switches back and forth between
"Discrete Compounding" and "Continuous Compounding". "Discrete
Compounding" should be the default since it is by far the most common.
Clicking on "End of Period Payments" switches back and forth between
"End of Period Payments" and "Beginning of Period Payments". "End of
Period Payments" should probably be the default since it is by far the
most common.
The user could click on the entry box under "Number of Payment
Periods", "Interest rate", "Present Value", "Periodic Payment"
and "Future Value" and enter a value or expression string to be
evaluated. Clicking on the corresponding "Compute" button would
compute that financial variable. I have made the entry boxes long so
that a user could enter a fairly long string. The boxes could also be
made scrollable. The entry function could be made simple and always
regard the entry as a string and pass the string to the
parser/evaluator to return the appropriate value.
The titles for the boxes, especially "Present Value" and "Future
Value", may need to be changed depending on the feedback from the
gnucash community. I have always used those names since they are
"generic" and represent all of the possible uses for the financial
calculator. In this way a single calculator GUI could be used instead
of inventing a GUI for mortgages, another for CDs, another for saving
accounts, another for annuities, another for sinking funds, etc.
The next question is the Amortization Schedule Interface. This
interface should not be available until all of the financial variables
are known. Once they are, the user should be able to click on a "tab"
or something to get the Amortization Schedule Interface. A first stab
at such an interface could be as shown below. Once the financial
variables are known - either all entered by the user or one computed
as indicated by the user, the function "Amortization_init" would be
called to compute the quatities needed for the user to choose which
amortization schedule is desired. The function "amort_opt" in the
file amort_opt.c contains all of the information that the user must
have to decide which of the six schedules is desired.
Note that the function "Amortization_init" needs two dates:
Effective Date of Transaction -- the date the papers were signed, etc.
Initial Payment Date -- self explanatory
Both dates could be initially filled in by the interface with default
values. For the Effective Date, the current date would probably be the
most likely candidate. The default Initial Payment Date would depend
on whether the payments were being made at the beginning or end of the
payment periods, i.e., bep in the financial information structure
above.
bep == TRUE, use the first day of the next month for the default
Initial Payment Date, e.g., if current day is August 6, 2000,
then the default Initial Payment Date would be September 1, 2000
bep == FALSE, use if the first day of the month after the next month,
e.g., if current day is August 6, 2000, then the default Initial
Payment Date would be October 1, 2000
The user could then change or accept the default dates as desired.
The only real checking that would need to be done on either date is to
insure that the Effective Date preceeds the Initial Payment Date.
The following functions are used for amortization schedules. All
functions use the amortization structure defined in "finvar.h",
"amort_sched".
Notice that some of the information is duplication of the infomation
used by the financial calculator structure. I thought this best in
order to "separate" these two functions so that if a new set of
financial variables is desired by the user, the old set used by the
amortization computation functions would still be available. The
amount of storage is minimal.
Note that the amortization schedule structure, "amort_sched" is
divided into three parts.
The first part is information set by the calling function(s) and is
the financial information from the financial information structure
supplemented with the Effective Date and Initial Payment Date
information.
The second part is also set by the calling function(s) and sets which
schedule and type of schedule is computed.
The third part is information set by the amortization functions. Some
of the information is needed for the GUI below for selecting which
amortization option to compute.
Amortization Schedule Selection GUI:
|----------------------------------------------------------------------------|
Amortization Schedule
Effective Date: mm/dd/yyyy
Initial Payment date: mm/dd/yyyy
The Original Present Value is: xxxxxx.xx (pv)
The Original Periodic Payment is: xxxx.xx (pmt)
The Original Future Value is: xxxx.xx (fv)
The Delayed Present Value is: xxxxxx.xx (pve)
The New Periodic Payment is: xxxx.xx (new_pmt)
The amortization options are:
|| Amortize with Original Present Value
Constant Payment to Principal: xxxxx.xx (cmpt1)
and final payment: xxxxx.xx (final_pmt_opt_1)
|| Amortize with Delayed Present Value
Constant Payment to Principal: xxxxx.xx (cmpt2)
and final payment: xxxxx.xx (final_pmt_opt_2)
|| Amortize with Original Transaction Values
and final payment: xxxxx.xx (final_pmt_opt_3)
|| Amortize with Delayed Present Value, Original Periodic Payment
and final payment: xxxxx.xx (final_pmt_opt_4)
|| Amortize with Delayed Present Value, New Periodic Payment
and final payment: xxxxx.xx (final_pmt_opt_5)
|| Amortize with Original Present Value, Original Periodic Payment
new number of total payments: xxx (new_n)
and final payment: xxxxx.xx (final_pmt_opt_6)
|----------------------------------------------------------------------------|
Note: I have included in parenthesis above the names of the variables
in the amortization structure, amort_sched, to be used in filling in
the appropriate values. Do not include the parenthesized infomation in
the GUI.
Note: the last option is available ONLY IF the variable "new_n" in the
amortization schedule structure is non-zero. If "new_n" is zero, the
last option could be "faded out" or left out completely.
Note that the options are mutually exclusive, picking one turns the
others off. Initially all should probably be off, thus forcing the
user to pick one.
Option 3 above is probably the one most likely to be selected by most
people. The others are more likely to be choosen as comparisons to
what is likely to be dictated by their lending institution which would
more than likely be option 3 (at least in the US).
I have laid out the above options to correspond to the values for the
variable "option" in the "amort_sched" structure. Any other sequence
could be used so long as the proper numbering is maintained for
"option".
Once a particular option, 1 to 6 inclusive, has been choosen above by
the user, they have one more choice to make: namely whether they want
an annual summary or a schedule of each payment. Also if "option" is
3, 4, 5 or 6 they also have the additional choices of whether they
want a fixed prepayment to principal or a variable prepayment to
principal schedule. If they choose a fixed prepayment schedule, then
they must enter the amount of the fixed prepayment. It's not as
complicated as it sounds. The dialog for these choices could look
something like:
|----------------------------------------------------------------------------|
Type of Amortization Schedule
|| Annual Summary
|| Per Payment Schedule
|| Variable Prepayment to Principal
|| Fixed Prepayment to Principal Schedule
==xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
|----------------------------------------------------------------------------|
Note that zero for the fixed prepayment is valid. The value entered
(or not entered as the case may be) should be set into the variable,
"fixed_pmt" in the amortization schedule structure. If a value for the
fixed prepayment is entered by the user, this value should probably be
set into "fixed_pmt", irregardless of whether the user selects the
fixed prepayment option.
Note that the options are mutually exclusive, picking one turns the
others off. Initially all should probably be off, thus forcing the
user to pick one.
The last two options would only be available if they had selected
options 3, 4, 5 or 6 from the amortization options dialog. The
information from this dialog would be used to set the variable
"summary" in the "amort_sched" structure. The permissable values are:
'y' -- indicates an annual summary schedule, first option above
'p' -- indicates a per payment schedule, option 2 above
'a' -- indicates a variable prepayment schedule, option 3 above
'f' -- indicates a fixed prepayment schedule, option four above.
I have provided a function, "amort_opt", in the file "amort_opt.c" to
demonstrate which information is used to fill in the appropriate
places in the above two GUIs. You can use this function as a guide.
Once all of that information has been gathered from the user,
the amortization schedule can be computed. The function
"Amortization_Schedule" is called and does the computation, but not
the display/output of the schedule. I orginally had the output of the
schedule integrated in this function, but decided that was a dumb
idea. Passing back a pointer in the "amort_sched" structure for
all of the computed values was a better idea. This way the actual
output/display of the schedule can be tailered as desired.
I have provided the function "amort_prt" in the file "amort_prt.c" as
a guide on how to use the structures and the information they contain
to display/output the desired schedule. Currently, the function
"prints" the information to a specified file. I could alter the
function to output HTML tables if desired. The changes would not be
very great. Also, a flag could be passed to indicate whether HTML or
plain text was desired. Or the function could be completely rewritten
to output some completely different format as desired.
The functions used for the amortization schedule are:
Amortization_init -- this functions initializes the information for
the schedule so that the user can decide which
schedule and which type is desired.
Amortization_Schedule -- this function computes the desired
amortization schedule.
Amortization_free -- this function is called to free the dynamically
allocated memory in the amortization schedule - it
does not free the amortization schedule structure passed,
but the schedule structures allocated by the call to
"Amortization_Schedule" and pointed to in the union
in the amortization schedule structure.
amort_opt - a function to display the options to the user and input which
schedule and which type is desired. Uses a text display - for
guidance in designing the eventual GUI used.
amort_prt - a function to print the desired schedule, again for guidance
in how to use the information computed for each type of schedule.
A typical sequence would be:
1: use the financial calculator functions to enter/calculate the
financial variables
2: copy the financial information from the financial information
structure to the amortization schedule structure
3: call the "Amortization_init" function to initialize the amortization
schedule
4: obtain from the user, schedule option and type -- "amort_opt" function
for command line version
5: call the "Amortization_Schedule" function to compute the desired
amortization schedule
6: display/output the desired amortization schedule -- "amort_prt"
function for the command line version
7: call the "Amortization_free" function to free memory in computed schedule
8: loop back to step 4 above for another schedule using same financial
data and dates
9: loop back to step 1 above for new financial data
Right now the documentation is written around the use of the financial
calculator in QTAwk, my personal implementation of a superset of awk,
nawk and gawk. The document, especially the examples section, will
have to be rewritten to a small extent for gnucash. I would like to
defer this until we have settled on the GUI since that will determine
the end document.
The examples illustrate the general nature of the calculator and the
many "seemingly" unrelated uses for it. I used CA Simply Money under
OS/2 for many years. They had several "calculators". At first I
thought that was great. Then I really looked at what they had - it was
really just many different GUIs for the same calculator, The same
calculator under different guises. I stopped using all of theirs very
quickly and developed my own since having it all in one was much
easier to understand. It also underscored the power of the finanacial
functions in everyday life.
<<<<<<<<<<------------------------------------------------------------>>>>>>>>
The only thing left to describe is the expression parser/evaluator.
The expression parser/evaluator is contained in the file
"expression_parser.c".
Before describing the parser per se, I want to describe the structures
used to contain the results returned from the parser. The structure is
defined in "finvar.h":
typedef struct var_store *var_store_ptr;
typedef struct var_store {
unsigned char *variable_name; /* variable name if variable,
NULL otherwise */
unsigned char use_flag; /* flag if variable has been assigned to */
unsigned char assign_flag; /* flag if variable is used */
void *value; /* pointer to imp[lementation defined
numeric value */
var_store_ptr next_var; /* pointer to next variable in linked
list */
} var_store;
The "use_flag" variable is for internal use of the parser and can be
ignored by the user. The "variable_name" variable possibly points to
a string containing the name of the value returned, a "variable
name". If NULL, then this is a temporary value. The "value" variable
points to a user defined structure containing the numeric value of the
variable.
In designing and writing the parser, I decided early on that the
parser should be an "expression parser/evaluator" and that the actual
arithmetic was the responsibility of the caller/user. In reading the
debate on "gnucash-devel" and realizing that the exact numeric
representation used in gnucash was very probably going to be changing
in the future, I decided that the parser should be totally independent
of the numeric representation used, and thus the exact details of how
the arithmetic was performed. To accomplish this, four functions are
supplied by the user/caller:
1: trans_numeric - this function translates the text string into a
numeric in the desired representation and returns a pointer to
the representation as a (void *) this function has three parameters
passed:
1: digit_str -- the actual text string of the numeric to be converted
to the internal representation
2: radix_point -- the ASCII character used to represent the radix point
3: rstr -- a pointer to a location in which to return a pointer to the
first character not part of the numeric string translated
If this pointer is NULL, do not return a value. This parameter
is the same as the second parameter of the standard C library
functions "strtod" or "strtol"
2: numeric_ops - this function does the actual arithmetic on two numeric
quantities in internal representation. It has three
parameters passed:
1: op_sym -- the numeric operation to be performed. The possible
values are defined in "finvar.h" and are:
ADD_OP - addition
SUB_OP - subtraction
DIV_OP - division
MUL_OP - multiplication
ASN_OP - assignment
2: left_value - the left hand operand of the binary operator
3: right_value - the right hand operand of the binary operator
Note: left_value and right_value are passed as
(void *). This function is responsible for casting
to the proper type to use.
Note: this function should make no assumptions about
overwriting or re-using either left_value or
right_value, except for ASN_OP. Both values passed
must be left unchanged by any operation except ASN_OP.
This function is also responsible for allocating/freeing
memory as necessary to perform the designated function
and returning the result.
I STRONGLY suggest that the result be returned in
dynamically allocated memory. If static memory is used,
the parser has no means of copying the returned result
or managing static memory to prevent overwriting
the result and invalidating the result.
3: negate_numeric - this function negates the value passed (as a (void *))
4: free_numeric - this function is responsible for freeing memory used by
the internal numeric representation.
I have included the file "numeric_ops.c" containing the above
functions for the usual "double" and "int" representation of
numerics. The functions perform integer or floating point operations
as appropriate for the string entered by the user. The division
operation is done in "double" since I do not think that anybody really
wants (9 / 2) to equal 4 instead of 4.5 for financial operations.
These functions use the structure defined in finvar.h:
typedef struct numeric *numeric_ptr;
typedef struct numeric {
unsigned char type; /* designates type of value */
union {
long int int_value; /* long integer value */
double dbl_value; /* double value */
} value;
} numeric;
to contain all numeric values. The variable "type" in this structure
can have the values:
INT_TYPE
DBL_TYPE
which are defined in "finvar.h".
All "named variables", variables defined by the user for storing
intermediate results for future reference/use, and temporary variables
used by the parser use the variable storage structure, var_store,
defined above. The result of parsing and evaluating the string passed
are returned in a variable storage structure specified by the caller.
If the returned variable value is not named, i.e., "variable_name ==
NULL", then the user/caller is responsible for freeing the memory used
by the internal representation of the numeric value. If, however,
"variable_name != NULL", freeing the memory used by the internal
numeric representation will cause a segmentation fault later, when
parser attempts to free the memory through a call to
the "free_numeric". In addition, freeing the memory will probably
invalidate the numeric value contained therein and lead to pernicuous
results when the value is used.
If "variable_name != NULL", the user/caller should never attempt to
free this memory, that is the sole responsibility of the parser.
It may be that the calling function has certain "variables" that need
to be "pre-defined" for the user to manipulate. The demo financial
calculator "pre-defines" the financial variables, "n, i, pv, pmt, fv,
CF, PF, disc and bep". I would suggest that the gnucash financial
calculator also pre-define these for direct manipulation by the user
if they so desire. Other modules of gnucash could pre-define other
variables with common names used in accounting if they so desire. This
would allow knowledgable users quick access to such values and to be
able to easly manipulate the values to obtain desired results. One
method of "pre-defining" variables is illustrated in the
"fin-interactive.c" file for the demo interactive financial
calculator. In essence the function "pre-defining" variables sets up
a linked list of variable storage structures with the proper "names"
and numeric values. The number of "pre-defined" variables and a
pointer to the structure array is passed to the parser in the
initialization call. After the parser is eventually exited, the
calling function is responsible for freeing any memory used by the
"pre-defined" variables and their final numeric representation.
I think the use of "named variables" and "pre-defined" variables could
become very useful in gnucash eventually as people get used to the
idea. It may be useful to allow users to define variables with values
convienent to them and that are persistent across invocations of
gnucash.
A second design goal of the parser was that it should be callable
concurrently by multiple modules within gnucash independently. That
each module should be capable of using differing "pre-defined"
variables and user defined variables and even internal numeric
representations. To that end the calling module must first initialize
the parser with a call to "init_parser". This call creates the parser
internal structure for subsequent calls to the parser proper. The
structure created and returned must then be passed to subsequent calls
to the parser. When no further calls to the parser are to be made,
the module then calls "exit_parser" with the pointer returned by
"init_parser", so that the parser may release dynamically allocated
memory.
The parser recognizes the following binary operators:
+
-
/
*
=
+=
-=
/=
*=
In addition, the unary operators
+
-
are recognized. All numerics are initially recognized as positive
numbers. If negative, the unary '-' operator is applied. This saves
the logic of having to recogize strings as
-123
The logic recognizes "-" and "123" separately. The '-' unary operator
is then applied to negate the numeric. This also has the advanatge
that the same logic can be used for
-123
+123.45
+uvar
-uvar
In each case, the appropriate unary operator is applied to obtain the
desired result with no increase in the parsing logic. Thus keeping
things as simple as possible.
The parser also follows the C practice that the assignment operators
return a value. Thus, allowing multiple assignments and assignment
within expressions. The following expressions are all valid:
nni = 123
hnk = nni = 23.45
jkl = 5 * (nj = 68.9)
The first time variables are used in an expression, they are
initialized to zero, 0. Thus, even if the following variables have not
been assigned a value previously, the following expressions are valid:
nni *= 123
above results in zero in nni
jk += 45.6
above results in 45.6 in jk
56.8 - tyh
result of above is 56.8
tgh - 45.7
above the same as
-45.7
After parsing the above expressions the variables nni, jk, tyh and tgh
would all be defined.
There are six functions needed to use the parser/evaluator:
Note: in the last five functions, in the function paramter (parse_env_ptr pe),
"pe" is the pointer returned by the "init_parser" function.
parser_env_ptr
init_parser(var_store_ptr predefined_vars,
unsigned char radix_point,
void *trans_numeric(unsigned char *digit_str,
unsigned char radix_point,
unsigned char **rstr),
void *numeric_ops(unsigned char op_sym,
void *left_value,
void *right_value),
void *negate_numeric(void *value),
void free_numeric(void *numeric_value));
This function is called by the gnucash module/function/whatever to
initialize the parser. The parser returns a pointer to a structure
that contains all relevant information for parsering strings. The
pointer is returned as (void *) since all information is and should
remain pertinent only to the parser. The calling function(s) should
never rely on manipulating any information inside this structure
directly, since it may and could change in the future.
-- The first parameter is a pointer to an array of "pre-defined"
variables the caller wishes to use with subsequent calls to
the parser.
-- The second parameter is the radix character to use in numeric
strings in subsequent calls to the parser. The parser needs this
information to recognize numeric strings of the form
".123", where '.' is the radix.
-- The third, fourth, fifth, and sixth parameters are the functions
I descibed above for the internal numeric representation desired
by the calling function(s).
void exit_parser(parser_env_ptr pe);
This function is called to exit the parser and free all dynamically
allocated memory used by the parser for an internal stack and user
defined variables.
unsigned get_parse_error(parser_env_ptr pe);
Whenever the parser encounters an error in parsing/evaluating a
string, it returns a NULL pointer instead of a valid pointer to a
variable storage structure. This call returns an unsigned integer
designating the error encountered. The possible values are defined in
the "finvar.h" file. A function "parse_error" has been defined in the
file "fin-interactive.c" illustrating how to use this function and
pin-point for the user the exact location of the error.
var_store_ptr get_vars(parser_env_ptr pe)
This function returns a pointer to a linked list of variable storage
structures containing the user defined named variables if any
exist. NULL is returned if none exist. An illustration of using this
function is contained in the "main" function in the
"fin-interactive.c" file. The calling function should not alter the
variable names. The numeric values may be altered if the calling
function really knows what it is doing.
unsigned delete_var(unsigned char *var_name,
parse_env_ptr pe);
This function will delete the user defined named variable with a name
identical to the name string passed in the first parameter. If no user
defined variable exists with an identical name, zero, 0, returned. If
the delete operation is successful, one, 1, is returned.
unsigned char *parse_string(var_store_ptr value,
unsigned char *string,
parser_env_ptr pe);
This function parses the string passed in the second parameter and
returns a pointer to the last character not recognized upon a parsing
error. If no error occurred, NULL is returned. The first parameter
specifies a variable storage structure to contain the result of the
parser/evaluator.
Note: The parser/evaluator uses a simple recursive descent parser.
I decided on this type for the simple reason that for a simple four
function calculator a recursive descent parser is, in my opnion, the
easiest to construct. I also think that recursive descent parsers are
easier for the human to understand and thus maintain. Since gnucash is
open source and will probably have many maintainers in its history,
the later point is probably the more relevant.
Also, the parser uses a stack which is dynamically allocated in memory
and can grow as needed. I have not provided any mechanism for
shrinking the stack. The initial stack size is set at 50. I really do
not anticipate that under normal and even most extreme cases, that it
will ever approach that size in actual use. Under "normal" operation,
the stack will probably never exceed 3 or 4 in size and 50 is probably
an overkill for normal use. However, since the stack is pointers and
not entire structures, a stack size of 50 is not that much memory and
can be tolerated by most users. Thus, a mechanism for shrinking the
stack will probably never be needed.

View File

@ -1,151 +0,0 @@
/***************************************************************************
amort_opt.c - description
-------------------
begin : Thursday June 15 2000
email : tboldt@attglobal.net
Author : Terry D. Boldt
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*
* Functions to determine amortizations options
* 7-2-2000
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "finvar.h"
#include "finproto.h"
#include "fin_spl_protos.h"
amort_sched_ptr amort_opt(
amort_sched_ptr amortsched,
void *parse_env)
{
char buffer[200], *errp;
unsigned long ii;
unsigned prec = amortsched->prec;
var_store value;
numeric_ptr nval;
struct tm *times_E,
*times_I;
/* print amortization options */
times_E = (struct tm *)calloc(1, sizeof(struct tm));
ii = amortsched->Eff_Date_jdn;
times_E->tm_mday = amortsched->day_E;
times_E->tm_mon = amortsched->month_E - 1;
times_E->tm_year = amortsched->year_E - 1900;
times_E->tm_wday = (ii + 1) % 7;
times_E->tm_yday = amortsched->yday_E;
times_I = (struct tm *)calloc(1, sizeof(struct tm));
ii = amortsched->Init_Date_jdn;
times_I->tm_mday = amortsched->day_I;
times_I->tm_mon = amortsched->month_I - 1;
times_I->tm_year = amortsched->year_I - 1900;
times_I->tm_wday = (ii + 1) % 7;
times_I->tm_yday = amortsched->yday_I;
printf("\n******************************");
qof_strftime(buffer, (size_t)50, "%c", times_E);
printf("\nEffective Date: %s\n", buffer);
qof_strftime(buffer, (size_t)50, "%c", times_I);
printf("Initial Payment Date: %s\n", buffer);
free(times_E);
free(times_I);
printf("The Original Present Value (pv) is: %.*f\n", (int)prec, amortsched->pv);
printf("The Original Periodic Payment (pmt) is: %.*f\n", (int)prec, amortsched->pmt);
printf("The Original Future Value (fv) is: %.*f\n", (int)prec, amortsched->fv);
printf("The Delayed Present Value (pve) is: %.*f\n", (int)prec, amortsched->pve);
printf("The New Periodic Payment (pmt) for pve is: %.*f\n\n", (int)prec, amortsched->new_pmt);
printf("The amortization options are:\n");
printf("1 -- Amortize with Original Amount and Constant Payment to Principal: %.*f\n", (int) prec, amortsched->cpmt1);
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_1);
printf("2 -- Amortize with Delayed Amount and Constant Payment to Principal: %.*f\n", (int)prec, amortsched->cpmt2);
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_2);
printf("3 -- Amortize with Original Transaction Values\n");
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_3);
printf("4 -- Amortize with Delayed Amount, Original Periodic Payment\n");
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_4);
printf("5 -- Amortize with Delayed Amount, New Periodic Payment\n");
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_5);
if ( amortsched->new_n )
{
printf("6 -- Amortize with Original Amount, Original Periodic Payment,\n");
printf(" new number of total payments (n): %u\n", amortsched->new_n);
printf(" and final payment: %.*f\n", (int)prec, amortsched->final_pmt_opt_6);
} /* endif */
printf("Enter choice 1, 2, 3, 4, 5 or 6: ");
fgets(buffer, 190, stdin);
amortsched->option = buffer[0] - '0';
printf("Amortization Schedule:\n");
printf("y -- Yearly Summary\n");
printf("p -- Periodic Payment\n");
if ( amortsched->option < 3 )
{
printf("Enter Choice y or p: ");
}
else
{
printf("f -- Fixed Advanced Payment\n");
printf("a -- Variable Advanced Payment\n");
printf("Enter Choice y, p, f or a: ");
} /* endif */
fgets(buffer, 190, stdin);
amortsched->summary = buffer[0];
if ( amortsched->summary == 'f' )
{
if ( amortsched->fixed_pmt != 0.0 )
{
printf("Current Fixed Prepayment: %.*f\nChange Fixed Prepayment? (y/n): ", (int)prec, amortsched->fixed_pmt);
fgets(buffer, 190, stdin);
}
else
{
buffer[0] = 'y';
} /* endif */
if ( buffer[0] == 'y' )
{
printf("Enter Fixed Prepayment Amount: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched->fixed_pmt = (double)(nval->value.int_value);
break;
case DBL_TYPE:
amortsched->fixed_pmt = nval->value.dbl_value;
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
} /* endif */
} /* endif */
return amortsched;
} /* amort_opt */

View File

@ -1,307 +0,0 @@
/***************************************************************************
amort_prt.c - description
-------------------
begin : Thursday June 15 2000
email : tboldt@attglobal.net
Author : Terry D. Boldt
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*
* Functions to print amortization schedules
* 6-15-2000
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <mcheck.h>
#include "finvar.h"
#include "finproto.h"
#include "fin_spl_protos.h"
void prt_amortization_schedule(
amort_sched_ptr amortsched, /* amortization schedule to print */
FILE *ofile) /* output file */
{
unsigned j,
jj,
prec = amortsched->prec,
option = amortsched->option,
fv_case = amortsched->fv_case;
unsigned char datel[100],
summary = amortsched->summary;
struct tm *times_E,
*times_I;
amort_sched_yr_ptr amortyr,
prst_yr;
sched_pmt_ptr pmtsched = NULL;
yearly_summary_ptr annual_summary;
times_E = (struct tm *)calloc(1, sizeof(struct tm));
times_E->tm_mday = amortsched->day_E;
times_E->tm_mon = amortsched->month_E - 1;
times_E->tm_year = amortsched->year_E - 1900;
times_E->tm_wday = (amortsched->Eff_Date_jdn + 1) % 7;
times_E->tm_yday = amortsched->yday_E;
times_I = (struct tm *)calloc(1, sizeof(struct tm));
times_I->tm_mday = amortsched->day_I;
times_I->tm_mon = amortsched->month_I - 1;
times_I->tm_year = amortsched->year_I - 1900;
times_I->tm_wday = (amortsched->Init_Date_jdn + 1) % 7;
times_I->tm_yday = amortsched->yday_I;
fprintf(ofile, "Amortization Table\n");
qof_strftime(datel, (size_t)100, "%c", times_E);
fprintf(ofile, "Effective Date: %s\n", datel);
qof_strftime(datel, (size_t)100, "%c", times_I);
fprintf(ofile, "Initial Payment Date: %s\n", datel);
fprintf(ofile, "Compounding Frequency per year: %u\n", amortsched->CF);
fprintf(ofile, "Payment Frequency per year: %u\n", amortsched->PF);
fprintf(ofile, "Compounding: %s\n", (amortsched->disc ? "Discrete" : "Continuous"));
fprintf(ofile, "Payments: %s\n", (amortsched->bep ? "Beginning of Period" : "End of Period"));
fprintf(ofile, "Payments (%u): %.*f\n", amortsched->n - 1, (int)prec, (option < 3) ? amortsched->cpmt : (option == 5) ? amortsched->new_pmt : amortsched->pmt);
fprintf(ofile, "Final payment (%u): %.*f\n", amortsched->n, (int)prec, amortsched->final_pmt);
if ( (amortsched->CF == 1) && (amortsched->PF == 1) ) fprintf(ofile, "Nominal Interest per Payment Period: %g\t(Annualized: %g)\n", amortsched->nint, amortsched->nint * 12);
else fprintf(ofile, "Nominal Annual Interest Rate: %g\n", amortsched->nint);
fprintf(ofile, " Effective Interest Rate Per Payment Period: %g\n", amortsched->eint);
fprintf(ofile, "Present Value: %.*f\n", (int)prec, amortsched->pv);
if ( (amortsched->option == 2) || (amortsched->option > 3) )
{
fprintf(ofile, "Interest due to Delayed Intial Payment: %.*f\n", (int)prec, amortsched->delayed_int);
} /* endif */
free(times_E);
free(times_I);
if ( amortsched->option < 3 )
{
summary = (summary == 'y') ? 'x' : 'o';
} /* endif */
switch ( summary )
{
case 'a':
/* variable prepayment schedule
*/
fprintf(ofile, "Advanced Prepayment Amortization - Variable Prepayment\n");
amortyr = amortsched->schedule.first_yr;
for ( j = amortsched->total_periods , jj = 0 ; j && amortyr ; j-- )
{
if ( !jj )
{
fprintf(ofile, "Pmt * Interest Principal Prepay Total Pmt Balance\n");
pmtsched = amortyr->payments;
jj = amortyr->num_periods;
} /* endif */
fprintf(ofile, "%4u %12.*f %12.*f %12.*f %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->principal,
(int)prec, pmtsched->advanced_pmt,
(int)prec, pmtsched->total_pmt,
(int)prec, pmtsched->balance);
if ( !--jj )
{
fprintf(ofile, "Summary for: %u:\n", amortyr->year);
fprintf(ofile, " Interest Paid: %.*f\n", (int)prec, amortyr->interest_pd);
fprintf(ofile, " Principal Paid: %.*f\n", (int)prec, amortyr->principal_pd);
fprintf(ofile, " Year Ending Balance: %.*f\n", (int)prec, amortyr->yr_end_balance);
fprintf(ofile, " Sum of Interest Paid: %.*f\n", (int)prec, amortyr->total_interest_pd);
prst_yr = amortyr;
amortyr = amortyr->next_yr;
}
else
{
pmtsched++;
} /* endif */
} /* endfor */
break;
case 'f':
/* fixed prepayment schedule
*/
fprintf(ofile, "Advanced Prepayment Amortization - Fixed Prepayment: %.*f\n", (int)prec, amortsched->fixed_pmt);
amortyr = amortsched->schedule.first_yr;
for ( j = amortsched->total_periods , jj = 0 ; j && amortyr ; j-- )
{
if ( !jj )
{
fprintf(ofile, "Pmt * Interest Principal Prepay Total Pmt Balance\n");
pmtsched = amortyr->payments;
jj = amortyr->num_periods;
} /* endif */
fprintf(ofile, "%4u %12.*f %12.*f %12.*f %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->principal,
(int)prec, pmtsched->advanced_pmt,
(int)prec, pmtsched->total_pmt,
(int)prec, pmtsched->balance);
if ( !--jj )
{
fprintf(ofile, "Summary for: %u:\n", amortyr->year);
fprintf(ofile, " Interest Paid: %.*f\n", (int)prec, amortyr->interest_pd);
fprintf(ofile, " Principal Paid: %.*f\n", (int)prec, amortyr->principal_pd);
fprintf(ofile, " Year Ending Balance: %.*f\n", (int)prec, amortyr->yr_end_balance);
fprintf(ofile, " Sum of Interest Paid: %.*f\n", (int)prec, amortyr->total_interest_pd);
prst_yr = amortyr;
amortyr = amortyr->next_yr;
}
else
{
pmtsched++;
} /* endif */
} /* endfor */
break;
case 'o':
/* constant payment to principal
*/
fprintf(ofile, "Constant Payment to Principal: %.*f\n", (int)prec, amortsched->cpmt);
amortyr = amortsched->schedule.first_yr;
for ( j = amortsched->total_periods , jj = 0 ; j && amortyr ; j-- )
{
if ( !jj )
{
fprintf(ofile, "Pmt# Interest Total Payment Balance\n");
pmtsched = amortyr->payments;
jj = amortyr->num_periods;
} /* endif */
fprintf(ofile, "%4u %12.*f %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->total_pmt,
(int)prec, pmtsched->balance);
if ( !--jj )
{
fprintf(ofile, "Summary for: %u:\n", amortyr->year);
fprintf(ofile, " Interest Paid: %.*f\n", (int)prec, amortyr->interest_pd);
fprintf(ofile, " Principal Paid: %.*f\n", (int)prec, amortyr->principal_pd);
fprintf(ofile, " Year Ending Balance: %.*f\n", (int)prec, amortyr->yr_end_balance);
fprintf(ofile, " Sum of Interest Paid: %.*f\n", (int)prec, amortyr->total_interest_pd);
prst_yr = amortyr;
amortyr = amortyr->next_yr;
}
else
{
pmtsched++;
} /* endif */
} /* endfor */
break;
case 'p':
/* normal payment schedule
*/
fprintf(ofile, "Normal Amortization Schedule\n");
amortyr = amortsched->schedule.first_yr;
for ( j = amortsched->total_periods - 1 , jj = 0 ; j && amortyr ; j-- )
{
if ( !jj )
{
fprintf(ofile, amortsched->fv_case ? "Pmt * Interest Balance\n" : "Pmt * Interest Principal Balance\n");
pmtsched = amortyr->payments;
jj = amortyr->num_periods;
} /* endif */
if ( fv_case )
{
fprintf(ofile, "%4u %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->balance);
}
else
{
fprintf(ofile, "%4u %12.*f %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->principal,
(int)prec, pmtsched->balance);
} /* endif */
if ( !--jj )
{
fprintf(ofile, "Summary for: %u:\n", amortyr->year);
fprintf(ofile, " Interest Paid: %.*f\n", (int)prec, amortyr->interest_pd);
if ( !fv_case ) fprintf(ofile, " Principal Paid: %.*f\n", (int)prec, amortyr->principal_pd);
fprintf(ofile, " Year Ending Balance: %.*f\n", (int)prec, amortyr->yr_end_balance);
fprintf(ofile, " Sum of Interest Paid: %.*f\n", (int)prec, amortyr->total_interest_pd);
prst_yr = amortyr;
amortyr = amortyr->next_yr;
}
else
{
pmtsched++;
} /* endif */
} /* endfor */
if ( !jj )
{
fprintf(ofile, amortsched->fv_case ? "Pmt * Interest Balance\n" : "Pmt * Interest Principal Balance\n");
pmtsched = amortyr->payments;
} /* endif */
fprintf(ofile, "Final Payment: %.*f\n", (int)prec, amortyr->final_pmt);
if ( fv_case )
{
fprintf(ofile, "%4u %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->balance);
}
else
{
fprintf(ofile, "%4u %12.*f %12.*f %12.*f\n",
pmtsched->period_num,
(int)prec, pmtsched->interest,
(int)prec, pmtsched->principal,
(int)prec, pmtsched->balance);
} /* endif */
fprintf(ofile, "Summary for: %u:\n", amortyr->year);
fprintf(ofile, " Interest Paid: %.*f\n", (int)prec, amortyr->interest_pd);
if ( !fv_case ) fprintf(ofile, " Principal Paid: %.*f\n", (int)prec, amortyr->principal_pd);
fprintf(ofile, " Year Ending Balance: %.*f\n", (int)prec, amortyr->yr_end_balance);
fprintf(ofile, " Sum of Interest Paid: %.*f\n", (int)prec, amortyr->total_interest_pd);
break;
case 'x':
/* constant payment to principal - annual summary
*/
case 'y':
/* normal payment - annual summary
*/
if ( summary == 'x' ) fprintf(ofile, "Annual Summary - Constant Payment to Principal: %.*f\n", (int)prec, amortsched->cpmt);
else fprintf(ofile, "Annual Summary - Normal Amortization\n");
fprintf(ofile, "Year Interest Ending Balance\n");
annual_summary = amortsched->schedule.summary;
for ( j = amortsched->total_periods , jj = 0 ; j ; j-- , jj++ )
{
fprintf(ofile, "%4u %12.*f %12.*f\n",
annual_summary[jj].year,
(int)prec, annual_summary[jj].interest,
(int)prec, annual_summary[jj].end_balance);
} /* endfor */
break;
} /* endswitch */
fprintf(ofile, "\nTotal Interest: %.*f\n", (int)prec, amortsched->total_interest);
} /* prt_amortization_schedule */

View File

@ -1,600 +0,0 @@
/***************************************************************************
fin-interactive.c - description
-------------------
begin : Thursday June 15 2000
email : tboldt@attglobal.net
Author : Terry D. Boldt
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*
* Functions to interact with user and call financial equations
* 6-22-2000
*
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <mcheck.h>
#include "finvar.h"
#include "finproto.h"
#include "fin_spl_protos.h"
#include "numeric_ops.h"
static void prt_status(
fi_ptr fi,
FILE *ofile);
static void set_fin_vars(
void);
static void unset_fin_vars(
void);
#define PREDEFINED_FIN_VARS 9
/* define local financial variables
*/
static unsigned npp;
static double ir;
static double pv;
static double pmt;
static double fv;
static unsigned CF;
static unsigned PF;
static unsigned disc;
static unsigned bep;
/* define local variable for roundoff precesion
* default here to value for US currency
*/
static unsigned prec = 2;
/* declare array of structures for local financial variables
*/
static var_store predefined_fin_vars[PREDEFINED_FIN_VARS];
/* declare array of finacial varibale names used by user to access financial variables
*/
static char *fin_var_names[] =
{
"n",
"i",
"pv",
"pmt",
"fv",
"CF",
"PF",
"disc",
"bep",
};
/* declare array of financial variables
*/
static void *fin_vars[] =
{
(void *)&npp,
(void *)&ir,
(void *)&pv,
(void *)&pmt,
(void *)&fv,
(void *)&CF,
(void *)&PF,
(void *)&disc,
(void *)&bep,
};
/* declare array of financial variable basic numeric types
*/
static char fin_type[] =
{
INT_TYPE,
DBL_TYPE,
DBL_TYPE,
DBL_TYPE,
DBL_TYPE,
INT_TYPE,
INT_TYPE,
INT_TYPE,
INT_TYPE,
};
static char sl_commands[] = "acdqsv";
/* function to set local financial variables into array for use by expression parser
* as pre-defined variables
*/
static void set_fin_vars(
void)
{
unsigned cntr;
numeric_ptr value;
for ( cntr = 0 ; cntr < PREDEFINED_FIN_VARS ; cntr++ )
{
predefined_fin_vars[cntr].variable_name = fin_var_names[cntr];
predefined_fin_vars[cntr].assign_flag = EOS;
predefined_fin_vars[cntr].value = value = (numeric_ptr)calloc(1, sizeof(numeric));
predefined_fin_vars[cntr].next_var = &predefined_fin_vars[cntr + 1];
switch ( value->type = fin_type[cntr] )
{
case INT_TYPE:
value->value.int_value = *(unsigned *)(fin_vars[cntr]);
break;
case DBL_TYPE:
value->value.dbl_value = *(double *)(fin_vars[cntr]);
break;
} /* endswitch */
} /* endfor */
predefined_fin_vars[PREDEFINED_FIN_VARS - 1].next_var = NULL;
} /* set_fin_vars */
/* free storage used by local financial variables
*/
static void unset_fin_vars(
void)
{
unsigned cntr;
numeric_ptr value;
for ( cntr = 0 ; cntr < PREDEFINED_FIN_VARS ; cntr++ )
{
free(predefined_fin_vars[cntr].value);
} /* endfor */
} /* unset_fin_vars */
/* check variable set by expression parser against local financial variables
* and update local values as necessary. Also convert variables to proper type
* to reflect the native type of the local variable
*/
void chk_vars(
var_store_ptr predefined_vars,
void **var_array,
char *var_type,
unsigned var_cnt)
{
unsigned cntr;
numeric_ptr value;
for ( cntr = 0 ; cntr < var_cnt ; cntr++ )
{
if ( predefined_vars[cntr].assign_flag == ASSIGNED_TO )
{
predefined_vars[cntr].assign_flag = EOS;
value = (numeric_ptr)(predefined_vars[cntr].value);
switch ( var_type[cntr] )
{
case INT_TYPE:
switch ( value->type )
{
case INT_TYPE:
*(int *)(var_array[cntr]) = value->value.int_value;
break;
case DBL_TYPE:
value->value.int_value =
*(int *)(var_array[cntr]) = (unsigned)(value->value.dbl_value);
value->type = INT_TYPE;
break;
} /* endswitch */
break;
case DBL_TYPE:
switch ( value->type )
{
case INT_TYPE:
value->value.dbl_value =
*(double *)(var_array[cntr]) = (double)(value->value.int_value);
value->type = DBL_TYPE;
break;
case DBL_TYPE:
*(double *)(var_array[cntr]) = value->value.dbl_value;
break;
} /* endswitch */
break;
} /* endswitch */
} /* endif */
} /* endfor */
} /* chk_fin_vars */
/* error encountered by expression parser - output error message
* and offending string
*/
void parse_error(unsigned error_code,
char *buf_start,
char *buf_err)
{
char *err_str;
unsigned bc = (unsigned)(buf_err - buf_start);
switch ( error_code )
{
case UNBALANCED_PARENS:
err_str = "Unbalanced Parenthesis\n";
break;
case STACK_OVERFLOW:
err_str = "Stack Overflow\n";
break;
case STACK_UNDERFLOW:
err_str = "Stack Underflow\n";
break;
case UNDEFINED_CHARACTER:
err_str = "Unrecognized Character\n";
break;
case NOT_A_VARIABLE:
err_str = "Need a Variable on Left side of assignment operator, '='\n";
break;
case NOT_A_FUNC:
err_str = "Need a valid Function name.\n";
break;
} /* endswitch */
printf(err_str);
printf("%s\n", buf_start);
if ( bc ) for ( bc - 1 ; bc ; bc-- ) printf(" ");
printf("^");
/* printf("%s\n",buf_err + 1); */
printf("\n");
} /* parse_error */
int main(int argc, char **argv, char **env)
{
char buffer[200], *errp;
size_t sbuf;
size_t retcnt;
var_store value;
var_store_ptr value_list;
numeric_ptr nval;
unsigned compute,
jj,
yrE,
monthE,
dayE,
yrI,
monthI,
dayI;
struct tm *times_E,
*times_I;
void *parse_env;
amort_sched amortsched;
financial_info fininfo;
/* check dynamic storage allocation
*/
/* mtrace(); */
set_default(&fininfo);
set_fin_vars();
parse_env = init_parser(predefined_fin_vars,
'.',
',',
trans_numeric,
numeric_ops,
negate_numeric,
free_numeric);
npp = fininfo.npp;
ir = fininfo.ir;
pv = fininfo.pv;
pmt = fininfo.pmt;
fv = fininfo.fv;
CF = fininfo.CF;
PF = fininfo.PF;
disc = fininfo.disc;
bep = fininfo.bep;
fininfo.prec = prec;
printf("Single Letter Commands:\na -- amortization schedule\nc -- compute financial variable\nd -- delete variable\ns -- output financial variable status\nq -- quit\nv -- list defined variables\n");
for (;;)
{
printf("<>");
retcnt = strlen(fgets(buffer, 190, stdin));
if ( (retcnt == 2) && (strchr(sl_commands, buffer[0]) != NULL) )
{
if ( buffer[0] == 'q' ) break;
amortsched.prec = fininfo.prec;
switch ( buffer[0] )
{
case 'a':
if ( amortsched.Eff_Date_jdn && amortsched.Init_Date_jdn )
{
printf("Current Effective year: %u\nCurrent Effective month: %u\nCurrent Effective day: %u\nCurrent Initial year: %u\nCurrent Initial month: %u\nCurrent Initial day %u\n",
amortsched.year_E,
amortsched.month_E,
amortsched.day_E,
amortsched.year_I,
amortsched.month_I,
amortsched.day_I);
printf("Change dates ? (y/n) ");
fgets(buffer, 190, stdin);
}
else
{
buffer[0] = 'y';
} /* endif */
if ( buffer[0] == 'y' )
{
printf("Enter Effective Date - year: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.year_E = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.year_E = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
printf("Enter Effective Date - month: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.month_E = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.month_E = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
printf("Enter Effective Date - day: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.day_E = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.day_E = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
printf("Enter Initial Payment Date - year: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.year_I = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.year_I = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
printf("Enter Initial Payment Date - month: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.month_I = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.month_I = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
printf("Enter Initial Payment Date - day: ");
fgets(buffer, 190, stdin);
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
amortsched.day_I = nval->value.int_value;
break;
case DBL_TYPE:
amortsched.day_I = (unsigned)(nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
} /* endif */
amortsched.n = npp;
amortsched.nint = ir;
amortsched.pv = pv;
amortsched.pmt = pmt;
amortsched.fv = fv;
amortsched.CF = CF;
amortsched.PF = PF;
amortsched.disc = disc;
amortsched.bep = bep;
Amortization_init(&amortsched);
amort_opt(&amortsched, parse_env);
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
Amortization_free(&amortsched);
break;
case 'c':
printf("Compute:\nn - 1\ni - 2\npv - 3\npmt - 4\nfv - 5\n1, 2, 3, 4 or 5: ");
retcnt = strlen(fgets(buffer, 190, stdin));
compute = buffer[0] - '0';
switch ( compute-- )
{
case 0: /* all values specified nothing to compute */
break;
case 1: /* compute number of periods, npp */
printf("Computing numbor of periods\n");
npp = fi_calc_num_payments(&fininfo);
printf("Number of Periods: %u\n", npp);
nval = (numeric_ptr)(predefined_fin_vars[compute].value);
nval->value.int_value = npp;
break;
case 2: /* compute interest, ir */
printf("Computing interest rate\n");
ir = fi_calc_interest(&fininfo);
printf("Nominal Interest Rate: %.*f\n", prec, ir);
nval = (numeric_ptr)(predefined_fin_vars[compute].value);
nval->value.dbl_value = ir;
break;
case 3: /* compute present value, pv */
printf("Computing Present Value\n");
pv = fi_calc_present_value(&fininfo);
printf("Present Value: %.*f\n", prec, pv);
nval = (numeric_ptr)(predefined_fin_vars[compute].value);
nval->value.dbl_value = pv;
break;
case 4: /* compute periodic payment, pmt */
printf("Computing periodic payment\n");
pmt = fi_calc_payment(&fininfo);
printf("Periodic Payment: %.*f\n", prec, pmt);
nval = (numeric_ptr)(predefined_fin_vars[compute].value);
nval->value.dbl_value = pmt;
break;
case 5: /* compute future value, fv */
printf("Computing Future Value\n");
fv = fi_calc_future_value(&fininfo);
printf("Future Value: %.*f\n", prec, fv);
nval = (numeric_ptr)(predefined_fin_vars[compute].value);
nval->value.dbl_value = fv;
break;
default: /* whoops */
break;
} /* endswitch */
break;
case 'd':
printf("Enter name of variable to delete: ");
retcnt = strlen(fgets(buffer, 190, stdin));
buffer[retcnt - 1] = EOS;
if ( !delete_var(buffer, parse_env) )
{
printf("Unable to delete specified variable\n");
} /* endif */
break;
case 's':
prt_status(&fininfo,
stdout);
break;
case 'v':
for ( value_list = parser_get_vars(parse_env) ; value_list ; value_list = value_list->next_var )
{
printf("%s: ", value_list->variable_name);
nval = (numeric_ptr)(value_list->value);
switch ( nval->type )
{
case INT_TYPE:
printf("%i\n", nval->value.int_value);
break;
case DBL_TYPE:
printf("%.*f\n", prec, nval->value.dbl_value);
break;
} /* endswitch */
} /* endfor */
break;
} /* endswitch */
}
else if ( retcnt > 1 )
{
buffer[retcnt - 1] = EOS;
if ( (errp = parse_string(&value, buffer, parse_env)) == NULL )
{
if ( value.variable_name ) printf("Variable: %s\n", value.variable_name);
nval = (numeric_ptr)(value.value);
switch ( nval->type )
{
case INT_TYPE:
printf("Evaluated Value: %i\n", nval->value.int_value);
break;
case DBL_TYPE:
printf("Evaluated Value: %.*f\n", prec, nval->value.dbl_value);
break;
} /* endswitch */
if ( !value.variable_name ) free_numeric(value.value);
chk_vars(predefined_fin_vars, fin_vars, fin_type, PREDEFINED_FIN_VARS);
fininfo.npp = npp;
fininfo.ir = ir;
fininfo.pv = pv;
fininfo.pmt = pmt;
fininfo.fv = fv;
fininfo.CF = CF;
fininfo.PF = PF;
fininfo.disc = disc;
fininfo.bep = bep;
}
else
{
parse_error(get_parse_error(parse_env), buffer, errp);
} /* endif */
} /* endif */
} /* endfor */
exit_parser(parse_env);
unset_fin_vars();
} /* main */
static void prt_status(
fi_ptr fi,
FILE *ofile)
{
fprintf(ofile, "<================================>\nCurrent Financial Calculator Status:\n");
fprintf(ofile, "Compounding Frequency: (CF) %u\n", fi->CF);
fprintf(ofile, "Payment Frequency: (PF) %u\n", fi->PF);
fprintf(ofile, "Compounding: %s\n", fi->disc ? "Discrete (disc = TRUE)" : "Continuous (disc = FALSE)");
fprintf(ofile, "Payments: %s\n", fi->bep ? "Beginning of Period (bep = TRUE)" : "End of Period (bep = FALSE)");
if ( fi->npp > 12 ) fprintf(ofile, "Number of Payment Periods (n): %u\t\t(Years: %u)\n", fi->npp, fi->npp / fi->PF);
else fprintf(ofile, "Number of Payment Periods (n): %u\n", fi->npp);
if ((fi->CF == 1) && (fi->PF == 1) ) fprintf(ofile, "Nominal Interest per Payment Period (i): %f\t(Annualized: %.*f\n", fi->ir, fi->prec, fi->ir * 12);
else fprintf(ofile, "Nominal Annual Interest Rate (i): %.*f\n", fi->prec, fi->ir);
/* fprintf(ofile, " Effective Interest Rate Per Payment Period: %f\n",eff_int(nint/100.0,CF,PF)); */
fprintf(ofile, "Present Value (pv): %.*f\n", fi->prec, fi->pv);
fprintf(ofile, "Periodic Payment (pmt): %.*f\n", fi->prec, fi->pmt);
fprintf(ofile, "Future Value (fv): %.*f\n<================================>\n", fi->prec, fi->fv);
} /* prt_status */

View File

@ -1,160 +0,0 @@
/***************************************************************************
fin-main.c - description
-------------------
begin : Thursday June 15 2000
email : tboldt@attglobal.net
Author : Terry D. Boldt
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*
* Functions to call financial equations and output results
* 6-15-2000
*
*/
#include <stdio.h>
#include "finvar.h"
#include "finproto.h"
static void prt_status(
fi_ptr fi,
FILE *ofile);
int main(int argc, char **argv, char **env)
{
financial_info fininfo;
amort_sched amortsched;
set_default(&fininfo);
fininfo.prec = 2;
fininfo.npp = 360;
fininfo.ir = 8.25;
fininfo.pv = 345725.0;
(void)fi_calc_payment(&fininfo);
printf("With npp == %u\n ir == %.*f\n pv == %.*f\n", fininfo.npp, fininfo.prec, fininfo.ir, fininfo.prec, fininfo.pv);
printf("------------>Compute pmt: -2597.32\n");
prt_status(&fininfo,
stdout);
fi_calc_interest(&fininfo);
printf("\n------------>Compute ir\n");
prt_status(&fininfo,
stdout);
fi_calc_num_payments(&fininfo);
printf("\n------------>Compute npp\n");
prt_status(&fininfo,
stdout);
fi_calc_future_value(&fininfo);
printf("\n------------>Compute fv\n");
prt_status(&fininfo,
stdout);
set_default(&fininfo);
fininfo.npp = 360;
fininfo.ir = 8.25;
fininfo.pv = 345725.0;
fi_calc_payment(&fininfo);
printf("\n\n Reset financial variables and compute amortization schedules.\n");
printf("First Schedule - ignore delay in first payment and\noutput annual summary\n");
amortsched.n = fininfo.npp;
amortsched.nint = fininfo.ir;
amortsched.pv = fininfo.pv;
amortsched.pmt = fininfo.pmt;
amortsched.fv = fininfo.fv;
amortsched.CF = fininfo.CF;
amortsched.PF = fininfo.PF;
amortsched.disc = fininfo.disc;
amortsched.bep = fininfo.bep;
amortsched.prec = fininfo.prec;
amortsched.year_E = 1999;
amortsched.month_E = 6;
amortsched.day_E = 15;
amortsched.year_I = 1999;
amortsched.month_I = 8;
amortsched.day_I = 1;
amortsched.fixed_pmt = -400;
(void)Amortization_init(&amortsched);
amortsched.option = 3;
amortsched.summary = 'y';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nSecond Schedule - ignore delay in first payment and\noutput schedule for each payment\n");
amortsched.summary = 'p';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nThird Schedule - ignore delay in first payment and\noutput variable advanced prepayment schedule\n");
amortsched.summary = 'a';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nFourth Schedule - ignore delay in first payment and\noutput fixed prepayment schedule\n");
amortsched.summary = 'f';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nFifth Schedule - use new payments due to delay and\noutput annual summary\n");
amortsched.option = 5;
amortsched.summary = 'y';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nSixth Schedule - use new payments due to delay and\noutput periodic payment schedule\n");
amortsched.option = 5;
amortsched.summary = 'p';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nSeventh Schedule - use new payments due to delay and\noutput variable prepayment schedule\n");
amortsched.option = 5;
amortsched.summary = 'a';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
printf("\n\nEighth Schedule - use new payments due to delay and\noutput fixed prepayment schedule\n");
amortsched.option = 5;
amortsched.summary = 'f';
(void)Amortization_Schedule(&amortsched);
prt_amortization_schedule(&amortsched, stdout);
Amortization_free(&amortsched);
} /* main */
static void prt_status(
fi_ptr fi,
FILE *ofile)
{
fprintf(ofile, "<================================>\nCurrent Financial Calculator Status:\n");
fprintf(ofile, "Compounding Frequency: (CF) %u\n", fi->CF);
fprintf(ofile, "Payment Frequency: (PF) %u\n", fi->PF);
fprintf(ofile, "Compounding: %s\n", fi->disc ? "Discrete (disc = TRUE)" : "Continuous (disc = FALSE)");
fprintf(ofile, "Payments: %s\n", fi->bep ? "Beginning of Period (bep = TRUE)" : "End of Period (bep = FALSE)");
if ( fi->npp > 12 ) fprintf(ofile, "Number of Payment Periods (n): %u\t\t(Years: %u)\n", fi->npp, fi->npp / fi->PF);
else fprintf(ofile, "Number of Payment Periods (n): %u\n", fi->npp);
if ((fi->CF == 1) && (fi->PF == 1) ) fprintf(ofile, "Nominal Interest per Payment Period (i): %f\t(Annualized: %.*f\n", fi->ir, fi->prec, fi->ir * 12);
else fprintf(ofile, "Nominal Annual Interest Rate (i): %.*f\n", fi->prec, fi->ir);
/* fprintf(ofile, " Effective Interest Rate Per Payment Period: %f\n",eff_int(nint/100.0,CF,PF)); */
fprintf(ofile, "Present Value (pv): %.*f\n", fi->prec, fi->pv);
fprintf(ofile, "Periodic Payment (pmt): %.*f\n", fi->prec, fi->pmt);
fprintf(ofile, "Future Value (fv): %.*f\n", fi->prec, fi->fv);
} /* prt_status */

View File

@ -1,26 +0,0 @@
#! /bin/sh
#/***************************************************************************
# fin-proto.sh - description
# -------------------
# copyright : (C) 2000 by Terry D. Boldt
# email : tboldt@attglobal.net
# Author : Terry D. Boldt
# ***************************************************************************/
#
#/***************************************************************************
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU General Public License as published by *
# * the Free Software Foundation; either version 2 of the License, or *
# * (at your option) any later version. *
# * *
# ***************************************************************************/
#
# shell script for creating function prototype files
#
qtgrep -DHhf cfuncs.exp fin.c expression_parser.c numeric_ops.c amort_opt.c amort_prt.c >protos.out
echo Creating Global Prototype File: \"finproto.h\"
qtawk -f protos.exp protos.out >finproto.h
echo Creating Static Prototype File: \"fin_static_proto.h\"
qtawk -f static_protos.exp protos.out >fin_static_proto.h
rm -fv protos.out

View File

@ -1,36 +0,0 @@
n
12*30
14.75
100000
4
n
y
y
opmt=pmt
n
12*15
4
n
y
y
pmt-opmt
n
12*30
0
y

View File

@ -1,103 +0,0 @@
Evaluate expression (y/n): Current Value, n: 0
Enter new value
Current Value, i: 0.00
Enter new value
Current Value, pv: 0.00
Enter new value
Current Value, pmt: 0.00
Enter new value
Current Value, fv: 0.00
Enter new value
Current Value, CF: 12
Enter new value
Current Value, PF: 12
Enter new value
Compute:
n - 1
i - 2
pv - 3
pmt - 4
fv - 5
1,2,3,4 or 5:Computing periodic payment
<================================>
Current Financial Calculator Status:
Compounding Frequency: (CF) 12
Payment Frequency: (PF) 12
Compounding: Discrete (disc = TRUE)
Payments: End of Period (bep = FALSE)
Number of Payment Periods (n): 360 (Years: 30)
Nominal Annual Interest Rate (i): 14.75
Present Value (pv): 100000.00
Periodic Payment (pmt): -1244.48
Future Value (fv): 0.00
Compute Amortization Schedule (y/n)
Another example (y/n): Evaluate expression (y/n): Enter Expression
Evaluated Value: -1244.48
Another expression (y/n): Current Value, n: 360
Enter new value
Current Value, i: 14.75
Enter new value
Current Value, pv: 100000.00
Enter new value
Current Value, pmt: -1244.48
Enter new value
Current Value, fv: 0.00
Enter new value
Current Value, CF: 12
Enter new value
Current Value, PF: 12
Enter new value
Compute:
n - 1
i - 2
pv - 3
pmt - 4
fv - 5
1,2,3,4 or 5:Computing periodic payment
<================================>
Current Financial Calculator Status:
Compounding Frequency: (CF) 12
Payment Frequency: (PF) 12
Compounding: Discrete (disc = TRUE)
Payments: End of Period (bep = FALSE)
Number of Payment Periods (n): 180 (Years: 15)
Nominal Annual Interest Rate (i): 14.75
Present Value (pv): 100000.00
Periodic Payment (pmt): -1382.50
Future Value (fv): 0.00
Compute Amortization Schedule (y/n)
Another example (y/n): Evaluate expression (y/n): Enter Expression
Evaluated Value: -138.02
Another expression (y/n): Current Value, n: 180
Enter new value
Current Value, i: 14.75
Enter new value
Current Value, pv: 100000.00
Enter new value
Current Value, pmt: -1382.50
Enter new value
Current Value, fv: 0.00
Enter new value
Current Value, CF: 12
Enter new value
Current Value, PF: 12
Enter new value
Compute:
n - 1
i - 2
pv - 3
pmt - 4
fv - 5
1,2,3,4 or 5:<================================>
Current Financial Calculator Status:
Compounding Frequency: (CF) 12
Payment Frequency: (PF) 12
Compounding: Discrete (disc = TRUE)
Payments: End of Period (bep = FALSE)
Number of Payment Periods (n): 180 (Years: 15)
Nominal Annual Interest Rate (i): 14.75
Present Value (pv): 100000.00
Periodic Payment (pmt): -1382.50
Future Value (fv): 0.00
Compute Amortization Schedule (y/n)
Another example (y/n):

View File

@ -1,439 +0,0 @@
/***************************************************************************
numeric_ops.c - description
-------------------
begin : Wednesday June 21 2000
email : tboldt@attglobal.net
Author : Terry D. Boldt
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*
* Functions to execute arthmetic operators on integer and double operands
* 6-23-2000
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#define NUMERIC_OPS_STATICS
#include "finvar.h"
static double neg_table[] =
{
1e-256,
1e-128,
1e-64,
1e-32,
1e-16,
1e-8,
1e-4,
1e-2,
1e-1,
1.0
};
static double pos_table[] =
{
1e+256,
1e+128,
1e+64,
1e+32,
1e+16,
1e+8,
1e+4,
1e+2,
1e+1
};
#define MAX_SCALE ((LONG_MAX - 10) / 10)
/* function to translate ASCII string to numeric format.
*
* Recognizes either integer numerics or floating point numerics.
* Recognizes integers in format:
* (sign)digit_sequence
* digit_sequence may contain a grouping character, the grouping character is ignored
* optional sign == '+' or '-'
*
* Recognizes floating point in formats:
* (sign)digit_sequence.digits(exp)
* (sign)digit_sequence.(exp)
* (sign)digit_sequence(exp)
* (sign).digits(exp)
* '.' represents the radix point passed, digit_sequence may contain a grouping character
* the grouping character is ignored
* optional sign == '+' or '-'
* optional exp == ('e' or 'E')(sign)digits
*
* Terminates on first unrecognized character.
*
*/
void *trans_numeric(
const char *str, /* pointer to string to translate */
char radix_point, /* radix character */
char group_char, /* grouping character to left of radix */
char **endstr) /* where to return pointer to first unrecognized character */
{
double dblval = 0.0;
int exp = 0,
dchr,
err = 0,
base = 10;
long int inum = 0;
unsigned long msdec = 0,
lsdec = 0,
msscale = 1;
unsigned radix = 0,
sign = 0,
digit_cnt = 0;
const char *strinit = str;
numeric_ptr rslt = NULL;
while ( isspace(*str) ) str++;
switch (*str)
{
case '-':
sign++;
case '+':
str++;
default:
break;
} /* endswitch */
while ( *str )
{
while ( (*str >= '0') && (*str <= '9') )
{
digit_cnt++;
if ( msdec < MAX_SCALE ) msdec = msdec * 10 + (*str - '0');
else if ( msscale < MAX_SCALE )
{
lsdec = lsdec * 10 + (*str - '0');
msscale *= 10;
}
else exp++;
if ( radix ) exp--;
else
{
dchr = *str - '0';
if ( ((LONG_MIN + dchr) / base) > inum ) err = 1;
inum = inum * base + dchr;
} /* endif */
str++;
} /* endwhile */
if ( !radix )
{
if ( *str == radix_point ) radix++;
else if ( *str != group_char ) break;
}
else
{
break;
} /* endif */
str++;
} /* endwhile */
if ( digit_cnt )
{
unsigned exp_dcnt = 0;
if ( (*str == 'e') || (*str == 'E') )
{
char exp_sign = EOS;
int ex_exp = 0;
switch (*++str)
{
case '-':
exp_sign++;
case '+':
str++;
default:
break;
} /* endswitch */
while ( (*str >= '0') && (*str <= '9') )
{
if (ex_exp < (DBL_MAX_EXP * 2) ) ex_exp = ex_exp * 10 + (*str - '0');
str++;
exp_dcnt++;
} /* endwhile */
exp += exp_sign ? -ex_exp : ex_exp;
} /* endif */
if ( radix || exp )
{
int pow = 256;
dblval = msdec;
if ( msscale != 1 ) dblval = dblval * msscale + lsdec;
if ( dblval && exp )
{
unsigned u = 0;
pow = 256;
while ( exp > 0 )
{
while ( exp >= pow )
{
dblval *= pos_table[u];
exp -= pow;
} /* endwhile */
pow >>= 1;
u++;
} /* endwhile */
while ( exp < 0 )
{
while ( exp <= -pow )
{
dblval *= neg_table[u];
if ( dblval == 0.0 )
{
errno = ERANGE;
err = 1;
} /* endif */
exp += pow;
} /* endwhile */
pow >>= 1;
u++;
} /* endwhile */
/* if overflow occurred */
if ( dblval == HUGE_VAL )
{
errno = ERANGE;
err = 1;
} /* endif */
} /* endif */
if ( !err )
{
rslt = (numeric_ptr)calloc(1, sizeof(numeric));
rslt->type = DBL_TYPE;
rslt->value.dbl_value = dblval;
} /* endif */
}
else
{
if ( (!sign && (inum == LONG_MIN)) || err )
{
inum = LONG_MIN + sign;
errno = ERANGE;
}
else
{
rslt = (numeric_ptr)calloc(1, sizeof(numeric));
rslt->type = INT_TYPE;
rslt->value.int_value = inum;
} /* endif */
} /* endif */
} /* endif */
if ( endstr )
{
if ( !digit_cnt ) *endstr = (char *) strinit;
else *endstr = (char *) str;
} /* endif */
return (void *)rslt;
} /* strtod_flt */
/* function to free memory used by numeric structure
*/
void free_numeric(
void *numeric_value)
{
if ( numeric_value ) free(numeric_value);
} /* free_numeric */
/* function to perform unary '-' operation
*/
void *negate_numeric(
void *value)
{
numeric_ptr rslt = (numeric_ptr)value;
switch ( rslt->type )
{
case INT_TYPE:
rslt->value.int_value = -rslt->value.int_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = -rslt->value.dbl_value;
break;
} /* endswitch */
return (void *)rslt;
} /* negate_numeric */
/* function to perform binary operators
* op_symbol - operation to perform
* ADD_OP == perform '+'
* SUB_OP == perform '-'
* DIV_OP == perform '/'
* MUL_OP == perform '*'
* ASN_OP == perform '='
* l_value - pointer to left hand value
* r_value - pointer to right hand value
*/
void *numeric_ops(
char op_symbol,
void *l_value,
void *r_value)
{
numeric_ptr lval = (numeric_ptr)l_value,
rval = (numeric_ptr)r_value,
rslt = (op_symbol == ASN_OP) ? lval : (numeric_ptr)calloc(1, sizeof(numeric));
switch ( op_symbol )
{
case ADD_OP:
if ( lval->type == rval->type )
{
rslt->type = lval->type;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.int_value = lval->value.int_value + rval->value.int_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value + rval->value.dbl_value;
break;
} /* endswitch */
}
else
{
rslt->type = DBL_TYPE;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.dbl_value = (double)(lval->value.int_value) + rval->value.dbl_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value + (double)(rval->value.int_value);
break;
} /* endswitch */
} /* endif */
break;
case SUB_OP:
if ( lval->type == rval->type )
{
rslt->type = lval->type;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.int_value = lval->value.int_value - rval->value.int_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value - rval->value.dbl_value;
break;
} /* endswitch */
}
else
{
rslt->type = DBL_TYPE;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.dbl_value = (double)(lval->value.int_value) - rval->value.dbl_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value - (double)(rval->value.int_value);
break;
} /* endswitch */
} /* endif */
break;
case DIV_OP:
rslt->type = DBL_TYPE;
if ( lval->type == rval->type )
{
switch ( lval->type )
{
case INT_TYPE:
rslt->value.dbl_value = (double)(lval->value.int_value) / (double)(rval->value.int_value);
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value / rval->value.dbl_value;
break;
} /* endswitch */
}
else
{
switch ( lval->type )
{
case INT_TYPE:
rslt->value.dbl_value = (double)(lval->value.int_value) / rval->value.dbl_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value / (double)(rval->value.int_value);
break;
} /* endswitch */
} /* endif */
break;
case MUL_OP:
if ( lval->type == rval->type )
{
rslt->type = lval->type;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.int_value = lval->value.int_value * rval->value.int_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value * rval->value.dbl_value;
break;
} /* endswitch */
}
else
{
rslt->type = DBL_TYPE;
switch ( lval->type )
{
case INT_TYPE:
rslt->value.dbl_value = (double)(lval->value.int_value) * rval->value.dbl_value;
break;
case DBL_TYPE:
rslt->value.dbl_value = lval->value.dbl_value * (double)(rval->value.int_value);
break;
} /* endswitch */
} /* endif */
break;
case ASN_OP:
if ( !lval ) lval = (numeric_ptr)calloc(1, sizeof(numeric));
lval->type = rval->type;
lval->value = rval->value;
rslt = lval;
break;
} /* endswitch */
return (void *)rslt;
} /* numeric_ops */

View File

@ -1,39 +0,0 @@
/***************************************************************************
* -------------------
* create : Tue Jul 11 20:21:18 2000
* copyright: (C) 2000 by Terry D. Boldt
* email : tboldt@attglobal.net
* -------------------
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/***************************************************************************
* Global Function Prototypes
* Tue Jul 11 20:21:18 2000
*
***************************************************************************/
#ifndef NUMERIC_OPS_H
#define NUMERIC_OPS_H
void *trans_numeric(const char *str, /* pointer to string to translate */
char radix_point, /* radix character */
char group_char, /* grouping character to left of radix */
char **endstr); /* where to return pointer to first
* unrecognized character */
void free_numeric(void *numeric_value);
void *negate_numeric(void *value);
void *numeric_ops(char op_symbol,
void *l_value,
void *r_value);
#endif

File diff suppressed because it is too large Load Diff