
// binary tree routines. refer to the wikipedia article for binary trees.

int is_array_reference_name(int x) {
  int found;
  int condition_met;
  condition_met = 0;
  // following sets global annotation, returns size
  found = is_var(output_queue[x]);
  if (found != 0)
    if (global_annotation == ANNOTATION_LOCAL_VAR)
      if (local_variable_is_reference(output_queue[x]))
        condition_met = 1;
  if (condition_met)
    return 1;
  else
    // test for reference to global var array reference
    return output_queue[x + 2] > 1;
}

void display_leaf_for_scheme(int leaf) {
  if (is_hex(leaf))
    write_int(htoi(leaf));
  else
  if (leaf == assignment_tok)
    printf("set!");
  else
  if (leaf == div_tok)
    printf("quotient");
  else
  if (leaf == mod_tok)
    printf("remainder");
  else
  if (leaf == bit_and_tok)
    printf("bitwise-and");
  else
  if (leaf == bit_or_tok)
    printf("bitwise-or");
  else
  if (leaf == bit_xor_tok)
    printf("bitwise-xor");
  else
  if (leaf == bit_not_tok)
    printf("bitwise-not");
  else
  if (leaf == shr_tok)
    printf("shr");
  else
  if (leaf == shl_tok)
    printf("shl");
  else
  if (leaf == unary_minus_tok)
    printf("-");
  else
  if (leaf == equal_tok)
    printf("=");
  else
  //if (leaf == not_equals_tok)
  //  printf("<>");
  //else
  //if (leaf == not_tok)
  //  printf("not");
  //else
  //if (leaf == or_tok)
  //  printf("or");
  //else
  if (leaf == less_than_tok || leaf == greater_than_tok ||
      leaf == less_or_equal_tok || leaf == greater_or_equal_tok) {
    string_write_literal(leaf);
  }
  else
    string_write_literal(leaf);

  return;
}

int is_scheme_vector_set(int lst) {
  if (list_length(lst) == 3)
    if (is_pair(my_car(my_cdr(lst))) && !is_pair(my_car(lst)))
      return output_queue[my_car(lst)] == assignment_tok;
    else
      return 0;
  else
    return 0;
}

void display_binary_tree(int x) {
  int lst;
  int y;
  int is_first_in_list;
  write_char('(');
  lst = x;
  is_first_in_list = 1;
  while (!is_null(lst)) {
    y = my_car(lst);
    if (is_null(y))
      printf("()");
    else if (!is_pair(y)) {
      if (output_queue[y] == assignment_tok)
        display_leaf_for_scheme(output_queue[y]);
      else {
        if (is_first_in_list && is_array_reference_name(y))
          printf("vector-ref ");
        display_leaf_for_scheme(output_queue[y]);
      }
    }
    else
      display_binary_tree(y);
    if (!is_null(my_cdr(lst)))
      write_char(' ');
    is_first_in_list = 0;
    lst = my_cdr(lst);
  }
  write_char(')');
  return;
}

void display_scheme_vector_set(int lst) {
  printf("(vector-set! ");
  display_leaf_for_scheme(output_queue[my_car(my_car(lst))]);
  // vector name
  printf(" ");

  if (is_pair(my_car(my_cdr(my_car(lst)))))
    // vector index
    display_binary_tree(my_car(my_cdr(my_car(lst))));
  else
    display_leaf_for_scheme(output_queue[my_car(my_cdr(my_car(lst)))]);
  printf(" ");

  if (is_pair(my_car(my_cdr(lst))))
    // vector value
    display_binary_tree(my_car(my_cdr(lst)));
  else
    display_leaf_for_scheme(output_queue[my_car(my_cdr(lst))]);

  printf(")");

  return;
}

int pop_n_args(int n) {
  int pop_value;
  if (n == 0)
    return 0;
  else {
    // use a local var to force evaluation of the pop forcing left to
    // right evaluation. some compilers produce right to left evaluation
    pop_value = pop();
    return my_cons(pop_value, pop_n_args(n - 1));
  }
}

int filt_push(int x) {
  // write_comment();
  // printf("* filt push ");
  // if (is_pair(x))
  //   display_binary_tree(x);
  // else
  // if (is_null(x))
  //   printf("()");
  // else
  //   string_write(output_queue[x]);
  // writeln();

  return x;
}

