/*    DATA.C   DATA.C    DATA.C    DATA.C    DATA.C    DATA.C

DATA INITIALIZE AND CALCULATE      XXXX     X    XXXXX    X
                                   X   X   X X     X     X X
Oct 18, 1988                       X   X  X   X    X    X   X
                                   X   X  X   X    X    X   X
                                   X   X  XXXXX    X    XXXXX
                                   X   X  X   X    X    X   X
                                   XXXX   X   X    X    X   X
*/
#include "ctype.h"
#include "string.h"
#include "math.h"
#include "struct.def"
#include "defin.h"

extern struct vars allvars[];
extern char inline[];
extern int col;
extern int errcode,colerr;
extern char strngout[];
extern int valattr;

/* ****************************************************** initdata */
/* This function is used to initialize all of the variables to the */
/* starting values including their variable names.                 */
void initdata(struct vars *pnt)
{
int index;
    for (index = 0;index < 6;++index){
       (pnt+index)->varname[0] = 'A' + index;
       (pnt+index)->varname[1] = 0;
       (pnt+index)->outtype = 'F';
       (pnt+index)->value = 0.0;
    }
    for (index = 6;index < 12;++index){
       (pnt+index)->varname[0] = 'I' + index - 6;
       (pnt+index)->varname[1] = 0;
       (pnt+index)->outtype = 'D';
       (pnt+index)->value = 0;
    }
    (pnt+4)->value = 2.718281828459045;
    (pnt+5)->value = 3.141592653589793;
    (pnt+11)->value = 16777215;
}

/* ******************************************************* getnames */
/* This function reads in a new list of variable names to be used   */
/* in place of the names A to F. This is only as a convenience for  */
/* the user, so he can use more meaningful names.                   */
void getnames(void)
{
char var,index;
char dummy[80];

   col = 1;                      /* skip over and ignore the # sign */
   do {
      if (inline[col] == ' ') col++;   /* ignore all leading blanks */
      else {
         var = inline[col] - 'A';
         if ((var >= 0) && (var <= 5)) {             /* A through F */
            col++;
            if (inline[col] != '-') {           /* should be a dash */
               errchk(14);
               errout();
               return;
            }
            col++;
            for (index = 0;index < 6;index++)
               if (isalpha(inline[col]) || isdigit(inline[col])) {
                  dummy[index] = inline[col];
                  dummy[index + 1] = 0;
                  col++;
               }
            if (index > 0) {
               strcpy(allvars[var].varname,dummy);
               strcpy(strngout,"      ");         /* this is for    */
               strngout[6-strlen(dummy)] = 0;     /* leading blanks */
               strcat(strngout,dummy);
               strngdis(var + 1,1,valattr);
            }
         }
         else if ((var >= 8) && (var <= 13)) {       /* I through N */
            col++;
            if (inline[col] != '-') {           /* should be a dash */
               errchk(14);
               errout();
               return;
            }
            col++;
            if ((inline[col] == 'O') || (inline[col] == 'D') ||
                (inline[col] == 'H') || (inline[col] == 'X')) {
               var -= 2;
               allvars[var].outtype = inline[col];
               col++;
            }
            else {
               errchk(15);
               errout();
               return;
            }
         }
         else {                            /* unknown variable name */
            errchk(3);
            errout();
            return;
         }
      }
   } while (inline[col] && (errcode == 0));
}

