#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "loantype.def"
#include "macmenu.def"

/*
*************************************************************************

 ##                                      ##   ##                
 ##                                      ### ###                
 ##       ####    ####   #####    #####  #######  #####   ## ###
 ##      ##  ##      ##  ##  ##  ##      ## # ## ##  ##   ####  
 ##      ##  ##   #####  ##  ##   ####   ##   ## ##  ##   ###   
 ##      ##  ##  ##  ##  ##  ##      ##  ##   ##  #####   ##    
 ######   ####    ###### ##  ##  #####   ##   ##     ##   ##    
                                                 #####          

*************************************************************************

 Module:      LOANCALC.C


 Description: This module contains the code that implements all of 
              LoansMgr's financial calculations.


 Version:     2.5a1 (SuSE Linux 9.0, gcc compiler)

 Author:      Michael A. Campbell

****************************************************************************
*/

/**/

/*
--------------------------------
 External variable declarations
--------------------------------
*/

extern char *version;          /* The LoansMgr version string               */
extern char *month_names[];    /* Month names, defined in Loansmgr          */
extern char *year_names[];     /* Year names, defined in Loansmgr           */

/**/

/*
---------------------------------
 Private, module global functions
---------------------------------
*/

static BOOL equal ( num1, num2, threshold )

/***************************************************************************
 *                                                                         *
 * This function compares two numbers, and if the difference between them  *
 * is less than the supplied threshold, concludes that they are "equal",   *
 * and returns TRUE. Else, it returns FALSE.                               *
 *                                                                         *
 ***************************************************************************/

double num1, num2;    /* The two numbers to compare                        */
double threshold;     /* The maximum difference between "equal" values     */
{
   double delta;      /* The difference between the two numbers            */

   delta = fabs ( num1 - num2 );

   return ( delta <= threshold );

} /* equal */

/**/

static void build_date_str ( date_ctl, pi )

/***************************************************************************
 *                                                                         *
 * This function builds the date string that will be output per item       *
 * in a loan schedule being built by equity_interest_segment. The output   *
 * string is returned in the date_ctl structure element "date_str".        *
 *                                                                         *
 ***************************************************************************/

 struct t_date_ctl *date_ctl;  /* The date control struct                  */
 struct t_pi       *pi;        /* The payment interval data                */

 {
    char year_str [4];            /* String to hold the year value        */

    /* Check for year values in excess of LoanMgr's year name table. If   */
    /* is encountered, use "**" instead.                                  */

    if ( date_ctl->year < NUM_YEARS )
       strcpy ( year_str, year_names[date_ctl->year] );
    else
       strcpy ( year_str, "**" );

    if ( pi->units == 'M' ) {

       /* We are using months, so build the date string from the current   */
       /* month and year indices.                                          */

       strcpy ( date_ctl->date_str, month_names[date_ctl->month] );
       strcat ( date_ctl->date_str, " " );
       strcat ( date_ctl->date_str, year_str );
    }
    else {
       /* We are using weeks for our units, so build a valid date string   */
       /* only once per 52 weeks (1 year), since this is the only string   */
       /* we can be sure will be correct.                                  */

       if ( date_ctl->time_ctr == 0.0 ) {

          /* This is the start of a new 52 week period, so output month    */
          /* and year.                                                     */

          strcpy ( date_ctl->date_str, month_names[date_ctl->month] );
          strcat ( date_ctl->date_str, " " );
          strcat ( date_ctl->date_str, year_str );
       }
       else
          strcpy ( date_ctl->date_str, "--- --" );
    }

    /* Now that we have built the date string, increment the time counters  */
    /* for the next time around.                                            */

    if ( pi->units == 'M' ) {

       /* The units are months. Increment, and check for month rollover    */

       date_ctl->time_ctr += pi->n;

       if ( date_ctl->time_ctr >= 1.0 ) {

          /* We have rolled over to a new month. Increment the month, and   */
          /* then check to see if we have also rolled over to a new year.   */

          date_ctl->month   += (int) date_ctl->time_ctr;
          date_ctl->time_ctr = 0.0;

          if ( date_ctl->month >= 12 ) {

             /* We have rolled over to a new year. */

             date_ctl->month = 0;
             date_ctl->year  += 1;
          }
       }
    }
    else {
       /* The units are weeks. Increment, and check for year rollover.       */

       date_ctl->time_ctr += pi->n;

       if ( date_ctl->time_ctr >= 52.0 ) {

          /* We have rolled over to a new year. Increment the year counter   */

          date_ctl->year += 1;
          date_ctl->time_ctr = 0.0;
        }
    }

} /* build_date_str */

