/*
  Funktionsplotter in Maxon C++

  Function-Modul

  Jens Gelhar 09.11.91
*/


#include <exec/types.h>
#include <intuition/intuition.h>

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

#include "fclass.h"

// ******** Prototypen ********

struct Window *OpenWindow( struct NewWindow *newWindow );
void CloseWindow( struct Window *window );

void SetAPen( struct RastPort *rp, unsigned long pen );
void Move( struct RastPort *rp, long x, long y );
void Draw( struct RastPort *rp, long x, long y );

struct Message *WaitPort( struct MsgPort *port );

// ******** Pragmas ***********

#pragma amicall(SysBase, 384, WaitPort(a0))

#pragma amicall(GfxBase, 240, Move(a1,d0,d1))
#pragma amicall(GfxBase, 246, Draw(a1,d0,d1))
#pragma amicall(GfxBase, 342, SetAPen(a1,d0))

#pragma amicall(IntuitionBase, 204, OpenWindow(a0))
#pragma amicall(IntuitionBase, 72, CloseWindow(a0))

// ******** Zeichenroutine ********

// Windowdimensionen:
#define WW 640
#define WH 190


// 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;
}

#pragma break -

void plot(Func *f, double minx, double maxx, double miny, double maxy)
{
  NewWindow mynewwin =
   { 0, 0, WW, WH+10, 2, 1, IDCMP_CLOSEWINDOW,
     WFLG_ACTIVATE | WFLG_SIZEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_GIMMEZEROZERO,
     NULL, NULL, " Plot ++ ", NULL, NULL, 100, 50, 640, 200, WBENCHSCREEN};

  struct Window* mywin = OpenWindow( &mynewwin);
  if (!mywin) exit(1);

  RastPort *RP = ( RastPort *)mywin->RPort;

  double XS = WW / (maxx-minx), YS = WH / (maxy-miny);

  int X0 = int(-XS*minx), Y0 = int(-YS*miny);

  SetAPen( ( RastPort *)mywin->RPort, 3);
  if (miny < 0 && maxy > 0)
    { // y-Achse zeichnen:
      Move(RP,  0, WH-Y0);
      Draw(RP, WW, WH-Y0);
    }

  if (minx < 0 && maxx > 0)
    { // x-Achse zeichnen:
      Move(RP, X0, 0);
      Draw(RP, X0, WH);
    }

  SetAPen( ( RastPort *)mywin->RPort, 0);
  Move( ( RastPort *)mywin->RPort, 0, 0);

  for (int i=0; i<WW; i++)
    { double x = minx+i/XS, y = f->eval(x);
      int yp = int(YS*y+Y0);

      if (yp > -WH && yp < 2*WH) Draw(RP, i, WH-yp);
      SetAPen(RP, 1);
    }

  WaitPort(mywin->UserPort);
  CloseWindow(mywin);
}

#pragma break +

void main()
{
  char input[100], buf[200];

  cout << "\nPlot ++\nGeschrieben von Jens Gelhar mit Maxon C++\n\n";

  cout << "Funktion: f(x) = "; cin.getline(input, 100);

  Func *MyExp = Parse(input);

  if (MyExp)
    { double minX, maxX, minY, maxY;

      for(;;)
       { cout << "X-Bereich: von "; cin >> minX;
         cout << "           bis "; cin >> maxX;
         if (minX < maxX) break;
         cout << "Das Minimum muß kleiner als das Maximum sein.\n";
       }

      for(;;)
       { cout << "Y-Bereich: von "; cin >> minY;
         cout << "           bis "; cin >> maxY;
         if (minY < maxY) break;
         cout << "Das Minimum muß kleiner als das Maximum sein.\n";
       }

      cout << "\nFunktion: "; MyExp->print(buf); cout << "\n";

      plot(MyExp, minX, maxX, minY, maxY);

      delete MyExp;
    }
  else
    cout << "Versuch's doch gleich noch mal!\n";

}

