#include #include #include #include "contract.h" #include "loans.h" static int WIDTH, HEIGHT; static int padscroll, duration; static WINDOW *pad; _Noreturn static void finish(int sig); void display_custom_form(FORM *); void print_contract(WINDOW *w, int x, int y, contract *c); int run_sim(FORM *, char **contract_subtypes); int min(int a, int b); int main() { int ch, i; /* setup ncurses */ signal(SIGINT, finish); /* arrange interrupts to terminate */ initscr(); /* initialize the curses library */ cbreak(); /* take input chars one at a time don't wait for CR */ noecho(); /* echo input */ nonl(); /* do not LF->CR-LF on output */ intrflush(stdscr, FALSE); /* do not flush on interrupts */ keypad(stdscr, TRUE); /* enable keyboard mapping */ getmaxyx(stdscr, HEIGHT, WIDTH); /* create the form */ FIELD *fields[8]; FORM *form; char *contract_types[] = { "loan", "savings", NULL }; char *contract_subtypes[] = { "bullet", "sla", "mortgage", NULL }; /* initialize fields */ fields[0] = new_field(1, 11, 4, 22, 0, 0); /* contract type */ set_field_type(fields[0], TYPE_ENUM, contract_types, FALSE, TRUE); fields[1] = new_field(1, 11, 5, 22, 0, 0); /* contract subtype */ set_field_type(fields[1], TYPE_ENUM, contract_subtypes, FALSE, TRUE); fields[2] = new_field(1, 11, 6, 22, 0, 0); /* contract duration */ set_field_type(fields[2], TYPE_INTEGER, 1, 0, INT_MAX); fields[3] = new_field(1, 11, 7, 22, 0, 0); /* yearly payments */ set_field_type(fields[3], TYPE_INTEGER, 1, 0, INT_MAX); fields[4] = new_field(1, 11, 8, 22, 0, 0); /* delay duration */ set_field_type(fields[4], TYPE_INTEGER, 1, 0, INT_MAX); fields[5] = new_field(1, 11, 9, 22, 0, 0); /* starting amount */ set_field_type(fields[5], TYPE_NUMERIC, 2, 0.01, 0.0); fields[6] = new_field(1, 11, 10, 22, 0, 0); /* rate */ set_field_type(fields[6], TYPE_NUMERIC, 3, 0.0, 1.0); fields[7] = NULL; /* configure fields */ for (i = 0; fields[i]; i++) { set_field_back(fields[i], A_UNDERLINE); /* Print a line for the option */ field_opts_off(fields[i], O_AUTOSKIP); /* Don't go to next field when this */ /* Field is filled up */ } /* create form */ form = new_form(fields); display_custom_form(form); refresh(); int formactive = TRUE; /* mainloop */ while ((ch = getch()) != KEY_EXIT) { if (formactive) { switch (ch) { case KEY_DOWN: /* Go to next field */ form_driver(form, REQ_NEXT_FIELD); /* Go to the end of the present buffer */ /* Leaves nicely at the last character */ form_driver(form, REQ_END_LINE); break; case KEY_UP: form_driver(form, REQ_PREV_FIELD); form_driver(form, REQ_END_LINE); break; case KEY_LEFT: form_driver(form, REQ_PREV_CHOICE); break; case KEY_RIGHT: form_driver(form, REQ_NEXT_CHOICE); break; case KEY_BACKSPACE: form_driver(form, REQ_DEL_PREV); break; case '\r': if ((E_OK == form_driver(form, REQ_VALIDATION)) && run_sim(form, contract_subtypes)) { unpost_form(form); formactive = FALSE; curs_set(0); box(stdscr, 0, 0); padscroll = 0; mvwprintw(stdscr, 1, 2, "Reinbursement Table"); mvwprintw(stdscr, 3, 3, "%4s/%-4s | %12s | %10s | %10s | %10s\n", "cur", "tot", "principal", "interest", "part", "annuity"); refresh(); prefresh(pad, padscroll, 0, 4, 1, HEIGHT-2, WIDTH-1); } break; default: /* If this is a normal character, it gets */ /* Printed */ form_driver(form, ch); } } else { switch (ch) { case KEY_DOWN: padscroll += (padscroll < duration) ? 1 : 0; pnoutrefresh(pad, padscroll, 0, 4, 1, HEIGHT-2, WIDTH-1); doupdate(); break; case KEY_UP: padscroll -= (padscroll > 0) ? 1 : 0; pnoutrefresh(pad, padscroll, 0, 4, 1, HEIGHT-2, WIDTH-1); doupdate(); break; case KEY_NPAGE: padscroll += min(HEIGHT - 6, duration - padscroll); pnoutrefresh(pad, padscroll, 0, 4, 1, HEIGHT-2, WIDTH-1); doupdate(); break; case KEY_PPAGE: padscroll -= min(HEIGHT - 6, padscroll); pnoutrefresh(pad, padscroll, 0, 4, 1, HEIGHT-2, WIDTH-1); doupdate(); break; case 'q': display_custom_form(form); formactive = TRUE; curs_set(1); break; } } } delwin(pad); if (formactive) unpost_form(form); free_form(form); for (i = 0; fields[i]; i++) free_field(fields[i]); finish(0); } _Noreturn static void finish (int sig) { endwin(); exit(sig); } void display_custom_form(FORM *form) { post_form(form); mvprintw(2, 5, "Initialization Parameters"); mvprintw(4, 2, "Contract type:"); mvprintw(5, 2, "Contract subtype:"); mvprintw(6, 2, "Contract Duration:"); mvprintw(7, 2, "Yearly payments:"); mvprintw(8, 2, "Delay Duration:"); mvprintw(9, 2, "Starting Amount:"); mvprintw(10, 2, "Rate:"); form_driver(form, REQ_FIRST_FIELD); } void print_contract(WINDOW* w, int x, int y, contract *c) { mvwprintw(w, x, y, "%4d/%-4d | %12.2f | %10.2f | %10.2f | %10.2f\n", c->k, c->n, (double)c->P, (double)c->I, (double)c->D, (double)c->A); } int run_sim(FORM *form, char **contract_subtypes) { /* loan_type type; */ int n, d, yp; double r, p; loan_type lt; FIELD **fields = form_fields(form); lt = read_loan_type(contract_subtypes, field_buffer(fields[1], 0)); n = atoi(field_buffer(fields[2], 0)); yp = atoi(field_buffer(fields[3], 0)); d = atoi(field_buffer(fields[4], 0)); p = atof(field_buffer(fields[5], 0)); r = atof(field_buffer(fields[6], 0)); if (!n || !yp || (p <= 0.0)) return 0; //adapt wrt yearly payments if (yp > 1) { n = n*yp; d = d*yp; r = r/(double)yp; } /* setup pad */ pad = newpad(n+1, WIDTH-2); /* run simulation */ loan l = loan_init(lt, n, d, r, p); for (int i = 0; i <= n; i++) { print_contract(pad, i, 2, l.c); loan_update(l); } duration = n - HEIGHT + 6; return 1; } int min(int a, int b) { return (a < b) ? a : b; }