//  $Id: sheet_calc.cc,v 1.7 1998/08/06 21:08:50 aml Exp $

#include "sheet.hh"
#include "canvas.hh"

void Sheet::manipulateReferences(Cell *cell, int operation, Oper_info *info) {
    int i;
    unsigned char *aux;
    int typ;
    double *pt_fp;
    Ref *pt_ref,ref;
    short *pt_int;
    char *pt_st;
    Range *pt_rg;
    Stack_elem *new_top;
    double x1,x2;
    int cnt;
    Formula *form ;
    Sheet *sheet;
    Cell *cell2;
    short r,c;
    Range_iter ri;
    Range rg;
    
    
    sheet = this;

    i = 0;

    form = cell->formula;
    aux = (unsigned char *)form->formula;

    while (aux - (unsigned char *) form->formula < form->size) {
        typ = *aux;
        aux++;

        switch(typ) {
          case FORM_FP:
            aux += FORM_FP_SIZE;
            break;
            
          case FORM_REF:
            memcpy(&ref,aux,sizeof(Ref));
            c = cell_address(ref.col,cell->col);
            r = cell_address(ref.row,cell->row);
            if (CHECK_COL_BOUNDS(c) &&
                CHECK_ROW_BOUNDS(r)) {
              cell2 = cellFind(c,r);
              if (operation == INSERT_REFERENCES)
                cell2->addDependence(cell);
              else if (operation == REMOVE_REFERENCES)
                cell2->removeDependence(cell);
              else if (operation == INSERT_ROW_OR_COLUMN) 
                cell->changeCellReference(aux,info,c,r);
            }
            aux += FORM_REF_SIZE;
            break;
            
            
          case FORM_INT:
            aux += FORM_INT_SIZE;
            break;
            
          case FORM_RANGE:
            memcpy(&rg,aux,sizeof(Range));
            ri.init(rg);
            cnt = 0;
            if (operation == INSERT_REFERENCES ||
                operation == REMOVE_REFERENCES) 
                for(ri.first(ref); !ri.last();  ri.next(ref)){
                    c = cell_address(ref.col,cell->col);
                    r = cell_address(ref.row,cell->row);
                    if (CHECK_COL_BOUNDS(c) &&
                        CHECK_ROW_BOUNDS(r)) {
                      cell2 = cellFind(c,r);
                      if (operation == INSERT_REFERENCES)
                        cell2->addDependence(cell);
                      else if (operation == REMOVE_REFERENCES)
                        cell2->removeDependence(cell);
                    }
                    
                }
            else if (operation == INSERT_ROW_OR_COLUMN) {
                c = cell_address(rg.start.col,cell->col);
                r = cell_address(rg.start.row,cell->row);
                cell->changeCellReference(aux,info,c,r);
                c = cell_address(rg.end.col,cell->col);
                r = cell_address(rg.end.row,cell->row);
                cell->changeCellReference(aux+4,info,c,r);
            }
            aux += FORM_RANGE_SIZE;
            break;
            
          case FORM_RETURN:
            return;
            
          case FORM_PAR:
            break;

          case FORM_STRING:
            aux += strlen((char*)aux)+1;
            break;
            
          case FORM_UNMINUS:
          case FORM_UNARY_MINUS:
            break;

            /* All operators with a fixed number of arguments */
          case FORM_PLUS:
          case FORM_SUB:
          case FORM_MULT:
          case FORM_DIV:
          case FORM_POWER:
          case FORM_ABS:
          case FORM_INTF:
          case FORM_SQRT:
          case FORM_LOG:
          case FORM_LN:
          case FORM_PI:
          case FORM_SIN:
          case FORM_COS:
          case FORM_TAN:
          case FORM_ATAN2:
          case FORM_ATAN:
          case FORM_ASIN:
          case FORM_ACOS:
          case FORM_EXP:
          case FORM_MONTH:
          case FORM_DAY:
          case FORM_SECOND:
          case FORM_HOUR:
          case FORM_MINUTE:
          case FORM_MOD:
            break;

          case FORM_GREATER:
          case FORM_SMALLER:
          case FORM_EQUAL:
          case FORM_GREATEREQ:
          case FORM_SMALLEREQ:
          case FORM_NEQUAL:
          case FORM_AND:
          case FORM_OR:
            break;

          case FORM_NOT:
            break;

          case FORM_ROUND:
          case FORM_UPPER:
          case FORM_LOWER:
          case FORM_PROPER:
          case FORM_VLOOKUP:
          case FORM_HLOOKUP:
          case FORM_IF:
            break;

            /* All functions with a variable number of arguments are similar */
          case FORM_SUM:
          case FORM_SUMIF:
          case FORM_COUNT:
          case FORM_MIN:
          case FORM_MAX:
            aux++;
            break;
          default:
            cerr << "Unknown formula item code " << typ << "\n";
            return;
            internal_error();
            
        }
    }
}


