home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************/
- /* */
- /* am - Amortization Schedule Generator */
- /* */
- /* (c) Copyright 1987, 1992, 1993 Brett K. Carver */
- /* */
- /************************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
-
- #include "patchlevel.h"
-
- /* these two constants may be changed at will */
- #define ARRAY_SIZE 128 /* special arrays size: -I, -E, -U */
- #define SEP '-' /* seperator for dates: 1/1/93 or 1-1-93*/
-
- /* these constants should not be changed */
- #define FIELD_LENGTH 15 /* how wide are numbers: 123,456,789.12 */
- #define MAX_VAL 999999999.99 /* maximum dollar amount allowed */
- #define S_LEN 100 /* length of input buffer string */
- #define PLURAL(x) (x)!=1?"s":"" /* adds an 's' for plural items */
-
- #ifndef TRUE
- # define TRUE 1
- # define FALSE 0
- #endif
-
- int number; /* number of payments */
- double amount; /* amount of loan */
- double rate; /* interest rate */
- double payment; /* amount of monthly payment */
- int month, day, year; /* starting date */
- int e_idx = 0; /* index into extra payment arrays */
- int e_mon[ARRAY_SIZE]; /* months of extra payments */
- double e_pay[ARRAY_SIZE]; /* amounts of extra payments */
- int u_idx = 0; /* index into unusual payment arrays */
- int u_mon[ARRAY_SIZE]; /* months of unusual payments */
- double u_pay[ARRAY_SIZE]; /* amounts of unusual payments */
- int i_idx = 0; /* index into interest rate arrays */
- int i_mon[ARRAY_SIZE]; /* months of interest rates */
- double i_pay[ARRAY_SIZE]; /* amounts of interest rates */
- int list; /* months to list for */
- int delta_day; /* first month 30 +/- delta days */
- int balloon = FALSE; /* last payment is a balloon payment */
- int format = FALSE; /* include nroff formatting commands */
- char istr[S_LEN]; /* input buffer string */
-
- #define def_number 360 /* default number of payments */
- #define def_amount 10000.0 /* default amount of loan */
- #define def_rate 10.0 /* default interest rate */
- #define def_payment 100.0 /* default amount of monthly payment */
- #define def_start "1-1-90"/* default starting date */
- #define def_list 0 /* default months to list for */
-
- char * usage_text[] = {
- "\t-n\tnumber of payments (-n-30 is 30 years, 360 months):",
- "\t\t\t=0 - till paid in full",
- "\t\t\t>0 - number of months",
- "\t\t\t<0 - number of years",
- "\t-a\tamount of loan (<= $999,999,999.99)",
- "\t-i\tinterest rate (-i10 is 10%)",
- "\t-p\tmonthly payment amount:",
- "\t\t\t=0 - computed based on number of payments",
- #if (SEP == '-')
- "\t-s\tstart first payment on mm-dd-yy (-s12-15-93 is Dec 15 1993)",
- #else
- "\t-s\tstart first payment on mm/dd/yy (-s12/15/93 is Dec 15 1993)",
- #endif
- "\t-l\tlist loan for:",
- "\t\t\t=0 - list all payments",
- "\t\t\t>0 - number of months",
- "\t\t\t<0 - number of years",
- "\t-b\tmake last payment a balloon payment if ballance due",
- "\t-I\tmonth:rate for new interest rate[s] (up to 128 allowed):",
- "\t\t\t>0 - new interest rate (-e8:12 rate at 12% on month 8)",
- "\t-E\tmonth:amount for extra payment[s] (up to 128 allowed):",
- "\t\t\t>0 - extra principal payment amount (-e7:50 extra $50)",
- "\t\t\t<0 - add to unpaid principal (-e5:-100 add $100)",
- "\t-U\tmonth:unusual payment[s] (up to 128 allowed):",
- "\t\t\t=0 - missed payment (-u9:0 missed payment on month 9)",
- "\t\t\t>0 - non-standard payment amount (-u7:75 only paid $75)",
- "\t-D\tchange first month by +/- days",
- "\t\t\t>0 - month had more days than normal",
- "\t\t\t<0 - month had less days than normal",
- "\t-f\tOutput with nroff formatting commands",
- 0 }; /* last entry MUST be null */
-
- char *titles = "NUMBER DATE PAYMENT INTEREST PRINCIPAL BALANCE";
-
- /************************************************************************/
- /* */
- /************************************************************************/
- char * add_commas(value)
- double value;
- {
- static char string[32];
- static char * sptr = &string[0];
- register int i, j, k;
- register int length;
- int negative;
-
- negative = FALSE;
- if (value < 0) {
- value = -value;
- negative = TRUE;
- }
-
- if (value > MAX_VAL) {
- fprintf(stderr, "Error:\ta value has exceeded $999,999,999.99,\n");
- fprintf(stderr, "\tplease use another program for calculating the national debt.\n");
- exit(1);
- }
-
- sprintf(sptr, "%.2f ", value);
- length = strlen(sptr); /* sprintf() doesn't return length on all unix's */
- length -= 16; /* 1234567890123456 */
- length --; /* make 0 relative */
- string[FIELD_LENGTH] = '\000';
- j = FIELD_LENGTH - 1;
- k = 6;
-
- for (i=length; i>=0; i--) {
- if ((k--) == 0) {
- string[j--] = ',';
- k = 2;
- }
- string[j--] = string[i];
- string[i] = ' ';
- }
-
- if (negative)
- string[j--] = '-';
-
- return(sptr);
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- int get_int(text, def)
- char *text;
- int def;
- {
- int value;
-
- fprintf(stderr, "%s [%d]: ", text, def);
- fgets(istr, S_LEN, stdin);
-
- if (strlen(istr) <= 1)
- return(def);
-
- value = 0; /* in case nothing scans */
- sscanf(istr, "%d", &value);
-
- return(value);
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- double get_double(text, def)
- char *text;
- double def;
- {
- double value;
-
- fprintf(stderr, "%s [%.0f]: ", text, def);
- fgets(istr, S_LEN, stdin);
-
- if (strlen(istr) <= 1)
- return(def);
-
- value = 0.0; /* in case nothing scans */
- sscanf(istr, "%lf", &value);
-
- return(value);
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- dup_warning(param)
- int param;
- {
- fprintf(stderr, "Warning: duplicate '-%c' parameter\n", param);
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- init(argc, argv)
- int argc;
- char *argv[];
- {
- extern char *optarg;
- extern int optind;
- int c;
- int error_flag = FALSE;
- char z[2]; /* a sscanf temp */
- int got_number = FALSE;
- int got_amount = FALSE;
- int got_rate = FALSE;
- int got_payment = FALSE;
- int got_start = FALSE;
- int got_list = FALSE;
- int i;
-
- while ((c = getopt(argc, argv, "n:a:i:p:s:l:bI:E:U:D:f?")) != EOF)
- switch (c) {
-
- case 'n':
- if (got_number)
- dup_warning(c);
- sscanf(optarg, "%d", &number);
- got_number = TRUE;
- break;
-
- case 'a':
- if (got_amount)
- dup_warning(c);
- sscanf(optarg, "%lf", &amount);
- if (amount <= 0.0) {
- fprintf(stderr, "Error: amount <= 0.0\n");
- error_flag = TRUE;
- }
- if (amount > MAX_VAL) {
- fprintf(stderr,
- "Error: amount > 999,999,999.99\n");
- error_flag = TRUE;
- }
- got_amount = TRUE;
- break;
-
- case 'i':
- if (got_rate)
- dup_warning(c);
- sscanf(optarg, "%lf", &rate);
- if (rate <= 0.0) {
- fprintf(stderr, "Error: rate <= 0.0\n");
- error_flag = TRUE;
- }
- got_rate = TRUE;
- break;
-
- case 'p':
- if (got_payment)
- dup_warning(c);
- sscanf(optarg, "%lf", &payment);
- if (payment < 0.0) {
- fprintf(stderr, "Error: payment < 0.0\n");
- error_flag = TRUE;
- }
- got_payment = TRUE;
- break;
-
- case 's':
- if (got_start)
- dup_warning(c);
- sscanf(optarg, "%d%[- /]%d%[- /]%d",
- &month, z, &day, z, &year);
- got_start = TRUE;
- break;
-
- case 'l':
- if (got_list)
- dup_warning(c);
- sscanf(optarg, "%d", &list);
- got_list = TRUE;
- break;
-
- case 'b':
- if (balloon)
- dup_warning(c);
- balloon = TRUE;
- break;
-
- case 'E':
- if (e_idx >= ARRAY_SIZE) {
- fprintf(stderr, "Error: more than %d extra payments\n",
- ARRAY_SIZE);
- error_flag = TRUE;
- break;
- }
- sscanf(optarg, "%d:%lf", &e_mon[e_idx], &e_pay[e_idx]);
- if (e_mon[e_idx] <= 0) {
- fprintf(stderr,
- "Error: invalid extra payment month: %d\n",
- e_mon[e_idx]);
- error_flag = TRUE;
- }
- if (e_pay[e_idx] == 0) {
- fprintf(stderr,
- "Warning: $0.00 extra payment for month: %d, ignored\n",
- e_mon[e_idx]);
- }
- for (i=0; i<e_idx; i++)
- if (e_mon[i] == e_mon[e_idx]) {
- fprintf(stderr,
- "Error: duplicate extra payment month: %d\n",
- e_mon[e_idx]);
- error_flag = TRUE;
- }
- e_idx++;
- break;
-
- case 'U':
- if (u_idx >= ARRAY_SIZE) {
- fprintf(stderr, "Error: more than %d unusual payments\n",
- ARRAY_SIZE);
- error_flag = TRUE;
- break;
- }
- sscanf(optarg, "%d:%lf", &u_mon[u_idx], &u_pay[u_idx]);
- if (u_mon[u_idx] <= 0) {
- fprintf(stderr,
- "Error: invalid unusual payment month: %d\n",
- u_mon[u_idx]);
- error_flag = TRUE;
- }
- if (u_pay[u_idx] < 0) {
- fprintf(stderr,
- "Error: invalid unusual payment: %d:%.2f\n",
- u_mon[u_idx], u_pay[u_idx]);
- error_flag = TRUE;
- }
- for (i=0; i<u_idx; i++)
- if (u_mon[i] == u_mon[u_idx]) {
- fprintf(stderr,
- "Error: duplicate unusual payment month: %d\n",
- u_mon[u_idx]);
- error_flag = TRUE;
- }
- u_idx++;
- break;
-
- case 'I':
- if (i_idx >= ARRAY_SIZE) {
- fprintf(stderr, "Error: more than %d interest rate changes\n",
- ARRAY_SIZE);
- error_flag = TRUE;
- break;
- }
- sscanf(optarg, "%d:%lf", &i_mon[i_idx], &i_pay[i_idx]);
- i_pay[i_idx] /= 100.0;
- i_pay[i_idx] /= 12.0;
- if (i_mon[i_idx] <= 0) {
- fprintf(stderr,
- "Error: invalid interest rate change month: %d\n",
- i_mon[i_idx]);
- error_flag = TRUE;
- }
- if (i_pay[i_idx] <= 0) {
- fprintf(stderr,
- "Error: invalid interest rate change: %d:%.2f\n",
- i_mon[i_idx], i_pay[i_idx]);
- error_flag = TRUE;
- }
- for (i=0; i<i_idx; i++)
- if (i_mon[i] == i_mon[i_idx]) {
- fprintf(stderr,
- "Error: duplicate interest rate change month: %d\n",
- i_mon[i_idx]);
- error_flag = TRUE;
- }
- i_idx++;
- break;
-
- case 'D':
- sscanf(optarg, "%d", &delta_day);
- if ((delta_day < -30) || (delta_day > 30)) {
- fprintf(stderr, "Error: first month can only be +/- 30 days\n");
- error_flag = TRUE;
- }
- break;
-
- case 'f':
- if (format)
- dup_warning(c);
- format = TRUE;
- break;
-
- case '?':
- error_flag = TRUE;
- break;
-
- default:
- error_flag = TRUE;
- break;
-
- }
-
- if (error_flag || optind < argc) {
- char **text;
-
- fprintf(stderr, "\n%s: %s\n", argv[0], &ident[5]);
- fprintf(stderr, "\t(c) 1987, 1992, 1993 - Brett K. Carver\n\n");
- fprintf(stderr, "usage: %s [arguments]\n", argv[0]);
- text = usage_text;
- while (*text)
- fprintf(stderr, "%s\n", *text++);
- exit(1);
- }
-
- if (!got_number)
- number = get_int("Enter number of payments (- = years, + = months, 0 = till paid)", def_number);
-
- if (number < 0)
- number *= -12;
-
- if (!got_amount)
- amount = get_double("Enter amount of loan", def_amount);
-
- if (amount <= 0.0) {
- fprintf(stderr, "Error: amount <= 0.0\n");
- exit(1);
- }
-
- if (amount > MAX_VAL) {
- fprintf(stderr, "Error: amount > 999,999,999.99\n");
- exit(1);
- }
-
- if (!got_rate)
- rate = get_double("Enter interest rate(%)", def_rate);
-
- if (rate <= 0.0) {
- fprintf(stderr, "Error: rate <= 0.0\n");
- exit(1);
- }
-
- rate /= 100.0;
- rate /= 12.0;
-
- if (!got_payment) {
- if (number == 0)
- payment = get_double("Enter amount of payment (required)",
- def_payment);
- else
- payment = get_double("Enter amount of payment (0 for computed)",
- 0.0);
- }
-
- if (payment < 0.0) {
- fprintf(stderr, "Error: payment < 0.0\n");
- exit(1);
- }
-
- while ((payment < 0.01) && (number == 0)) {
- fprintf(stderr, "Error: payment required (unable to compute):");
- scanf("%lf", &payment);
- }
-
- if (payment < 0.01) {
- /****************************************/
- /* A * R */
- /* ---------------- */
- /* P = 1 */
- /* 1 - ---------- */
- /* N */
- /* (1 + R) */
- /****************************************/
- payment = amount * rate /
- (1.0 - (1.0 /
- pow(1.0 + rate, (double)number)));
- }
-
- payment = ceil(payment * 100.0) / 100.0;
-
- if (payment < rate * amount) {
- fprintf(stderr, "Warning: payment < interest amount.\n");
- }
-
- if (!got_start) {
- fprintf(stderr, "Starting month%cday%cyear [1%c1%c90]: ",
- SEP, SEP, SEP, SEP);
- fgets(istr, S_LEN, stdin);
- if (strlen(istr) <= 1)
- strcpy(istr, def_start);
- sscanf(istr, "%d%[- /]%d%[- /]%d", &month, z, &day, z, &year);
- }
-
- year %= 100;
-
- if (!got_list)
- list = get_int("Number of payments to list (0 for all)", def_list);
-
- if (list < 0)
- list *= -12;
-
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- print_header()
- {
-
- if (format) {
- printf(".nf\n"); /* no fill */
- printf(".na\n"); /* no adjust */
- printf(".ll 80\n"); /* line length */
- printf(".lt 80\n"); /* title length */
- printf(".de hd\n"); /* define header */
- printf(".sp 4\n");
- printf(".tl '\\fI%s\\fR'\n", titles);
- printf("..\n");
- printf(".de fo\n"); /* define footer */
- printf(".sp 2\n");
- printf(".tl ''- %% -''\n");
- printf(".bp\n");
- printf("..\n");
- printf(".wh 0 hd\n"); /* header trap */
- printf(".wh -4 fo\n"); /* footer trap */
- printf(".sp 3\n");
- }
-
- if (number == 0)
- printf("Number of payments: till paid\n");
- else
- printf("Number of payments: %5d\n", number);
-
- printf("Amount of loan: %s\n", add_commas(amount));
- printf("Interest rate: %8.2f %%\n", rate*100.0*12.0);
- printf("Payment amount: %s\n", add_commas(payment));
- printf("Starting date: %.2d%c%.2d%c%.2d\n", month, SEP, day, SEP, year);
-
- if (e_idx > 0)
- printf("With %d extra payment%s defined.\n", e_idx, PLURAL(e_idx));
-
- if (u_idx > 0)
- printf("With %d unusual payment%s defined.\n", u_idx, PLURAL(u_idx));
-
- if (i_idx > 0)
- printf("With %d interest rage change%s defined.\n", i_idx,
- PLURAL(i_idx));
-
- if (delta_day != 0)
- printf("With a first month of %d day%s.\n", 30+delta_day,
- PLURAL(30+delta_day));
-
- if (list != 0) {
- if (list < number || number == 0)
- number = list;
- printf("Listing for %d payment%s, %.1f year%s.\n", number,
- PLURAL(number), number/12.0, PLURAL(number/12.0));
- }
- else {
- if (number != 0)
- printf("Listing for %d payment%s, %.1f year%s.\n", number,
- PLURAL(number), number/12.0, PLURAL(number/12.0));
- else {
- printf("Listing till paid.\n");
- number = 99999;
- }
- }
-
- if (balloon)
- printf("With a balloon final payment.\n");
-
- if (format) {
- printf(".sp 3\n");
- printf("\\fI%s\\fR\n", titles);
- }
- else {
- printf("\n\n\n");
- printf("%s\n", titles);
- }
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- print_totals(leader, t_pay, t_int, t_pcp)
- char *leader;
- double t_pay, t_int, t_pcp;
- {
-
- printf("%s", leader);
-
- if (format)
- printf("\\fB");
-
- printf("%s ", add_commas(t_pay));
- printf("%s ", add_commas(t_int));
- printf("%s", add_commas(t_pcp));
-
- if (format)
- printf("\\fR");
-
- printf("\n");
- }
-
- /************************************************************************/
- /* */
- /************************************************************************/
- main(argc, argv)
- int argc;
- char *argv[];
- {
- double interest;
- double principal;
- double pay;
- double t_interest = 0.0;
- double t_principal = 0.0;
- double t_payment = 0.0;
- double gt_interest = 0.0;
- double gt_principal = 0.0;
- double gt_payment = 0.0;
- int i, j;
-
- init(argc, argv);
- print_header();
-
- for (i=1; i<=number; i++) {
-
- for (j=0; j<i_idx; j++)
- if (i == i_mon[j]) { /* interest rate change */
- rate = i_pay[j];
- break; /* found one, we're done */
- }
-
- interest = rate * amount;
-
- if (delta_day != 0)
- if (i == 1)
- /* adjust first month to be days more/less than 30 */
- interest += delta_day * (rate / 30.0) * amount;
-
- /* INTEREST */
- /****************************************************************/
- /* There are several ways one might round the interest due. */
- /* Un-comment a different method if that applies in your case. */
- /****************************************************************/
- /* round up */
- /* interest = ceil(interest * 100.0) / 100.0; */
- /* normal rounding ( >= .5 < )*/
- interest = floor(interest * 100.0 + 0.5) / 100.0;
- /* round down (truncate) */
- /* interest = floor(interest * 100.0) / 100.0; */
-
- principal = payment - interest;
- pay = payment;
-
- for (j=0; j<u_idx; j++)
- if (i == u_mon[j]) { /* unusual payment defined */
- if (u_pay[j] == 0) { /* missed payment */
- principal = -interest;
- pay = interest = 0.0;
- }
- else { /* low payment */
- pay = u_pay[j];
- if (interest < pay)
- principal = pay - interest;
- else {
- principal = -(interest - pay);
- interest = pay;
- }
- }
- break; /* found one, we're done */
- }
-
- for (j=0; j<e_idx; j++)
- if (i == e_mon[j]) { /* extra payment defined */
- if (e_pay[j] < 0) { /* penality */
- principal += e_pay[j];
- pay += e_pay[j];
- }
- else { /* extra principal */
- principal += e_pay[j];
- pay += e_pay[j];
- }
- break; /* found one, we're done */
- }
-
- /* SPECIAL */
- /********************************************************/
- /* This is the place to do any special case handling */
- /* that can't be handled by the -e, -u, or -d arguments */
- /* It is triggered by a specific payment number (to be */
- /* filled in at the 'xx') and allows for modifying the */
- /* principal or payment amound for that month. */
- /* >>>>>> USE WITH CARE <<<<<< */
- /********************************************************/
- /* if (i == xx) {
- principal += <some value>;
- pay += <some value>;
- } */
-
- if (principal >= amount) { /* loan paid off */
- principal = amount;
- pay = interest + principal;
- number = 0;
- }
-
- if (i == number && balloon) { /* loan done, balloon */
- principal = amount;
- pay = interest + principal;
- number = 0;
- }
-
- amount -= principal;
- printf("%4d %.2d%c%.2d%c%.2d ", i, month, SEP, day, SEP, year);
- printf("%s ", add_commas(pay));
- printf("%s ", add_commas(interest));
- printf("%s ", add_commas(principal));
- printf("%s\n", add_commas(amount));
- t_interest += interest;
-
- if (principal > 0)
- t_principal += principal;
-
- if (pay > 0)
- t_payment += pay;
-
- if (month == 12) {
- print_totals(" ", t_payment, t_interest,
- t_principal);
- gt_interest += t_interest;
- gt_principal += t_principal;
- gt_payment += t_payment;
- t_interest = t_principal = t_payment = 0.0;
- month = 0;
- year += 1;
-
- if (year > 99)
- year = 0;
-
- if (i < number) {
- printf("\n");
- if (format)
- printf(".ne 14\n"); /* need 14 lines per year */
- else
- printf("%s\n", titles);
- }
- }
- month += 1;
- }
-
- if (month != 1) { /* pickup a partial year */
- print_totals(" ", t_payment, t_interest, t_principal);
- gt_interest += t_interest;
- gt_principal += t_principal;
- gt_payment += t_payment;
- }
-
- printf("\n");
- print_totals("loan totals: ", gt_payment, gt_interest, gt_principal);
-
- exit(0);
- }
-
- /* end of file */
-