/**/

static void put_header ( file_ptr, page_eject, fmt_ctl )

/****************************************************************************
 * This function prints an amortization file page header to the supplied    *
 * file. It is called to place the header at the start of each page.        *
 ****************************************************************************/

FILE *file_ptr;              /* File pointer for amortization file          */
BOOL page_eject;             /* Output a page eject or not?                 */
struct t_fmt_ctl *fmt_ctl;   /* Formatting control information for header   */

{
   int  i;            /* Loop counter                                      */
   int  name_len;     /* Length of the user supplied loan name             */
   int  offset;       /* Horizontal offset to center user supplied name    */
   char output[82];   /* String for output buffering                       */

   const num_header_lines = 11;  /* Number of lines in the printed header  */

   /* Output a page eject, if requested, and a page number                 */

   if ( page_eject )
      fprintf ( file_ptr, "\f" );

   fprintf ( file_ptr, "%75s %d\n", "Page", fmt_ctl->page_num++ );

   /* Center the user-supplied loan name within a field, and then output   */
   /* name.                                                                */

   setmem ( output, sizeof(output), ' ' );
   name_len = strlen (fmt_ctl->loan_name );
   offset   = (80 - name_len) / 2;

   for ( i = 0; i < name_len; i++, offset++ )
      output[offset] = fmt_ctl->loan_name[i];
   output[offset] = '\0';

   fprintf ( file_ptr, "%s\n", output );

   /* Output the LoansMgr "masthead" message                                */

   fprintf ( file_ptr, "%21s %s%s\n\n", " ", fmt_ctl->version, "Amortization Schedule" );

   /* Output the relevant parameters of the loan                            */

   fprintf ( file_ptr, "%12s %10.2lf %s %10.2lf  %s %s\n",
             " L =", fmt_ctl->l, " P =", fmt_ctl->p, "Pay Interval =", fmt_ctl->pi_str );

   fprintf ( file_ptr, "%12s %10.2lf %s %10.2lf  %s %s\n\n",
             " I =", fmt_ctl->i, " C =", fmt_ctl->c, "Start Date =", fmt_ctl->start_date_str );


   /* Output the column headings, with a row of '-'s before and after       */
   
   setmem ( output, sizeof(output), '-' );
   output[79] = '\0';

   fprintf ( file_ptr, "%s\n", output );

   fprintf ( file_ptr, "%s %s \n",
             " Pay#   Date     Payment    Princpl   Interest    ",
             "PrPaid    IntPaid    Balance" );

   fprintf ( file_ptr, "%s\n\n", output );

   /* Update the lines_printed var, and we are done!                        */

   fmt_ctl->lines_printed = num_header_lines;
}

/**/

/*
------------------
 Public functions
------------------
*/

double equiv_interest_rate ( i, c, ny )

/*************************************************************************

 This function computes the simple interest rate that is equivalent to the
 given rate I compounded C times per year, with NY payments being made per
 year. The equivalent interest rate is returned.

 *************************************************************************/

double i;            /* Quoted interest rate                             */
double c;            /* Number of interest compounding periods per year  */
double ny;           /* Number of payments per year                      */