Stack_elem *Sheet::evaluate(Cell *cell) {
    int i;
    unsigned char *aux;
    int typ;
    double *pt_fp;
    Ref *pt_ref,ref;
    short *pt_int;
    char *pt_st;
    Range *pt_rg;
    Stack_elem *new_top;
    double x1,x2;
    int i1,i2;
    int cnt;
    Formula *form ;
    Sheet *sheet;
    int nargs;
    
    sheet = this;

    i = 0;

    form = cell->formula;
    aux = (unsigned char *)form->formula;

    top = stack;


    while (aux - (unsigned char *) form->formula < form->size) {
        typ = *aux;
        aux++;
        top->type = typ;
        top->contents.error_code = NO_ERROR;

        switch(typ) {
          case FORM_FP:
            pt_fp = (double*)aux;
            memcpy(&(top->contents.fp_val),aux,sizeof(double));
            top->type = FORM_FP;
            top++;
            aux += FORM_FP_SIZE;
            break;
            
          case FORM_REF:
            memcpy(&ref,aux,sizeof(Ref));
            new_top = cellRef(cell_address(ref.col,cell->col),
                            cell_address(ref.row,cell->row));
            *top = *new_top;
            propagate_error(new_top,top);
            new_top->type = FORM_REF;
            top++;
            aux += FORM_REF_SIZE;
            

            break;
            
            
          case FORM_INT:
            memcpy(&(top->contents.int_val),aux,sizeof(short));
            top->type = FORM_INT;
            top++;
            aux += FORM_INT_SIZE;
            break;
            
          case FORM_RANGE:
/*            pt_rg = (Range*) aux;
            top->contents.rg_val = *pt_rg; */
            memcpy(&(top->contents.rg_val),aux,sizeof(Range));
            top++;
            aux += FORM_RANGE_SIZE;
            top->type = FORM_RANGE;
            break;
            
          case FORM_RETURN:
            /* Formula ended */
            return(top-1);
            break;
            
          case FORM_PAR:
            break;

          case FORM_STRING:
            cnt = strlen((char*)aux);
            memcpy(top->contents.string_val,aux,cnt+1);
            aux += cnt+1;
            top->type = FORM_STRING;
            top++;
            break;
            
          case FORM_UNMINUS:
          case FORM_UNARY_MINUS:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
              new_top->contents.fp_val = - stack_value(top-1);
              new_top->type = FORM_FP;
            }
            top = new_top+1;
            break;
            /* All operators with two arguments */
          case FORM_PLUS:
          case FORM_SUB:
          case FORM_MULT:
          case FORM_DIV:
          case FORM_POWER:
            new_top = top-2;
            /* Tricky decision here. Any operation becomes a double result */

            if (!propagate_error(top-1,new_top) &&
                !propagate_error(top-2,new_top)) {
              x1 = stack_value(top-2);
              x2 = stack_value(top-1);
              SET_ERROR(new_top, NO_ERROR);
              new_top->type = FORM_FP;
              switch(typ) {  
                case FORM_PLUS:
                  new_top->contents.fp_val = x1+x2; break;
                case FORM_SUB:
                  new_top->contents.fp_val = x1-x2; break;
                case FORM_MULT:
                  new_top->contents.fp_val = x1*x2; break;
                case FORM_DIV:
                  if (x2 == 0) {
                      SET_ERROR(new_top,ERROR_DIV_BY_0);
                  } else
                      new_top->contents.fp_val = x1/x2; 
                  break;
                case FORM_POWER:
                  if (x1 < 0) 
                      SET_ERROR(new_top,ERROR_BAD_VALUE)
                  else {
                      if (x1 > 0 && (log(x1)*x2 > UPPER_EXP_LIMIT ||
                          log(x1)*x2 < - UPPER_EXP_LIMIT)) 
                          SET_ERROR(new_top,ERROR_BAD_VALUE)
                      else 
                          new_top->contents.fp_val = pow(x1,x2); 
                  }
                  break;
              }
            }
            top = new_top+1;
            break;

          case FORM_GREATER:
          case FORM_SMALLER:
          case FORM_EQUAL:
          case FORM_GREATEREQ:
          case FORM_SMALLEREQ:
          case FORM_NEQUAL:
          case FORM_AND:
          case FORM_OR:
            new_top = top-2;
            /* Tricky decision here. Any operation becomes a double result */
            if (!propagate_error(top-1,new_top) &&
                !propagate_error(top-2,new_top)) {
              x1 = stack_value(top-2);
              x2 = stack_value(top-1);
                SET_ERROR(new_top, NO_ERROR);
                switch(typ) {  
                  case FORM_EQUAL:
                    if (x1 == x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_GREATER:
                    if (x1 > x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_SMALLER:
                    if (x1 < x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_NEQUAL:
                if (x1 != x2) new_top->contents.int_val = 1;
                else new_top->contents.int_val = 0;
                    break;
                  case FORM_SMALLEREQ:
                    if (x1 <= x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_GREATEREQ:
                    if (x1 >= x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_AND:
                    if (x1 && x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                  case FORM_OR:
                    if (x1 || x2) new_top->contents.int_val = 1;
                    else new_top->contents.int_val = 0;
                    break;
                }
            }
            new_top->type = FORM_INT;
            top = new_top+1;
            break;
            /* Simple functions with a single argument */

          case FORM_NOT:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1) new_top->contents.int_val = 0;
                else new_top->contents.int_val = 1;
            }
            new_top->type = FORM_INT;
            new_top = new_top+1;
            break;
          case FORM_ABS:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = fabs(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_INTF:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = floor(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_SQRT:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 < 0) {
                    SET_ERROR(new_top,ERROR_BAD_VALUE);
                }
                else 
                    new_top->contents.fp_val = sqrt(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_LOG:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 < 0) {
                    SET_ERROR(new_top,ERROR_BAD_VALUE);
                }
                else 
                    new_top->contents.fp_val = log10(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_LN:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 < 0) {
                    SET_ERROR(new_top,ERROR_BAD_VALUE);
                }
                else 
                    new_top->contents.fp_val = log(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_PI:
            new_top = top;
            new_top->contents.fp_val = M_PI;
            new_top->type = FORM_FP;
            top = new_top+1;
            break;
          
          case FORM_SIN:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = sin(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_COS:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = cos(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_TAN:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = tan(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_ATAN2:
            new_top = top-2;
            if (!propagate_error(top-1,new_top) &&
                !propagate_error(top-2,new_top)) {
                double x;
                x1 = stack_value(top-2);
                x2 = stack_value(top-1);

                if (x2 == 0) 
                    SET_ERROR(new_top,ERROR_DIV_BY_0)
                else {
                    x = x1/x2;
                    if (x <= -M_PI/2.0 || x >= M_PI/2.0)
                        SET_ERROR(new_top,ERROR_BAD_VALUE)
                    else 
                        new_top->contents.fp_val = atan(x);
                }
            }
            new_top->type = FORM_FP;
            top = new_top+1;
            break;
          
          case FORM_ATAN:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 <= -M_PI/2.0 || x1 >= M_PI/2.0)
                    SET_ERROR(new_top,ERROR_BAD_VALUE)
                else 
                    new_top->contents.fp_val = tan(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_ASIN:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 <= -1.0 || x1 >= 1.0)
                    SET_ERROR(new_top,ERROR_BAD_VALUE)
                else 
                    new_top->contents.fp_val = asin(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_ACOS:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 <= -1.0 || x1 >= 1.0)
                    SET_ERROR(new_top,ERROR_BAD_VALUE)
                else 
                    new_top->contents.fp_val = acos(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;

          case FORM_EXP:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                if (x1 > UPPER_EXP_LIMIT ||
                    x1 < - UPPER_EXP_LIMIT)
                    SET_ERROR(new_top,ERROR_BAD_VALUE)
                else
                    new_top->contents.fp_val = exp(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          
          case FORM_DAY:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = day_function(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          case FORM_MONTH:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = month_function(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          case FORM_MINUTE:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = minute_function(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          case FORM_HOUR:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = hour_function(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;
          case FORM_SECOND:
            new_top = top-1;
            if (!propagate_error(top-1,new_top)) {
                x1 = stack_value(top-1);
                new_top->contents.fp_val = second_function(x1);
            }
            new_top->type = FORM_FP;
            new_top = new_top+1;
            break;

          case FORM_MOD:
            new_top = top-2;
            if (!propagate_error(top-2,new_top) &&
                !propagate_error(top-1,new_top)) {
                i1 = (int) stack_value(top-2);
                i2 = (int) stack_value(top-1);
                if (i2 == 0) 
                    SET_ERROR(new_top,ERROR_DIV_BY_0)
                else 
                    new_top->contents.int_val = i1 % i2;
            }
            new_top->type = FORM_INT;
            top = new_top+1;
            break;
          
            /* All functions with a fixed number of arguments are similar */
          case FORM_ROUND:
            nargs = 2;
            EVAL_FIXED_ARGS(Round,this,cell->col,cell->row,nargs);
            break;

          case FORM_UPPER:
            nargs = 1;
            EVAL_FIXED_ARGS(Upper,this,cell->col,cell->row,nargs);
            break;
          case FORM_LOWER:
            nargs = 1;
            EVAL_FIXED_ARGS(Lower,this,cell->col,cell->row,nargs);
            break;
          case FORM_PROPER:
            nargs = 1;
            EVAL_FIXED_ARGS(Proper,this,cell->col,cell->row,nargs);
            break;
          case FORM_VLOOKUP:
            nargs = 3;
            EVAL_FIXED_ARGS(Vlookup,this,cell->col,cell->row,nargs);
            break;
          case FORM_HLOOKUP:
            nargs = 3;
            EVAL_FIXED_ARGS(Hlookup,this,cell->col,cell->row,nargs);
            break;


            /* All functions with variable number of arguments are similar */
          case FORM_IF:
            EVAL_VAR_NUM_ARGS(If,this,cell->col,cell->row);
            break;

          case FORM_SUM:
            EVAL_VAR_NUM_ARGS(Sum,this,cell->col,cell->row);
            break;

          case FORM_SUMIF:
            EVAL_VAR_NUM_ARGS(Sumif,this,cell->col,cell->row);
            break;

          case FORM_COUNT:
            EVAL_VAR_NUM_ARGS(Count,this,cell->col,cell->row);
            break;

          case FORM_MIN:
            EVAL_VAR_NUM_ARGS(Min,this,cell->col,cell->row);
            break;
          case FORM_MAX:
            EVAL_VAR_NUM_ARGS(Max,this,cell->col,cell->row);
            break;

          default:
            internal_error();
            
        }
    }
}


Stack_elem *Sheet::formula(Cell *cell) {
    int i;
    unsigned char *aux;
    int typ;
    double *pt_fp,fp;
    Ref *pt_ref,ref;
    short *pt_int,sho;
    char *pt_st;
    Range *pt_rg,rg;
    Stack_elem *new_top;
    string s1,s2;
    char buf[256];
    short c,r,c1,r1,c2,r2;
    Formula *form;
    int cnt;
    int nargs;

    i = 0;

    form = cell->formula;
    aux = (unsigned char *) form->formula;

    top = stack;


    while (aux - (unsigned char *)form->formula < form->size) {
        typ = *aux;
        aux++;

        switch(typ) {
          case FORM_FP:
            memcpy(&fp,aux,sizeof(double));
            
            sprintf(buf,"%g",fp);
            top->st_val = buf;
            top->type = FORM_STRING;
            top++;
            aux += FORM_FP_SIZE;
            break;
            
          case FORM_REF:
            {
              short cc,rr;
              memcpy(&ref,aux,sizeof(Ref));
              c = ref.col;
              r = ref.row;

              cc = cell_address(c,cell->col);
              rr = cell_address(r,cell->row);
              
              if (CHECK_COL_BOUNDS(cc) &&
                  CHECK_ROW_BOUNDS(rr)) {
                sprintf(buf,"%s%s%s%d",
                        IS_RELATIVE(c) ? "" : "$",
                        coltoa(cell_address(c,cell->col)),
                        IS_RELATIVE(r) ? "" : "$",
                        cell_address(r,cell->row)+1
                        ); 
              }
              else
                sprintf(buf,"#REF");
              top->st_val = buf;
              top->type = FORM_STRING;
              top++;
              aux += FORM_REF_SIZE;
              break;
            }
            
          case FORM_INT:
            memcpy(&sho,aux,sizeof(short));
            sprintf(buf,"%d",sho);
            top->st_val = buf;
            top->type = FORM_STRING;
            top++;
            aux += FORM_INT_SIZE;
            break;
            
          case FORM_RANGE:
            {
              short cc1,cc2,rr1,rr2;

              memcpy(&rg,aux,sizeof(Range));
              c1 = rg.start.col;
              r1 = rg.start.row;
              c2 = rg.end.col;
              r2 = rg.end.row;

              cc1 = cell_address(c1,cell->col);
              cc2 = cell_address(c2,cell->col);
              rr1 = cell_address(r1,cell->row);
              rr2 = cell_address(r2,cell->row);
              
              
              if (CHECK_COL_BOUNDS(cc1) &&
                  CHECK_ROW_BOUNDS(rr1)) {
                sprintf(buf,"%s%s%s%d",
                        IS_RELATIVE(c1) ? "" : "$",
                        coltoa(cc1),
                        IS_RELATIVE(r1) ? "" : "$",
                        rr1+1);
              }
              else 
                sprintf(buf,"#REF");
              top->st_val = buf;
              sprintf(buf,":");
              top->st_val += buf;
              if (CHECK_COL_BOUNDS(cc2) &&
                  CHECK_ROW_BOUNDS(rr2)) {
                sprintf(buf,"%s%s%s%d",
                        IS_RELATIVE(c2) ? "" : "$",
                        coltoa(cc2),
                        IS_RELATIVE(r2) ? "" : "$",
                        rr2+1
                        ); 
              }
              else
                sprintf(buf,"#REF");
              top->st_val += buf;
              top->type = FORM_STRING;
              top++;
              aux += FORM_RANGE_SIZE;
            }
            break;
            
          case FORM_RETURN:
            /* Formula ended */
            return(top-1);
            break;
            
          case FORM_PAR:
            new_top = top-1;
            s1 = new_top->st_val;
            new_top->st_val = "(";
            new_top->st_val += s1;
            new_top->st_val += ")";
            new_top->type = FORM_STRING;
            break;

          case FORM_STRING:
            top->st_val = "\"";
            top->st_val += (char *)aux;
            top->st_val += "\"";
            top->type = FORM_STRING;
            aux += strlen((char*)aux)+1;
            top++;
            break;

          case FORM_UNMINUS:
          case FORM_UNARY_MINUS:
            new_top = top-1;
            s1 = (top-1)->st_val;
            new_top->st_val = "-";
            new_top->st_val += s1;
            new_top->type = FORM_STRING;
            top = new_top+1;
            break;
            /* All operators with two arguments */
          case FORM_PLUS:
          case FORM_SUB:
          case FORM_MULT:
          case FORM_DIV:
          case FORM_EQUAL:
          case FORM_GREATER:
          case FORM_SMALLER:
          case FORM_NEQUAL:
          case FORM_GREATEREQ:
          case FORM_SMALLEREQ:
          case FORM_POWER:
            new_top = top-2;
            s1 = (top-2)->st_val;
            s2 = (top-1)->st_val;
            new_top->st_val = s1;
            switch(typ) {  
              case FORM_PLUS:
                new_top->st_val += "+";
                break;
              case FORM_SUB:
                new_top->st_val += "-";
                break;
              case FORM_MULT:
                new_top->st_val += "*";
                break;
              case FORM_DIV:
                new_top->st_val += "/";
                break;
              case FORM_POWER:
                new_top->st_val += "^";
                break;
              case FORM_EQUAL:
                new_top->st_val += "=";
                break;
              case FORM_GREATER:
                new_top->st_val += ">";
                break;
              case FORM_SMALLER:
                new_top->st_val += "<";
                break;
              case FORM_NEQUAL:
                new_top->st_val += "<>";
                break;
              case FORM_GREATEREQ:
                new_top->st_val += ">=";
                break;
              case FORM_SMALLEREQ:
                new_top->st_val += "<=";
                break;
            }
            new_top->st_val += s2;
            new_top->type = FORM_STRING;
            top = new_top+1;
            break;

            /* Operators with fixed number os arguments */
          case FORM_NOT:
            nargs = 1;
            STRING_FIXED_ARGS("@NOT",nargs);
            break;
          case FORM_ABS:
            nargs = 1;
            STRING_FIXED_ARGS("@ABS",nargs);
            break;
          case FORM_INTF:
            nargs = 1;
            STRING_FIXED_ARGS("@INT",nargs);
            break;
          case FORM_SQRT:
            nargs = 1;
            STRING_FIXED_ARGS("@SQRT",nargs);
            break;
          case FORM_LOG:
            nargs = 1;
            STRING_FIXED_ARGS("@LOG",nargs);
            break;
          case FORM_LN:
            nargs = 1;
            STRING_FIXED_ARGS("@LN",nargs);
            break;
          case FORM_PI:
            new_top = top;
            new_top->st_val = "@PI()";
            top = new_top+1;
            break;
          case FORM_SIN:
            nargs = 1;
            STRING_FIXED_ARGS("@SIN",nargs);
            break;
          case FORM_COS:
            nargs = 1;
            STRING_FIXED_ARGS("@COS",nargs);
            break;
          case FORM_TAN:
            nargs = 1;
            STRING_FIXED_ARGS("@TAN",nargs);
            break;
          case FORM_ATAN2:
            nargs = 2;
            STRING_FIXED_ARGS("@ATAN2",nargs);
            break;
          case FORM_ATAN:
            nargs = 1;
            STRING_FIXED_ARGS("@ATAN",nargs);
            break;
          case FORM_ASIN:
            nargs = 1;
            STRING_FIXED_ARGS("@ASIN",nargs);
            break;
          case FORM_ACOS:
            nargs = 1;
            STRING_FIXED_ARGS("@ACOS",nargs);
            break;
          case FORM_EXP:
            nargs = 1;
            STRING_FIXED_ARGS("@EXP",nargs);
            break;
          case FORM_DAY:
            nargs = 1;
            STRING_FIXED_ARGS("@DAY",nargs);
            break;
          case FORM_MONTH:
            nargs = 1;
            STRING_FIXED_ARGS("@MONTH",nargs);
            break;
          case FORM_MINUTE:
            nargs = 1;
            STRING_FIXED_ARGS("@MINUTE",nargs);
            break;
          case FORM_HOUR:
            nargs = 1;
            STRING_FIXED_ARGS("@HOUR",nargs);
            break;
          case FORM_SECOND:
            nargs = 1;
            STRING_FIXED_ARGS("@SECONDS",nargs);
            break;
          case FORM_MOD:
            nargs = 2;
            STRING_FIXED_ARGS("@MOD",nargs);
            break;
          case FORM_ROUND:
            nargs = 2;
            STRING_FIXED_ARGS("@ROUND",nargs);
            break;
          case FORM_UPPER:
            nargs = 1;
            STRING_FIXED_ARGS("@UPPER",nargs);
            break;
          case FORM_LOWER:
            nargs = 1;
            STRING_FIXED_ARGS("@LOWER",nargs);
            break;
          case FORM_PROPER:
            nargs = 1;
            STRING_FIXED_ARGS("@PROPER",nargs);
            break;
          case FORM_VLOOKUP:
            nargs = 3;
            STRING_FIXED_ARGS("@VLOOKUP",nargs);
            break;
          case FORM_HLOOKUP:
            nargs = 3;
            STRING_FIXED_ARGS("@HLOOKUP",nargs);
            break;
          case FORM_AND:
            nargs = 2;
            STRING_FIXED_ARGS("@AND",nargs);
            break;
          case FORM_OR:
            nargs = 2;
            STRING_FIXED_ARGS("@OR",nargs);
            break;

            /* Operators with variable numbers of arguments */
          case FORM_IF:
            STRING_VAR_NUM_ARGS("@IF"); /* This is really a complex macro */
            break;
          case FORM_SUM:
            STRING_VAR_NUM_ARGS("@SUM"); /* This is really a complex macro */
            break;
          case FORM_SUMIF:
            STRING_VAR_NUM_ARGS("@SUMIF"); /* This is really a complex macro */
            break;
          case FORM_AVG:
            STRING_VAR_NUM_ARGS("@AVERAGE");/*This is really a complex macro */
            break;
          case FORM_COUNT:
            STRING_VAR_NUM_ARGS("@COUNT"); /* This is really a complex macro */
            break;
          case FORM_MIN:
            STRING_VAR_NUM_ARGS("@MIN"); /* This is really a complex macro */
            break;
          case FORM_MAX:
            STRING_VAR_NUM_ARGS("@MAX"); /* This is really a complex macro */
            break;



          default:
            top->st_val = "UNKNOWN";
            return(top);
            internal_error();
            
        }
    }
}

char *Sheet::formula_string(Cell *cell) {
    Stack_elem *result;
    static string st;

    switch(cell->type) {
      case CODE_FORMULA:
        result = formula(cell);
        return(result->st_val);
      case CODE_NUMBER:
        sprintf(st,"%g",cell->value);
        return(st);
      case CODE_LABEL:
        return(cell->label);
    }
}


/* $Log: sheet_calc.cc,v $
 * Revision 1.7  1998/08/06 21:08:50  aml
 * Released alpha version of Abacus.
 *
// Revision 1.6  1997/03/27  10:00:47  aml
// Started implementing graphs.
// Fixed bug in tkCanvasPs.c
// Created bindings for composite characters in Portuguese.
//
 * Revision 1.5  1997/02/10 15:11:09  aml
 * Print settings now are saved to file.
 * Fixed buggy error message when loading sheets.
 *
 * Revision 1.4  1997/01/07 01:07:50  aml
 * Error propagation for formulas fixed.
 * Edit operations in place.
 *
 * Revision 1.3  1997/01/02 16:15:38  aml
 * Fixed unsufficient range of colunm width.
 * First cut of vlookup and hlookup functions.
 * Fixed bug in display routines.
 *
 * Revision 1.2  1996/12/11 21:40:09  aml
 * Sumif implemented.
 * Diverse time functions implemented.
 * Fixed needtoscroll2 to avoid out of control scroll.
 *
 * Revision 1.1  1996/10/06 19:21:53  aml
 * Initial revision
 * */
