/*

Copyright (C) 2002  Paul Wilkins

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
/* funcs.c  by Paul Wilkins */

#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>

#include "stack.h"
#include "number.h"
#include "funcs.h"
#include "undo.h"
#include "constant.h"
#include "editor.h"
#include "run_cmd.h"
#include "error.h"
#include "mode.h"

void decomposeMtrx();
void createMtrx();
Number * readNumber(char *line, int *nread);
int isDigitBase(char c);

void readLine(char *line){
   int pos;  /* the number of characters in the number */
   Number *n1;

   if((*line != '"' && *line != '\'') && (isDigitBase(*line) || *line == '-' || *line == '+' || *line == '.')){

      /* try to read in a number */
      if(NULL == (n1 = readNumber(line, &pos))){
	 setStringError("Error: Cannot read number.");
         return;
      }

      SaveStackState(0);  /* what to restore to the stack after an undo */
      Push(n1);
      UndoStackState(1);  /* what to undo from the stack after an undo */

      /* what's left must be a command */
      if(strlen(line) > pos) runCmd(line+pos);

   } else {

      /* this is a command */
      runCmd(line);
   }
}



void DupStack(){
   Number *n1;

   finishEditor();

   if(checkArgs("Dup", 1)) return;
   SaveStackState(0);  /* what to restore to the stack after an undo */

   n1 = getStackEle(0);
   Push(setNumberNumber(newNumber(), n1));

   UndoStackState(1);  /* what to undo from the stack after an undo */
}

void PopStack(){
   if(checkArgs("Drop", 1)) return;
   SaveStackState(1);

   freeNumber(Pop());

   UndoStackState(0);
}

void PushStack(){
   if(isEditingEditor()) finishEditor();
   else DupStack();

}

void PiStack(){
   Number *n1;

   finishEditor();

   SaveStackState(0);

   n1 = setNumberReal(newNumber(), realPi);
   Push(n1);

   UndoStackState(1);
}

void NegStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("+/-", 1)) return;
   SaveStackState(1);

   n2 = negNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void InvStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("Inv", 1)) return;
   SaveStackState(1);

   n2 = invNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}


void LnStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("ln", 1)) return;
   SaveStackState(1);

   n2 = lnNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void LogStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("log", 1)) return;
   SaveStackState(1);

   n2 = logNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void TenxStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("10^x", 1)) return;
   SaveStackState(1);

   n3 = setNumberReal(newNumber(), realTen);

   n2 = powNumber(n3, (n1=Pop()));
   freeNumber(n3);

   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1);
      CancelStackState();
   }
}

void ExpStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("exp", 1)) return;
   SaveStackState(1);

   n2 = expNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void PowStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("pow", 2)) return;
   SaveStackState(2);

   n1 = Pop();

   n3 = powNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void NrootStack(){
   Number *n1, *n2, *n3, *n4;

   finishEditor();

   if(checkArgs("nroot", 2)) return;
   SaveStackState(2);

   n4 = invNumber((n1=Pop()));
   if(NULL == n4){
      Push(n1);
      CancelStackState();
      return;
   }

   n3 = powNumber((n2=Pop()), n4);
   freeNumber(n4);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void SqrStack(){
   Real *r1;
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("sqr", 1)) return;
   SaveStackState(1);

   r1 = setRealDouble(newReal(), 2.0);
   n3 = setNumberReal(newNumber(), r1);
   freeReal(r1);

   n2 = powNumber((n1=Pop()), n3);
   freeNumber(n3);

   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1);
      CancelStackState();
   }
}

void SqrtStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("sqrt", 1)) return;
   SaveStackState(1);

   n3 = setNumberReal(newNumber(), realHalf);

   n2 = powNumber((n1=Pop()), n3);
   freeNumber(n3);

   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1);
      CancelStackState();
   }
}

void Db10Stack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("db10", 1)) return;
   SaveStackState(1);

   n1 = Pop();

   n2 = dbNumber(n1, 10.0);
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1);
      CancelStackState();
   }
}

