Logo Search packages:      
Sourcecode: calcoo version File versions

c_op.c

/*
 * Calcoo: c_op.c
 *
 * Copyright (C) 2001, 2002, 2003 Alexei Kaminski
 *
 * handles keypressings for arithmetic and algebraic operations
 * and also "=" key, push key, paren keys, and register swap (exchange) keys
 */

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

#include "codes.h"
#include "const.h"
#include "cpu.h"
#include "c_headers.h"
#include "io_headers.h"
#include "aux_headers.h"

void perform_binary_operation(void);
void perform_binop_chain(int, int);
int  no_open_parens(void);

void call_binary_op(int received_code)
{
      if (cpu->last_action == ACTION_INPUT) 
            input_to_x();

      if (cpu->rpn_mode) {
            cpu->op = received_code;
            perform_binary_operation();
            pop_stack();
            cpu->last_action = ACTION_ENTER;
            /* no need to use ACTION_BINOP in RPN */
      } else { 
            if (cpu->op == CODE_NO_OPERATION) { 
                  cpu->y = cpu->x;
                  cpu->last_action = ACTION_BINOP;
                  cpu->op = received_code;
                  cpu->number_of_parens = 0;
            } else {
                  if(priority(received_code) > priority (cpu->op) ||
                     cpu->number_of_parens > 0 ) {
                        push_stack();
                        cpu->y = cpu->x;
                        cpu->op = received_code;
                        cpu->number_of_parens = 0;
                        cpu->last_action = ACTION_BINOP;
                  } else {
                        perform_binop_chain(priority(received_code), 
                                        FALSE);
                        push_stack();
                        cpu->y = cpu->x;
                        cpu->op = received_code;
                        cpu->number_of_parens = 0;
                        cpu->last_action = ACTION_BINOP;
                  }
            }
      }
      
      aftermath();
}