int filt_pop(int x) {
  // write_comment();
  // printf("* filt pop ");
  // if (is_pair(x))
  //   display_binary_tree(x);
  // else
  // if (is_null(x))
  //   printf("()");
  // else
  //   string_write(output_queue[x]);
  // writeln();

  return x;
}

//int self(int x) {
//  // as an alternative to reverse
//  return x;
//}

int addition_by_zero(int operator, int left_operand, int right_operand) {
  if (operator != plus_tok)
    return 0;
  else if (!is_pair(left_operand))
    return is_zero(output_queue[left_operand]);
  else if (!is_pair(right_operand))
    return is_zero(output_queue[right_operand]);
  else
    return 0;
}

int is_immediate_constant(int operand) {
  int n;
  if (!is_pair(operand)) {
    n = output_queue[operand];
    if (is_number(n)) {
      if (is_hex(n)) {
        n = htoi(n);
        return (n >= 0) && (n <= 255);
      }
      else {
        // decimal
        n = _atoi(n);
        return (n >= 0) && (n <= 255);
      }
    }
    else
      return 0;
  }
  else
    return 0;
}

int make_binary_tree(int queue_idx) {
  int token;
  int annotation_1;
  int annotation_2;
  int annotation_3;
  int left_operand;
  int right_operand;
  int operand;
  if (queue_idx == output_queue_idx)
    // end of queue voila a binary expression tree
    return filt_pop(pop());
  else {
    token = output_queue[queue_idx];
    annotation_1 = output_queue[queue_idx + 1];
    annotation_2 = output_queue[queue_idx + 2];
    annotation_3 = output_queue[queue_idx + 3];
    if (is_operator(token)) {
      if (is_binary_operator(token)) {
        left_operand = filt_pop(pop());
        right_operand = filt_pop(pop());
        if (addition_by_zero(token, left_operand, right_operand))
          // optimize away the addition
          if (is_zero(output_queue[left_operand]))
            push(filt_push(right_operand));
          else
            push(filt_push(left_operand));
        else {
          if ((token != assignment_tok)
              && (token != times_tok)
              && is_immediate_constant(left_operand)) {
            // set immediate constant annotation
            // binary operator constant numerical value
            output_queue[queue_idx + 3] =
              output_queue[left_operand];
            // constant subexpression constant numerical value
            output_queue[left_operand + 3] =
              output_queue[left_operand];
          }
          push(filt_push
               (my_cons(queue_idx,
                  my_cons(right_operand,
                    my_cons(left_operand, 0)))));
        }
      }
      else {
        operand = filt_pop(pop());
        push(filt_push(my_cons(queue_idx, my_cons(operand, 0))));
      }
    }
    else if ((annotation_1 == ANNOTATION_FUNCTION) ||
             (annotation_1 == ANNOTATION_PROCEDURE)) {
      push(my_cons(queue_idx, my_reverse(pop_n_args(n_args(token)))));
    }
    else
      if (((annotation_2 > 1) && (annotation_3 == 0))
          || ((annotation_1 == 3) && local_variable_is_reference(token)))
      // array vars have just one subscript
      push(filt_push(my_cons(queue_idx, my_cons(filt_pop(pop()), 0))));
    else
      // number or simple type variable
      push(filt_push(queue_idx));
    return make_binary_tree(queue_idx + 4);
  }
}

void traverse_binary_tree(int binary_tree) {
  // flattens binary tree to the stack
  int left;
  int right;
  if (is_null(binary_tree))
    return;
  else
  if (!is_pair(binary_tree)) {
    push(binary_tree);
    return;
  }
  else {
    // a pair
    left = my_car(binary_tree);
    right = my_cdr(binary_tree);
    traverse_binary_tree(left);
    traverse_binary_tree(right);
    return;
  }
}

//if (!is_null(binary_tree)) {
//  left = my_car(binary_tree);
//  right = my_cdr(binary_tree);
//  if (!is_pair(left)) {
//    push(left);
//    traverse_binary_tree(right);
//  }
//  else {
//    traverse_binary_tree(left);
//    traverse_binary_tree(right);
//  }
//}
//return;
//}

int is_assignment_expression(int x) {
  if (!is_null(x))
    if (is_pair(x))
      if (!is_pair(my_car(x)))
        if (stringeq(assignment_tok, output_queue[my_car(x)]) == 0)
          return 1;
        else
          return 0;
      else
        return 0;
    else
      return 0;
  else
    return 0;
}

