/************************************************************************/ /* */ /* am - Amortization Schedule Generator */ /* */ /* (c) Copyright 1987, 1992, 1993 Brett K. Carver */ /* */ /************************************************************************/ #include #include #include #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= 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= 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 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= .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>>>>> USE WITH CARE <<<<<< */ /********************************************************/ /* if (i == xx) { principal += ; pay += ; } */ 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 */