/* ******************************************************* calcdata */
/* This is the function that actually does the work of deciding     */
/* what calculations need to be done. The method used is described  */
/* in detail below.                                                 */
/*    Three variables and two operators are         number1         */
/*    stored as per the list. "op1" is always         op1           */
/*    a plus or minus, and op2 is always a          number2         */
/*    multiplication or divide. If a plus or          op2           */
/*    minus is found for op2, number1 and           number3         */
/*    number2 are combined according to op1                         */
/*    and the result becomes number1, the new op2 becomes op1, and  */
/*    number3 becomes number2. If op2 is either a multiplication or */
/*    divide, number2 and number3 are combined according to op2 and */
/*    the result is placed in number2. When a parentheses is found, */
/*    a recursive call is made and the expression in the parenthe-  */
/*    ses is evaluated. A bit of study will be very fruitful in     */
/*    understanding this operation.                                 */
void calcdata(double *newval)
{
double number1,number2,number3,getnum();
char op1,op2,getop();
   number1 = 0.0;
   op1 = '+';
   number2 = 0.0;
   while (inline[col] == ' ') col++;               /* ignore blanks */
                                        /* check for unary operator */
   if ((inline[col] == '+') || (inline[col] == '-')) {
      op1 = inline[col];
      col++;
   }

   number2 = getnum();          /* (inline[col]== 0 ) = End of line */
                                /* (inline[col]=='$') = End of line */
   if ((inline[col] == 0) || (inline[col] =='$')) {
      calcdat(&number1,&op1,&number2);
      *newval = number1;
      return;
   }
   for (;;){
      op2 = getop();
      col++;
                               /* check for end of line  0, ), or $ */
      if (op2 == '$') op2 = 0;        /* force a $ to lool like eol */
      if ((op2 == 0) || (op2 == ')')) {
         col++;
         calcdat(&number1,&op1,&number2);
         *newval = number1;
         return;
      }
      number3 = getnum();
      if ((op2 == '*') || (op2 == '/')){
         calcdat(&number2,&op2,&number3);
      }
      else{
         calcdat(&number1,&op1,&number2);
         op1 = op2;
         number2 = number3;
      }
   } /* end of infinite for loop */
}

/* ******************************************************** calcdat */
/* This function actually does the calculations described in the    */
/* function above.                                                  */
void calcdat(double *number1,char *op1,double *number2)
{
   switch (*op1) {
      case '*' : *number1 = (*number1)*(*number2);
                 break;
      case '/' : if (*number2 == 0.0) {
                     errchk(10);
                     break;
                 }
                 *number1 = (*number1)/(*number2);
                 break;
      case '+' : *number1 = *number1 + *number2;
                 break;
      case '-' : *number1 = *number1 - *number2;
                 break;
      default  : break;
   }
   return;
}