void Db20Stack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("db20", 1)) return;
   SaveStackState(1);

   n1 = Pop();

   n2 = dbNumber(n1, 20.0);
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1);
      CancelStackState();
   }
}

void RipStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("rip", 2)) return;
   SaveStackState(2);

   n1 = Pop();

   n3 = ripNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}


void SinStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("sin", 1)) return;
   SaveStackState(1);

   n2 = sinNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void CosStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("cos", 1)) return;
   SaveStackState(1);

   n2 = cosNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void TanStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("tan", 1)) return;
   SaveStackState(1);

   n2 = tanNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void AsinStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("asin", 1)) return;
   SaveStackState(1);

   n2 = asinNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void AcosStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("acos", 1)) return;
   SaveStackState(1);

   n2 = acosNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void AtanStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("atan", 1)) return;
   SaveStackState(1);

   n2 = atanNumber((n1=Pop()));
   if(n2){
      Push(n2); freeNumber(n1);
      UndoStackState(1);
   } else {
      Push(n1); 
      CancelStackState();
   }
}

void AddStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("+", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n3 = addNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void SubStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("-", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n3 = subNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void MulStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("*", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n3 = mulNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void DivStack(){
   Number *n1, *n2, *n3;

   finishEditor();

   if(checkArgs("/", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n3 = divNumber((n2=Pop()), n1);
   if(n3){
      Push(n3); freeNumber(n1); freeNumber(n2);
      UndoStackState(1);
   } else {
      Push(n2); Push(n1);
      CancelStackState();
   }
}

void SwapStack(){
   Number *n1, *n2;

   finishEditor();

   if(checkArgs("swap", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n2 = Pop();
   Push(n1);
   Push(n2);

   UndoStackState(2);
}

void CplxStack(){
   Number *n1, *n2;
   Cmplx *c1;

   finishEditor();

   if(checkArgs("cplx", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n2 = Pop();
   if(n1->type != REAL || n2->type != REAL){
      setStringError("-> complex Error: Bad Argument Type.");
      Push(n2);
      Push(n1);
      CancelStackState();
      return;
   }
   c1 = inputCmplxReal(newCmplx(), n2->data, n1->data);
   freeNumber(n1); freeNumber(n2);
   Push(setNumberCmplx(newNumber(), c1));
   freeCmplx(c1);

   UndoStackState(1);
}

/* either make a matrix or decompose a matrix */
void MtrxStack(){
   Number *n1;

   finishEditor();

   if(checkArgs("matrix", 1)) return;

   n1 = getStackEle(0);

   switch(n1->type){
      case COMPLEX:
         setStringError("matrix Error: Bad Argument Type.");
         break;
      case MATRIX:
         decomposeMtrx();
         break;
      case REAL:
         createMtrx();
         break;
   }
}


void decomposeMtrx(){
   int i, j, matSize;
   Number *n1, **ptr;
   Real *rs, *cs;
   Matrix *m1;

   if(checkArgs("matrix", 1)) return;
   SaveStackState(1);

   n1 = Pop();
   m1 = (Matrix *)n1->data;
   matSize = m1->rows * m1->cols;

   for(i=0; i<m1->rows; i++){
      for(j=0; j<m1->cols; j++){
         ptr = m1->data + (m1->cols * i) + j;
         Push(setNumberNumber(newNumber(), *ptr));
      }
   }
   Push(setNumberReal(newNumber(), setRealDouble((cs=newReal()), m1->cols)));
   Push(setNumberReal(newNumber(), setRealDouble((rs=newReal()), m1->rows)));
   freeReal(cs);
   freeReal(rs);

   freeNumber(n1);

   UndoStackState(2 + matSize);
}


void createMtrx(){
   int i, j;
   int rows, cols;
   Number *n1, *n2;
   Matrix *m1;

   if(checkArgs("matrix", 2)) return;
   SaveStackState(2);

   n1 = Pop();
   n2 = Pop();
   if(n1->type != REAL || n2->type != REAL){
      setStringError("matrix Error: Bad Argument Type.");
      Push(n2);
      Push(n1);
      CancelStackState();
      return;
   }
   rows = (int)setDoubleReal(n1->data);
   cols = (int)setDoubleReal(n2->data);
   if(rows == 0.0 || cols == 0.0){
      setStringError("matrix Error: Bad Argument Type.");
      Push(n2);
      Push(n1);
      CancelStackState();
      return;
   }

   if(checkArgs("matrix", rows * cols)){
      Push(n2);
      Push(n1);
      CancelStackState();
      return;
   }
   freeNumber(n1); freeNumber(n2);

   AddStackState(rows * cols);

   /* read in the matrix */
   m1 = newMatrix();

   for(i=rows-1; i>=0; i--){
      for(j=cols-1; j>=0; j--){

         setMatrix(m1, (n1=Pop()), i, j);

         freeNumber(n1); 
      }
   }

   Push(setNumberMatrix(newNumber(), m1));

   freeMatrix(m1); 

   UndoStackState(1);
}

#define IS_HEX(c) (\
   (c)=='a' || (c)=='A' || \
   (c)=='b' || (c)=='B' || \
   (c)=='c' || (c)=='C' || \
   (c)=='d' || (c)=='D' || \
   (c)=='e' || (c)=='E' || \
   (c)=='f' || (c)=='F' || \
   isdigit((c)))
#define IS_DEC(c) isdigit((c))
#define IS_OCT(c) (\
   (c)=='0' || (c)=='1' || \
   (c)=='2' || (c)=='3' || \
   (c)=='4' || (c)=='5' || \
   (c)=='6' || (c)=='7')
#define IS_BIN(c) ((c)=='0' || (c)=='1') 

int isDigitBase(char c){
   switch(getBaseMode()){
      case HEXIDECIMAL:
         return IS_HEX(c);
         break;
      case DECIMAL:
         return IS_DEC(c);
         break;
      case OCTAL:
         return IS_OCT(c);
         break;
      case BINARY:
         return IS_BIN(c);
         break;
   }
}


Number * readNumber(char *line, int *nread){
   char *p, buf[2];
   int i1;
   double d1, num;
   Real *r1;
   Number *n1;

   /* read in the number */
   switch(getBaseMode()){
      case HEXIDECIMAL:
         *nread = 0;
         /* look for "0x" */
         if(0 == strncmp(line, "0x", 2)){
            line+=2;
            *nread = 2;
         }
         num = 0.0;
         buf[1] = '\0';
         for(p=line; IS_HEX(*p); p++, (*nread)++){
            buf[0] = *p;
            sscanf(buf, "%x", &i1);
            num *= 16.0;
            num += (double)i1;
         }
         if(p-line == 0) return NULL;
	 break;
      case DECIMAL:
	 /* the line starts with a number-- read it in */
	 if(1 != sscanf(line, "%lg%n", &num, nread)){
	    return NULL;
	 }
	 break;
      case OCTAL:
         *nread = 0;
         num = 0.0;
         buf[1] = '\0';
         for(p=line; IS_OCT(*p); p++, (*nread)++){
            buf[0] = *p;
            sscanf(buf, "%d", &i1);
            num *= 8.0;
            num += (double)i1;
         }
         if(p-line == 0) return NULL;
	 break;
      case BINARY:
         *nread = 0;
         num = 0.0;
         for(p=line; IS_BIN(*p); p++, (*nread)++){
            num *= 2.0;
            if(*p == '1') num += 1.0;
         }
         if(p-line == 0) return NULL;
	 break;
   }


   r1 = setRealDouble(newReal(), num);
   n1 = setNumberReal(newNumber(), r1);
   freeReal(r1);

   return n1;
}




syntax highlighted by Code2HTML, v. 0.9.1