/*
  Funktionsplotter in Storm C

  Function-Modul

*/

#include <iostream.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>

#include "fclass.h"

// Destruktoren:

Func::~Func() { }       // Default: garnix

UnOpN::~UnOpN()
 { delete arg; }

BinOpN::~BinOpN()
 { delete l;
   delete r;
 }

// Konstruktoren:

double Func::Dummy;

KonstN::KonstN (double c)
 { Konst = c; }

UnOpN::UnOpN(UnOps op, Func *n):
 oper(op), arg(n)
{ }

BinOpN::BinOpN(BinOps op, Func *ln, Func *rn):
 oper(op), l(ln), r(rn)
{ }

// "Konstant"-Funktionen:

int Func::isconst (double &retval)
  { return 0; }

int KonstN::isconst (double &retval)
  { retval = Konst;
    return 1;
  }

// Ausgabe

#define EPANIC 26731

void KonstN::print(char*)
 {
   printf("%g", Konst);
 }

void VarN::print(char*)
 { printf("x"); }

void ErrorN::print(char*)
 { printf("?"); }

void UnOpN::print(char* str)
  { switch(oper)
    { case Op_neg: printf("-"); break;
      case Op_sqr: printf("sqr "); break;
      case Op_sqrt: printf("sqrt "); break;
      case Op_sin: printf("sin "); break;
      case Op_cos: printf("cos "); break;
      case Op_exp: printf("exp "); break;
      case Op_ln:  printf("ln "); break;
    }
    arg->print(str);
  }

void BinOpN::print(char* str)
  { printf("(");
    l->print(str);
    switch (oper)
    { case Op_add: printf("+"); break;
      case Op_sub: printf("-"); break;
      case Op_mult:printf("*"); break;
      case Op_div: printf("/"); break;
      case Op_pot: printf("^"); break;
      default: exit(EPANIC);
    }
    r->print(str);
    printf(")");
  }

// Auswertung

inline double sqr(double x)
  { return x*x; }

double intpotz(double base, int exp)
  { if (exp)
      { if (exp<0) return 1.0/intpotz(base,-exp)
        else if (exp==1) return base;
        else if (exp&1) return base*sqr(intpotz(base, exp/2));
                        else return sqr(intpotz(base, exp/2));
       }
     else return 1;
  }

double UnOpN::eval (double x)
 {
   double y = arg->eval(x);

   switch(oper)
   { case Op_neg:  return -y;
     case Op_sqr:  return y*y;
     case Op_sqrt: return sqrt(y);
     case Op_sin:  return sin(y);
     case Op_cos:  return cos(y);
     case Op_exp:  return exp(y);
     case Op_ln:   return log(y);
     default: exit(EPANIC);
   }
}

double BinOpN::eval (double x)
 { double lv = l->eval(x), rv = r->eval(x);
   switch(oper)
   { case Op_add:  return lv+rv;
     case Op_sub:  return lv-rv;
     case Op_mult: return lv*rv;
     case Op_div:  return lv/rv;
     case Op_pot:  if (r->isconst() && floor(rv)==rv)
                     return intpotz(lv, int(rv))
                   else if (lv > 0) return exp(rv*log(lv));
                                    return 0;
     default: exit(EPANIC);
   }
 }


// ******** Parsing ********

#define EULER 2.7182818284
//#define PI 3.141592653589

char *cp;       // Zeiger auf String
char c;         // gescanntes Zeichen
short ErrFlag;  // Fehler aufgetreten?


char Get()
{ while ((c = *cp) == ' ')
    cp++;
  if(c) cp++;
  return c;
}

Func *Fehler()
 { if (!ErrFlag)
     { cout << "\nError.\n";
       ErrFlag = 1;
     }
   return new ErrorN;
 }

Func *Expression();

Func *Term();

Func *Potenz();

Func *Factor()
{ while (c=='+') Get();

  if (c=='x' || c=='X')
    { Get();
      return new VarN;
    }
  else if (c >= '0' && c <= '9')
    {
      int i = 0;
      enum {max = 80};
      char numstr[max+1];

      while (isdigit(c) && i<max)
        { numstr[i++] = c; Get(); }

      if (c=='.')
        { numstr[i++] = c; Get(); }
      while (isdigit(c) && i<max)
        { numstr[i++] = c; Get(); }

      if (tolower(c)=='e')
        { numstr[i++] = c; Get();

          if (c=='+' || c=='-')
            { numstr[i++] = c; Get(); }
          else
          if (!isdigit(c))
            return Fehler();

          while (isdigit(c) && i<max)
            { numstr[i++] = c; Get(); }

        }

      numstr[i]=0;
      return new KonstN(atof(numstr));
     }
   else
   if (c=='(')
     { Get();
       Func *np = Expression();
       if (c==')')
         Get();
       else
         delete Fehler();
       return np;
     }
   else
   if (c=='c')
     { if (Get()=='o' && Get() == 's')
         { Get(); return new UnOpN(Op_cos, Factor()); }
       else
         return Fehler();
     }
   else
   if (c=='e')
     { if (Get()!='x')
         return new KonstN(EULER);
       else if (Get() == 'p')
         { Get(); return new UnOpN(Op_exp, Factor()); }
       else
         return Fehler();
     }
   else if (c=='l')
     { if (Get()=='n')
         return Get(), new UnOpN(Op_ln, Factor());
       else
         return Fehler();
     }
   else if (c=='p')
     { if (Get()!='i')
         return Fehler();
       Get(); return new KonstN(PI);
     }
   else if (c=='s')
     { Get();
       if (c=='i')
         { if (Get() != 'n') return Fehler();
           Get();
           return new UnOpN(Op_sin, Factor());
         }
       else if (c=='q')
         { if (Get() != 'r') return Fehler();
           if (Get()=='t')
             { Get();
               return new UnOpN(Op_sqrt, Factor());
             }
           else return new UnOpN(Op_sqr, Factor());
         }
       else
         return Fehler();
     }

   // Default-Ergebnis:
   return Fehler();
}

Func *Potenz()
{
  Func *n1 = Factor();

  while (c=='^')
    { Get();
      Func *n2 = Factor();
      n1 = new BinOpN(Op_pot, n1, n2);
    }
  return n1;
}

Func *Term()
{ Func *n1 = Potenz();

  while (c=='*' || c=='/')
    { BinOps op = c=='*'? Op_mult : Op_div;
      Get();
      Func *n2 = Potenz();
      n1 = new BinOpN(op, n1, n2);
    }
  return n1;
}

Func *Expression()
{
  if (c=='-')
   { Get();
     Func *np = Term();
       return new UnOpN(Op_neg,np);
   }
  else
   { Func *n1 = Term();
     while (c=='+' || c=='-')
       { BinOps op = c=='+'? Op_add : Op_sub;
         Get();
         Func *n2 = Term();
         n1 = new BinOpN(op, n1, n2);
       }
     return n1;
   }
}

Func *Parse(char *string)
{ cp = string;
  ErrFlag = 0;

  Get();
  Func *expr = Expression();
  if (c) delete Fehler();

  if(ErrFlag)
    { delete expr; return 0; }
  else
    return expr;
}