void fixup_binary_tree_if_assignment(int x) {
  int y;
  if (is_assignment_expression(x)) {
    y = my_car(my_cdr(x));
    while (is_pair(y))
      y = my_car(y);

    // fixup

    if (output_queue[y + 1] == ANNOTATION_GLOBAL_VAR)
      output_queue[y + 1] = ANNOTATION_GLOBAL_VAR_ASSIGNMENT;
    else if (output_queue[y + 1] == ANNOTATION_LOCAL_VAR)
      output_queue[y + 1] = ANNOTATION_LOCAL_VAR_ASSIGNMENT;
    // else don't fixup return assignment
  }
  return;
}

int get_fast_expression(int binary_tree) {
  int left;
  int right;
  if (is_null(binary_tree))
    return 1;
  else
  if (!is_pair(binary_tree)) {
    if (output_queue[binary_tree + 1] == ANNOTATION_OPERATOR) {
      if ((output_queue[binary_tree] == div_tok)
          || (output_queue[binary_tree] == mod_tok)
          || (output_queue[binary_tree] == abs_tok)
          || (output_queue[binary_tree] == times_tok))
        // div and mod are not fast
        return 0;
      else
        return 1;
    }
    else if (output_queue[binary_tree + 1] == ANNOTATION_FUNCTION)
      return 0;
    else if (output_queue[binary_tree + 1] == ANNOTATION_PROCEDURE)
      return 0;
    else
      return 1;
  }
  else {
    left = my_car(binary_tree);
    if (!get_fast_expression(left))
      return 0;
    else {
      right = my_cdr(binary_tree);
      return get_fast_expression(right);
    }
  }
}

void set_fast_relop(int x) {
  if (!is_null(x))
    if (is_pair(x))
      if (!is_pair(my_car(x)))
        if (is_relop(output_queue[my_car(x)]))
          // fast relop
          output_queue[my_car(x) + 2] = 1;
  return;
}

int is_local_var_assignment(int x) {
  if (!is_pair(x))
    if (output_queue[x + 1] == ANNOTATION_LOCAL_VAR_ASSIGNMENT)
      return 1;
    else
      return 0;
  else
    return 0;
}

int is_simple_expression_operator(int x) {
  int operator_token;
  if (!is_pair(x))
    if (output_queue[x + 1] == ANNOTATION_OPERATOR) {
      operator_token = output_queue[x];
      if (operator_token == plus_tok)
        return 1;
      else
      if (operator_token == minus_tok)
        return 1;
      else
      if (operator_token == bit_and_tok)
        return 1;
      else
      if (operator_token == bit_or_tok)
        return 1;
      else
      if (operator_token == bit_xor_tok)
        return 1;
    }
  return 0;
}

int is_local_var_reference(int x) {
  if (!is_pair(x))
    if (output_queue[x + 1] == ANNOTATION_LOCAL_VAR)
      return 1;
    else
      return 0;
  else
    return 0;
}

void determine_is_simple_assignment_expression(int x) {
  // see if expression is of form: (= local_var (op local_var local_var/num))
  if (is_assignment_expression(x))
    if (is_local_var_assignment(my_car(my_cdr(x))))
      // local variable assignment
      if (list_length(x) == 3) {
        x = my_car(my_cdr(my_cdr(x)));
	if (is_pair(x))
          if (list_length(x) == 3)
            if (is_simple_expression_operator(my_car(x)) &&
                is_local_var_reference(my_car(my_cdr(x))) &&
                (is_local_var_reference(my_car(my_cdr(my_cdr(x)))) ||
                 numberp(my_car(my_cdr(my_cdr(x))))))
	      is_simple_assignment_expression = 1;
      }
  return;
}

void determine_is_simple_assignment_inc_dec_expression(int x) {
  // see if expression is of form: (= x (+/- x 1))
  int var1;
  int var2;
  int op;
  int n;
  var1 = my_car(my_cdr(x));
  x = my_car(my_cdr(my_cdr(x)));
  op = output_queue[my_car(x)];
  n = my_car(my_cdr(my_cdr(x)));
  if (!(op == plus_tok || op == minus_tok))
    return;
  else {
    var2 = my_car(my_cdr(x));
    if (!eqp(var1, var2))
      return;
    else
    if (!numberp(n))
      return;
    else
    if (_atoi(output_queue[n]) != 1)
      return;
    else {
      is_simple_assignment_inc_dec_expression = 1;
      return;
    }
  }
}