{

   double apr_i;     /* Input rate "I" as Annual Percentage Rate (APR)   */
   double equiv_i;   /* Equivalent simple interest rate for "I"          */

   /* Compute the effective annual interest rate. If C is set to 999.99  */
   /* use formulae for continuous compounding. Else, assume discrete     */
   /* compounding.                                                       */

   if ( c == 999.99 ) 
      apr_i = exp ( i / 100.0 ) - 1.0;
   else
      apr_i   = pow( (1.0 + i/(100.0 * c)), c ) - 1.0;

   equiv_i = ny * 100* ( pow( (1.0 + apr_i), (1.0 / ny) ) - 1.0 );

   return ( equiv_i );

} /* equiv_interest_rate */

/**/

void equity_interest ( p, n, i, ny, l, k, equity, interest )

/*************************************************************************

 This function computes and returns the equity and interest for payment
 k of a loan with the parameters p, n, i, and l.

 The implemented equations are :

                        n
 equity =        (1 + i)  * i * l
          -----------------------------
                 n                n-k+1
     ((1 + i)  - 1) * (i + 1)

 *************************************************************************/

 double p;          /* Payment size                                      */
 double n;          /* Number of payments                                */
 double i;          /* Annual interest rate                              */
 double ny;         /* The number of payments per year                   */
 double l;          /* Total loan size                                   */

 int k;             /* The payment to compute equity and interest for    */

 double *equity;    /* The equity portion of payment K                   */
 double *interest;  /* The interest portion of payment K                 */

 {

   double t1, t2, t3; /* Temporary vars, to avoid NABU compiler confusion */

   i = i / ny;        /* Interest rate per period (no percentage)         */
   i = i / 100.0;

   t1 = 1.0 + i;

   t2 = pow ( t1, n );
   t3 = t2 - 1.0;
   t2 = ( t2 * i ) / t3;
   t2 = t2 * l;

   t3 = n - (double) k + 1.0;
   t3 = pow ( t1, t3 );
   t3 = 1.0 / t3;

   t2 = t2 * t3;

   *equity   = t2;
   *interest = p - *equity;

} /* equity_interest */

/**/

double loan_size ( p, n, i, ny )

/****************************************************************************
 *                                                                          *
 *  This routine computes and returns the total loan size for a loan with   *
 *  the parameters P, N, and I.                                             *
 *                                                                          *
 *  This function implements :                                              *
 *                                                                          *
 *               / 1.0        1.0     \                                     *
 *              |  ---  -  ----------- |                                    *
 *  l = p * abs |                   n  |                                    *
 *              |   i      (1.0 + i )  |                                    *
 *               \                    /                                     *
 *                                                                          *
 ****************************************************************************/

double p;                 /* Payment size                                   */
double n;                 /* Number of payments                             */
double i;                 /* The Annual interest rate                       */
double ny;                /* The number of payments per year                */

{

   double t1;           /* Temp var to stop NABU compiler confusion         */
   double t2;           /* The same                                         */

   i = i / ny;          /* Interest rate per period (no percentage)         */
   i = i / 100.0;

   t1 = 1.0 + i;
   t1 = pow ( t1, n );
   t1 = t1 * i;
   t1 = 1.0 / t1;

   t2 = 1.0 / i;
   t1 = fabs ( t2 - t1 );

   t1 = t1 * p;

   return ( t1 );

} /* loan_size */

/**/

double payment_size ( l, i, ny, n )

/*****************************************************************************
 *                                                                           *
 *  This routine computes and returns the payment size for a loan with       *
 *  the parameters L, I, and N.                                              *
 *                                                                           *
 *  This function implements:                                                *
 *                                                                           *
 *  P =                L                                                     *
 *      -----------------------------------                                  *
 *       1         /             1        \                                  *
 *       -  * abs |   1  -   ----------    |                                 *
 *       I        |                  N     |                                 *
 *                 \          (1 + I)     /                                  *
 *****************************************************************************/

double l;           /* Loan size                                             */
double i;           /* The interest rate                                     */
double ny;          /* The number of payments per year                       */
double n;           /* Number of payments                                    */

{

   double t1;       /* Temp var to stop NABU compiler confusion                  */

   i = i / ny;      /* Interest rate per period (no percentage)              */
   i = i / 100.0;

   t1 = 1.0 + i;
   t1 = pow ( t1, n );
   t1 = 1.0 / t1;

   t1 = fabs ( 1.0 - t1) ;
   t1 = t1 / i;

   t1 = l / t1;

   return ( t1 );

} /* Payment_size */