void perform_binary_operation(void)
/* aux function; makes only one binop itself, no other operations;
 * is called by other functions like call_binary_op(), call_eq() */
{
      switch (cpu->op) {
      case CODE_ADD  :  
                  cpu->x = smart_sum(cpu->y, cpu->x, cpu->precision);
            break;
      case CODE_SUB  :  
                  cpu->x = smart_sum(cpu->y, -cpu->x, cpu->precision);
            break;
      case CODE_MUL  :  
            cpu->x = cpu->y * cpu->x;
            break;
      case CODE_DIV  :  
            if (cpu->x != 0.0)
                  cpu->x = cpu->y / cpu->x;
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_POW  :  
            if ( !(
                  (cpu->y == 0 && cpu->x <= 0) || 
                  (cpu->y < 0 && (cpu->x != floor(cpu->x))) 
                  ) )
                  cpu->x = pow(cpu->y, cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_NO_OPERATION  :  
            break;
      }

      cpu->op = CODE_NO_OPERATION;
}

void perform_binop_chain(int init_priority, int paren_closed)
/* aux function; performs the chain of binary operations from the stack,
 * stopping the chain at a paren or at a lower-priority operation */
{
      if (paren_closed) {
            /* if paren_closed == TRUE, it means there were open parens
             * it have been found in call_right_paren() */
            if (cpu->number_of_parens > 0){
            /* handling a weird case of one number in parens, like 2+(3) */
                        cpu->number_of_parens--;
                  return;
            } else {
                  while ( cpu->number_of_parens == 0 ) {
                        perform_binary_operation();
                        pop_stack();
                  }
                  cpu->number_of_parens--;
            }
      } else {
            while ( (cpu->op != CODE_NO_OPERATION)
                  && 
                  (
                        ( (priority(cpu->op) >= init_priority) 
                          && (cpu->number_of_parens == 0) )
                        ||
                        /* "=" effectively closes all parens */
                        init_priority == PRIORITY_CODE_MIN
                  ) 
                  ) {
                  perform_binary_operation();
                  pop_stack();
            }
      }
}

void call_exch_xy(void)
{
      double tmp;

      if ( (!cpu->rpn_mode) && (cpu->op == CODE_NO_OPERATION) ) 
            return; 

      if (cpu->last_action == ACTION_INPUT)
            input_to_x();

      tmp = cpu->x;
      cpu->x = cpu->y;
      cpu->y = tmp;

      cpu->last_action = ACTION_ENTER;

      aftermath();
}

void call_stack_up(void)
{
      double tmp1, tmp2;
      t_stack_element *stack_element;

      /* this operation is not meant for the algebraic mode */
      if ( !(cpu->rpn_mode)) {
            error_occured("call_stack_up() called in algebraic mode", 
                        FALSE);
            return;
      }

      if (cpu->last_action == ACTION_INPUT)
            input_to_x();

      if (cpu->stack_mode == STACK_MODE_INFINITE)
      {
            if (cpu->stack_head != NULL)
            {

                  tmp1 = cpu->stack_head->z;
                  cpu->stack_head->z = cpu->t;
                  cpu->t = cpu->z;
                  cpu->z = cpu->y;
                  cpu->y = cpu->x;

                  stack_element = cpu->stack_head;

                  while ( stack_element->next != NULL )
                  {
                        tmp2 = tmp1;
                        tmp1 = stack_element->next->z;
                        stack_element->next->z = tmp2;
                        stack_element = stack_element->next;
                  }
                  cpu->x = tmp1;
            } else {
                  tmp1 = cpu->x; /* for consistency */
                  if (cpu->y == 0.0 && cpu->z == 0.0 && cpu->t == 0.0)
                        goto done_with_stack_scrolling;
                  tmp1 = cpu->y;
                  cpu->y = cpu->x;
                  if (cpu->z == 0.0 && cpu->t == 0.0)
                        goto done_with_stack_scrolling;
                  tmp2 = tmp1;
                  tmp1 = cpu->z;
                  cpu->z = tmp2;
                  if (cpu->t == 0.0)
                        goto done_with_stack_scrolling;
                  tmp2 = tmp1;
                  tmp1 = cpu->t;
                  cpu->t = tmp2;
            done_with_stack_scrolling: 
                  cpu->x = tmp1;
            }
      } else {
            tmp1 = cpu->x;
            cpu->x = cpu->t;
            cpu->t = cpu->z;
            cpu->z = cpu->y;
            cpu->y = tmp1;
      }

      cpu->last_action = ACTION_ENTER;

      aftermath();
}

void call_stack_down(void)
{
      double tmp;
      t_stack_element *stack_element;

      /* this operation is not meant for the algebraic mode */
      if ( !(cpu->rpn_mode)) 
      {
            error_occured("call_stack_down() called in algebraic mode", 
                        FALSE);
            return;
      }

      if (cpu->last_action == ACTION_INPUT)
            input_to_x();

      tmp = cpu->x;

      if (cpu->stack_mode == STACK_MODE_INFINITE)
      {
            if (cpu->stack_head != NULL)
            {
                  cpu->x = cpu->y;
                  cpu->y = cpu->z;
                  cpu->z = cpu->t;
                  cpu->t = cpu->stack_head->z;

                  stack_element = cpu->stack_head;
                  while ( stack_element->next != NULL )
                  {
                        stack_element->z = stack_element->next->z;
                        stack_element = stack_element->next;
                  }
                  stack_element->z = tmp;
            } else {
                  if (cpu->y == 0.0 && cpu->z == 0.0 && cpu->t == 0.0)
                        goto done_with_stack_scrolling;
                  cpu->x = cpu->y;
                  if (cpu->z == 0.0 && cpu->t == 0.0)
                  {
                        cpu->y = tmp;
                        goto done_with_stack_scrolling;
                  }
                  cpu->y = cpu->z;
                  if (cpu->t == 0.0)
                  {
                        cpu->z = tmp;
                        goto done_with_stack_scrolling;
                  }
                  cpu->z = cpu->t;
                  cpu->t = tmp;
            done_with_stack_scrolling: 
                  ;
            }
      } else {
            cpu->x = cpu->y;
            cpu->y = cpu->z;
            cpu->z = cpu->t;
            cpu->t = tmp;
      }

      cpu->last_action = ACTION_ENTER;

      aftermath();
}


void call_eq(void)
{
      if (cpu->rpn_mode) 
      {
            error_occured("call_eq() called in RPN mode", FALSE);
            return;
      }

      if (cpu->last_action == ACTION_INPUT)
            input_to_x();

      perform_binop_chain(PRIORITY_CODE_MIN, FALSE);
      cpu->op = CODE_NO_OPERATION; 
      cpu->number_of_parens = 0;

      cpu->last_action = ACTION_ENTER;

      aftermath();
}

void call_enter(void)
{
      /* this operation is not meant for the non-RPN mode */
      if (!(cpu->rpn_mode)) 
      {
            error_occured("call_enter() called in algebraic mode", FALSE);
            return;
      }

      if (cpu->enter_mode == ENTER_MODE_TRADITIONAL) 
      {
            if (cpu->last_action == ACTION_INPUT)
                  input_to_x();
            push_stack();
            cpu->op = CODE_ENTER; 
            cpu->number_of_parens = 0;
            cpu->y = cpu->x;
            cpu->last_action = ACTION_ENTER_PUSH;
      } else {
            if (cpu->last_action == ACTION_INPUT)
                  input_to_x();
            else {
                  push_stack();
                  cpu->op = CODE_ENTER; 
                  cpu->number_of_parens = 0;
                  cpu->y = cpu->x;
            }
            cpu->last_action = ACTION_ENTER;
      }

      aftermath();
}

void call_left_paren(void)
{
      if (cpu->rpn_mode) 
      {
            error_occured("call_left_paren() called in RPN mode", FALSE);
            return;
      }

      if (cpu->last_action == ACTION_BINOP) 
      {
            cpu->number_of_parens++;
            aftermath();
      }
}

void call_right_paren(void)
{
      if (cpu->rpn_mode) 
      {
            error_occured("call_right_paren() called in RPN mode", FALSE);
            return;
      }

      if (cpu->last_action == ACTION_INPUT) 
      {
            input_to_x();
      }

      if (!no_open_parens()) 
      {
            if (cpu->op != CODE_NO_OPERATION) {
                  perform_binop_chain(PRIORITY_CODE_MIN, TRUE);
            }
      } else {
            /* acts like "=" to provide reasonable behavior in
             * the expressions like (2+3)/(4+5) */
            perform_binop_chain(PRIORITY_CODE_MIN, FALSE);
            cpu->op = CODE_NO_OPERATION; 
      }

      cpu->last_action = ACTION_ENTER;

      aftermath();
}

void push_stack(void)
/* attention! this function does not move the contents of x to y!
 * It affects only the higher elements of the stack. To make a full 
 * stack push you need the following sequence:
 *    push_stack();
 *    cpu->op = CODE_foo; 
 *      cpu->number_of_parens = 0;
 *    cpu->y = cpu->x;
 * The reason is that cpu->op is set manually rather than pushed from somewhr*/
{
      t_stack_element *new_element;

      if (cpu->rpn_mode) 
      {
            if ( (cpu->stack_mode == STACK_MODE_INFINITE)
                 && (cpu->stack_head != NULL || cpu->t != 0.0 ))
            {
                        new_element = (t_stack_element*) 
                              malloc (sizeof(t_stack_element));
                        if(new_element == NULL)
                              error_occured("malloc failed", TRUE);
                        new_element->next = cpu->stack_head;
                        new_element->z = cpu->t;
                        cpu->stack_head = new_element;
            }
            cpu->t = cpu->z;
            cpu->z = cpu->y;
      } else {
            if (cpu->op == CODE_NO_OPERATION)
                  return;
            new_element = (t_stack_element*) 
                          malloc (sizeof(t_stack_element));
            if(new_element == NULL)
                  error_occured("malloc failed", TRUE);
            new_element->next = cpu->stack_head;
            new_element->z = cpu->y;
            new_element->op = cpu->op;
            new_element->number_of_parens = cpu->number_of_parens;
            cpu->stack_head = new_element;
      }
}

void pop_stack(void)
{
      t_stack_element *to_remove;
      
      if (cpu->rpn_mode) 
      {
            cpu->y = cpu->z;
            cpu->z = cpu->t;
            if (cpu->stack_mode == STACK_MODE_INFINITE ) 
            {
                  if (cpu->stack_head != NULL) 
                  {
                        to_remove = cpu->stack_head;
                        cpu->t = (cpu->stack_head)->z;
                        cpu->stack_head = cpu->stack_head->next;
                        free(to_remove);
                  } else {
                        cpu->t = 0.0;
                  }
            }
      } else {
            if (cpu->stack_head != NULL) 
            {
                  to_remove = cpu->stack_head;
                  cpu->y = (cpu->stack_head)->z;
                  cpu->op = (cpu->stack_head)->op;
                  cpu->number_of_parens = 
                        (cpu->stack_head)->number_of_parens;
                  cpu->stack_head = cpu->stack_head->next;
                  free(to_remove);
            } else {
                  cpu->op = CODE_NO_OPERATION;
                  cpu->y = 0.0;
                  cpu->number_of_parens = 0;
            }
      }
}

int no_open_parens(void)
/* verifies if there are any open parens, returns TRUE if there are none */
{
      t_stack_element *current;

      if(cpu->number_of_parens > 0)
            return FALSE;

      current = cpu->stack_head;

      while ( current != NULL ) 
      {
            if (current->number_of_parens > 0)
                  return FALSE;
            current = current->next;
      }
      return TRUE;
}

void call_unary_op(int received_code)
{
      if (cpu->last_action == ACTION_INPUT) 
            input_to_x();

      switch (received_code){
      case CODE_LOG  :  
            if (cpu->x > 0.0){
                  if (fabs(cpu->x - 1.0 ) > cpu->precision)
                        cpu->x = log10(cpu->x);
                  else
                        cpu->x = 0.0;
            }
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_10TOX :  
            if (cpu->x < pow(10,EXP_INPUT_LENGTH) )
                  cpu->x = pow(10, cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_LN  :  
            if (cpu->x > 0){
                  if (fabs(cpu->x - 1.0 ) > cpu->precision)
                        cpu->x = log(cpu->x);
                  else
                        cpu->x = 0.0;
            }
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_ETOX  :  
            if (cpu->x * log10(exp(1.0)) < pow(10,EXP_INPUT_LENGTH))
                  cpu->x = exp(cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_SQRT  :  
            if (cpu->x >= 0.0)
                  cpu->x = sqrt(cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_SQR  :  
            cpu->x = cpu->x * cpu->x;
            break;
      case CODE_INVX  :  
            if (cpu->x != 0.0)
                  cpu->x = 1.0 / cpu->x;
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_FACT  :  
            if (cpu->x == 0.0)
                  cpu->x = 1.0;
            else if (!fact_too_large(cpu->x))
                  cpu->x = fact_function(cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_SIN:
            if (cpu->angle_units == CODE_DEG)
                  cpu->x *= PI / 180.0;
            if (almost_integer(cpu->x / PI, cpu->precision))
                  /* hack to have sin 180 == 0 */ 
                  cpu->x = 0.0;
            else              
                  cpu->x = sin (cpu->x);
            break;
      case CODE_ASIN:
            if (fabs(cpu->x) <= 1.0) {
                  cpu->x = asin(cpu->x);
                  if (cpu->angle_units == CODE_DEG)
                        cpu->x *= 180.0 / PI;
            }
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_SINH :
            cpu->x = sinh (cpu->x);
            break;
      case CODE_ASINH :
            cpu->x = asinh (cpu->x);
            break;
      case CODE_COS :
            if (cpu->angle_units == CODE_DEG)
                  cpu->x *= PI / 180.0;
            if (almost_integer((cpu->x - PI / 2.0) / PI, 
                           cpu->precision)
                || fabs(cpu->x - PI / 2.0) < cpu->precision)
                  /* case x == PI/2 requires special treatment */
                  /* hack to have cos 90 == 0 */ 
                  cpu->x = 0.0;
            else              
                  cpu->x = cos (cpu->x);
            break;
      case CODE_ACOS:
            if (fabs(cpu->x) <= 1.0) {
                  cpu->x = acos(cpu->x);
                  if (cpu->angle_units == CODE_DEG)
                        cpu->x *= 180.0 / PI;
            }
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_COSH :
            cpu->x = cosh (cpu->x);
            break;
      case CODE_ACOSH :
            if (cpu->x >= 1.0)
                  cpu->x = acosh(cpu->x);
            else
                  cpu->x_overflow = TRUE;
            break;
      case CODE_TAN:
            if (cpu->angle_units == CODE_DEG)
                  cpu->x *= PI / 180.0;
            if (almost_integer((cpu->x - PI / 2.0) / PI, 
                           cpu->precision)
                || fabs(cpu->x - PI / 2.0) < cpu->precision)
                  /* case x == PI/2 requires special treatment */
                  cpu->x_overflow = TRUE;
            else if (almost_integer(cpu->x / PI, cpu->precision))
                  cpu->x = 0.0;
            else              
                  cpu->x = tan (cpu->x);
            break;
      case CODE_ATAN:
            cpu->x = atan(cpu->x);
            if (cpu->angle_units == CODE_DEG)
                  cpu->x *= 180.0 / PI;
            break;
      case CODE_TANH :
            cpu->x = tanh (cpu->x);
            break;
      case CODE_ATANH :
            cpu->x = atanh (cpu->x);
            break;
      default:
            error_occured("call_unary_op() unknown unary_op code", FALSE);
      }

      cpu->last_action = ACTION_ENTER;
      
      aftermath();
}


Generated by  Doxygen 1.6.0   Back to index