/* ********************************************************* getnum */
/* This is the workhorse of the program. It actually scans the data */
/* input field and converts the data into numbers which are in the  */
/* internal format of the computer so the calculations can be done. */
double getnum(void)
{
int index,i,decpt;
double newval;
char name[7];
double sqrt(), exp(), log(), sin(), cos(), atan();

   while (inline[col] == ' ') col++;               /* ignore blanks */

   index = inline[col];
   if (index == '('){
       col++;
       calcdata(&newval);                         /* recursive call */
       col--;
       return(newval);
   }

   decpt = 0;                         /* search for a decimal point */
   i = col;
   index = inline[i];
   while ((index >= '0') && (index <= '9')) {
      i++;
      index = inline[i];
      if (index == '.') decpt = 1;
   }

   index = inline[col];
   if (((index > '0') && (index <= '9')) ||
       ((index == '0') && (decpt == 1))  ||
        (index == '.')) {                        /* floating number */
      newval = 0.0;
      while ((index >= '0') && (index <= '9')) {
         newval = 10.0*newval + (index - '0');
         col++;
         index = inline[col];
      }
      if (index == '.') {
         double constant = 0.1;
         col++;
         index = inline[col];
         while ((index >= '0') && (index <= '9')) {
            newval = newval + (index - '0') * constant;
            constant *= 0.1;
            col++;
            index = inline[col];
         }
      }
      return(newval);
   }                                /* end of floating point number */

   if (index == '0') {                      /* octal or hexadecimal */
      col++;
      index = inline[col];
      if ((index == 'x') || (index == 'X')){         /* hexadecimal */
         col++;
         index = inline[col];
         newval = 0.0;
         while (((index >= '0') && (index <= '9')) ||
                ((index >= 'A') && (index <= 'F')) ||
                ((index >= 'a') && (index <= 'f'))) {
            switch (index) {
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':i = index - '0';
                        break;
               case 'A':
               case 'B':
               case 'C':
               case 'D':
               case 'E':
               case 'F':i = index - 'A' + 10;
                        break;
               case 'a':
               case 'b':
               case 'c':
               case 'd':
               case 'e':
               case 'f':i = index - 'a' + 10;
                        break;
               default :break;
            }
            newval = 16.0*newval + i;
            col++;
            index = inline[col];
         }
      return(newval);
      }                                  /* end of hexadecimal 'if' */

      else {                                               /* octal */
         newval = 0.0;
         while ((index >= '0') && (index <= '7')) {
            newval = 8.0*newval + (index - '0');
            col++;
            index = inline[col];
         }
      return(newval);
      }                                      /* end of octal 'else' */

   }                         /* end of octal or hexadecimal numbers */

                                  /* find variable or function name */
   if ((inline[col] >= 'A') && (inline[col] <= 'Z')) {
      name[0] = inline[col++];
      i = 1;
      while ((((inline[col] >= 'A') && (inline[col] <= 'Z')) ||
             ((inline[col] >= '0') && (inline[col] <= '9'))) &&
             (i <= 5)) {           /* continue var or function name */
         name[i++] = inline[col++];
      }
      name[i] = 0;                                    /* name found */

      if (inline[col] == '('){                     /* function call */
         col++;
         if (strcmp(name,"ABS") == 0) {
            calcdata(&newval);
            if (newval < 0.0) newval = -newval;
         }
         else if (strcmp(name,"SQRT") == 0) {
            calcdata(&newval);
            if (newval < 0.0) {
               errchk(5);                     /* Neg value for SQRT */
               newval = 0.0;
            }
            else newval = sqrt(newval);
         }
         else if (strcmp(name,"FACT") == 0) {
            calcdata(&newval);
            if (newval < 0.0) {
               errchk(16);                    /* Neg value for FACT */
               newval = 0.0;
            }
            else {
            double count;
            int howmany;
               count = newval;
               newval = 1.0;
               for (howmany = count;howmany > 1;howmany--) {
                  count = howmany;
                  newval = newval * count;
               }
            }
         }
         else if (strcmp(name,"EXP") == 0) {
            calcdata(&newval);
            newval = exp(newval);
         }
         else if (strcmp(name,"LOG") == 0) {
            calcdata(&newval);
            if (newval < 0.0) {
               errchk(7);                      /* Neg value for LOG */
               newval = 0.0;
            }
            else newval = log(newval);
         }
         else if (strcmp(name,"SIN") == 0){
            calcdata(&newval);
            newval = sin(newval);
         }
         else if (strcmp(name,"COS") == 0){
            calcdata(&newval);
            newval = cos(newval);
         }
         else if (strcmp(name,"ATAN") == 0){
            calcdata(&newval);
            newval = atan(newval);
         }
         else {
            errchk(6);                        /* function not found */
            newval = 0.0;
         }
         col--;
         return(newval);
      }                                     /* end of function call */

      else {                                       /* variable name */
         for (i = 0;i < 12;i++) {
            if ((strcmp(name,allvars[i].varname)) == 0) {
               newval = allvars[i].value;
               return(newval);
            }
         }
         errchk(3);                           /* variable not found */
         newval = 0.0;
         return(newval);
      }                                     /* end of variable call */

   }                            /* end of variable or function call */
   errchk(9);
   return(0.0);                               /* invalid data found */
}

/* ********************************************************** getop */
/* A call to this function expects to find an operator, end of line */
/* or a close parenthesis.                                          */
char getop(void)
{
   while (inline[col] == ' ') col++;               /* ignore blanks */
   switch (inline[col]){
      case '$':
      case ')':
      case  0 :
      case '+':
      case '-':
      case '*':
      case '/': break;
      default : errchk(4);                      /* invalid operator */
   }
   return(inline[col]);
}

/* ********************************************************* errchk */
/* The error code is stored by this function and the cursor column  */
/* is adjusted in an attempt to put the cursor near the error that  */
/* caused the problem.                                              */
void errchk(int err)
{
   if(errcode) return;                       /* error already found */
   else {                         /* only the first error is stored */
      errcode = err;                      /* store the error number */
      switch (err){
         case 2  :
         case 4  :
         case 8  :
         case 9  :
         case 10 :
         case 11 :
         case 12 : colerr = col;
                   break;
         case 1  :
         case 3  : colerr = col - 1;
                   break;
         case 6  : colerr = col - 2;
                   break;
         case 5  :
         case 7  : colerr = col - 3;
                   break;
         default : colerr = col;
      }
   }
}