/**/

double num_payments ( l, p, i, ny )

/*****************************************************************************
 *                                                                           *
 *  This routine computes and returns the number of payments for a loan      *
 *  with the parameters L, P, and I.                                         *
 *                                                                           *
 *  This function implements:                                                *
 *                                                                           *
 *  N =                 /    P    \                                          *
 *             log abs |-----------|                                         *
 *                2     \ P - I*L /                                          *
 *             ---------------------                                         *
 *                log    ( 1 + I )                                           *
 *                   2                                                       *
 *****************************************************************************/

double l;           /* Loan size                                             */
double p;           /* Payment size                                          */
double i;           /* The interest rate                                     */
double ny;          /* The number of payments per year                       */

{

   double t1, t2;      /* Temp vars to stop NABU compiler confusion          */

   i = i / ny;         /* Interest rate per period (no percentage)           */
   i = i / 100.0;

   t1 = fabs( p / ( p - i * l ) );
   t1 = log ( t1 ) ;

   t2 = log ( 1.0 + i );

   t1 = t1 / t2;

   return ( t1 );

} /* num_payments */

/**/

double interest_rate ( l, p, n, ny, c, threshold, iterations )

/*****************************************************************************
 *                                                                           *
 *  This routine computes and returns the interest rate for a loan with the  *
 *  parameters L, P, and N. The interest rate returned is an Annual          *
 *  Percentage Rate (APR) assuming 1 month payment intervals, and a fixed    *
 *  rate loan.                                                               *
 *                                                                           *
 *  Note that an iterative approach like this one (or one like it) is        *
 *  necessary, as it is not possible to solve the compound interest equation *
 *  for interest rate directly.                                              *
 *                                                                           *
 *****************************************************************************/

double l;           /* Loan size                                             */
double p;           /* Payment size                                          */
double n;           /* The number of payments                                */
double ny;          /* The number of payments per year                       */
double c;           /* The number of interest compounding periods per year   */
double threshold;   /* The maximum allowable error                           */
int    *iterations; /* The number of iterations needed to achieve result     */

{
   int j;           /* Loop counter                                          */
   double inc;      /* The increment to add to i on each loop                */
   double i;        /* The interest rate we are solving for                  */
   double equiv_i;  /* Equivalent simple interest rate for compounded i      */
   double li;       /* The value of "l" computed for interest rate "i"       */

   *iterations = 0;       /* No iterations yet                               */

   i   = 0;
   inc = 10.0;

   /* This algorithm arrives at the value of i by "ranging" one of the other */
   /* variables in the compound interest equation. In this case, we have     */
   /* arbitrarily selected the loan size. This algorithms starts with the    */
   /* 10s digit, and tries I in increments of 10 (up to 100), until it finds */
   /* one that results in a computed loan size that is less than the given   */
   /* loan size. This implies that the 10s digit of I is now greater than    */
   /* the true value. It then subtracts 10, and moves on to the ones digit,  */
   /* and repeats this process there. It does through as many significant    */
   /* digits as needed. This algorithm is guaranteed to take no more than 10 */
   /* iterations per significant digit, in the worst case, and so, for loans */
   /* bounded between 1% and 99% (as LoansMgr loans are), achieving 2        */
   /* significant digits of accuracy is guaranteed to take no more than 40   */
   /* iterations, and usually, much less.                                    */

   while ( inc > threshold ) {

      for ( j=0; j <= 9; j++, (*iterations)++ ) {

         i      += inc;
         equiv_i = equiv_interest_rate ( i, c, ny );
         li      = loan_size ( p, n, equiv_i, ny );

         if ( li < l  || equal (li, l, threshold) )
            break;
      }

      if ( equal (li, l, threshold) )
         break;

      else {
         i   = i - inc;
         inc = inc / 10.0;
      }

   } /* while inc > threshold */

   return ( i );

} /* interest_rate */

/**/

double amortization_segment ( l, p, i, pi, app, out_file, date_ctl, fmt_ctl,
                              equity_paid, interest_paid )

/***************************************************************************
 *                                                                         *
 * This function calculates the amortization schedule for a loan segment.  *
 * A segment is the portion of a loan that lies between direct principle   *
 * payments. If there are no direct principle payments during a loan,      *
 * there is only one "segment" to the loan. This function is called from   *
 * the amortization_schedule, which sequences all loan segments            *
 * through this function. This function returns the balance of the loan at *
 * the end of the segment.                                                 *
 *                                                                         *
 ***************************************************************************/

double l, p, i;        /* Supplied loan parameters                         */
struct t_pi *pi;       /* Payment interval data                            */
double app;            /* Period between principle injections in #payments */
FILE   *out_file;      /* File to save schedule in                         */

struct t_date_ctl *date_ctl;  /* Date printing control                     */
struct t_fmt_ctl  *fmt_ctl;  /* Struct to control file formatting          */

double *equity_paid;   /* The equity that has been paid at end of segment  */
double *interest_paid; /* The interest that has been paid at end of segment*/

{
   int    k;                   /* Payment index                            */
   double balance;             /* Balance remaining, at payment K          */
   double equity;              /* Equity portion of payment K              */
   double interest;            /* Interest portion of payment K            */
   double n;                   /* The number of payments for this segment  */
   double n_frac;              /* The fractional portion of n              */

   /* Compute the number of payments for an overall loan with the supplied */
   /* parameters. Tables will be generated based on the total loan, but    */
   /* only for the supplied segment length (app)                           */

   balance = l;
   n       = num_payments ( l, p, i, pi->ny );

   if ( app == 0 ) {

      /* No additional principle payments involved in this loan            */

      if ( date_ctl->payment_number == 0 ) {

         /* It is unlikely that the number of payments will come up to be     */
         /* an even number. For the case where no additional principle        */
         /* payments are involved, round n up to the nearest integer, and     */
         /* recompute the payment size to account for the difference.         */

         n_frac = fmod ( n, 1.0 );
         app = n = ( n_frac > 0.0009 ? ceil(n) : floor(n) );
         p = payment_size ( l, i, pi->ny, n );
      }
   }

   else {
      /* Additional priciple payments are being applied to this loan.         */

      /* If the number of payments remaining is less than the segment size,   */
      /* and that number is fractional, add one last iteration to the loop    */
      /* below to pick up that last fractional payment.                       */

      if ( n < app ) {
         n_frac = fmod ( n, 1.0 );
         app = ( n_frac > 0.0009 ? ceil(n) : floor(n) );
      }
   }

   for ( k = 1; k <= (int) app; k++, date_ctl->payment_number++ ) {

      /* Compute the various elements of the loan schedule                 */

      equity_interest ( p, n, i, pi->ny, l, k, &equity, &interest );

      if ( balance < p ) {

         /* The outstanding balance was less than the payment size. This is */
         /* the last payment of the loan. Only the returned interest value  */
         /* is correct. Adjust the rest of the numbers.                     */

         equity   = balance;
         p        = equity + interest;
      }

      *equity_paid   += equity;
      *interest_paid += interest;
      balance        -= equity;

      if ( out_file != NULL ) {

         /* If we have a valid output file to write to, print the  results */
         /* to it. Note that the Num Payments menu calls this function     */
         /* with a nil outfile, when additional principle payments are     */
         /* involved, and so it is valid to get here and have no out_file. */

         build_date_str ( date_ctl, pi );

         fprintf ( out_file, "%5d  %s %10.2lf %10.2lf %10.2lf %10.2lf",
         date_ctl->payment_number+1, date_ctl->date_str, 
         p, equity, interest, *equity_paid );

         fprintf ( out_file, " %10.2lf %10.2lf \n",
         *interest_paid, balance          );

         /* Check for completion of one page, and if reached, and there is  */
         /* still more of the loan to output, put out a new page header     */

         if ( ++(fmt_ctl->lines_printed) >= fmt_ctl->lines_per_page ) {
            if ( balance > 0.0009 )
               put_header ( out_file, TRUE, fmt_ctl );
         }
         else {

            /* If not at the end of a page, check for the end of a year, and */
            /* output a newline to break up years in the file. As with the   */
            /* above comment, check for the need to output a new page header */

            if (( pi->units == 'M' && date_ctl->time_ctr == 0.0 && date_ctl->month == 0 ) ||
            ( pi->units == 'W' && date_ctl->time_ctr == 0.0 )) {
               fprintf ( out_file, "%s", "\n" );

               if ( ++(fmt_ctl->lines_printed) >= fmt_ctl->lines_per_page )
                  if ( balance > 0.0009 )
                     put_header ( out_file, TRUE, fmt_ctl );
            }
         }
      }

   } /* for */

   return ( balance );

} /* amortization_segment */

/**/

double amortization_schedule ( l, p, i, pi, apa, app, date_ctl,
                               fmt_ctl, interest_paid, out_file )

/***************************************************************************
 *                                                                         *
 * This function calculates an amortization schedule, and saves it in the  *
 * supplied file. This function is called from the schedule_menu_processor *
 * function, and from the num_payments menu, when additional principle     *
 * payments are enabled (can only figure out "n" by iterating through the  *
 * whole loan). This function returns the number of payments in the loan,  *
 * as the value of the function, and the total interest paid, in the       *
 * *interest_paid variable.                                                *
 *                                                                         *
 ***************************************************************************/

double l, p, i;    /* Supplied loan parameters                             */
struct t_pi *pi;   /* The payment interval data                  */
double apa;        /* Additional principle payment amount                  */
double app;        /* The interval between additional principle payments   */

struct t_date_ctl *date_ctl; /* Struct used to control date printing       */
struct t_fmt_ctl  *fmt_ctl;  /* Struct to control file formatting          */

double *interest_paid;       /* The total interest paid - output parameter */
FILE   *out_file;            /* File to save schedule in                   */

{
   double balance;             /* Balance remaining, at end of segment    */
   double equity_paid = 0.0;   /* Total equity paid, at end of segment    */

   *interest_paid     = 0.0;   /* Total interest paid over life of loan   */

   /* Print headers for the output file (assuming that it is not null).   */
   /* Note that it is valid for out_file to be null, as the               */
   /* Num Payments menu calls this function with a null out_file, when    */
   /* it just needs to determine the overall length of the loan.          */

   if ( out_file != NULL )
      put_header ( out_file, FALSE, fmt_ctl );

   /* Initialize date control structures                                */

   date_ctl->payment_number = 0;
   date_ctl->time_ctr       = 0.0;

   do {
      /* While there is still an outstanding balance on this loan,      */
      /* compute the next segment of the loan.                          */

      balance = amortization_segment ( l, p, i, pi, app, out_file,
                        date_ctl, fmt_ctl,
                        &equity_paid, interest_paid );

      if ( balance > 0.0 ) {

        /* We have completed a loan segment with an outstanding        */
        /* balance. Apply an additional principle payment to it, and   */
        /* carry on, if needed.                                        */

        /* If the remaining balance is less than the scheduled         */
        /* additional principle payment, reduce that payment to the    */
        /* amount of the outstanding balance.                          */

        if ( apa > balance )
           apa = balance;

        equity_paid += apa;
        balance     -= apa;
        l            = balance;

        /* Generate a line in the amortization schedule file, if needed */

        if ( out_file != NULL ) {

           fprintf ( out_file, "%s  %s %10.2lf %10.2lf %s %s",
                     "*    ", date_ctl->date_str, 
                     apa, apa, "  --------", "  --------" );

           fprintf ( out_file, " %s %10.2lf \n",
                     "  --------", balance );

           if ( ++(fmt_ctl->lines_printed) >= fmt_ctl->lines_per_page )
              put_header ( out_file, TRUE, fmt_ctl );
        }

      } /* if balance > 0 */

   } while ( balance > 0.0 );

   return ( (double) date_ctl->payment_number );

} /* amortization_schedule */

