// TO DO: tree rotations to bring constants together
// make sure address arithmetic (eg struct offsets) is also folded
// arithmetic identities (* 1, + 0, / 1)
// check for / 0

// common factors:  a*b + a*c = a*(b+c)

// advanced: formula simplification

void Fold(int p, int *count)
{
  int op;
  int arity;

  if ((p == -1) || (astop(p) == -1)) return;

  op = astop(p); arity = children(p);

  if (arity == 1) {
    if ((leftchild(p) != -1) && (astop(leftchild(p)) == AST_Const)) {
      switch (op) {
        case AST_UNeg: replace_with_int_const(p, -rightchild(leftchild(p))); *count = *count + 1; return;
        case AST_UPos: replace_with_int_const(p, +rightchild(leftchild(p))); *count = *count + 1; return;
        case AST_UBitNot: replace_with_int_const(p, ~rightchild(leftchild(p))); *count = *count + 1; return;
        case AST_UBoolNot: replace_with_int_const(p, !rightchild(leftchild(p))); *count = *count + 1; return;
        default:
	  return;
      }
    }
  } else if (arity == 2) {
    if ((astop(leftchild(p)) == AST_Const) && (astop(rightchild(p)) == AST_Const)) {
    switch (op) {
    case AST_Add: replace_with_int_const(p, rightchild(leftchild(p)) + rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_Sub: replace_with_int_const(p, rightchild(leftchild(p)) - rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_Mul: replace_with_int_const(p, rightchild(leftchild(p)) * rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_Div: if (rightchild(rightchild(p)) != 0) replace_with_int_const(p, rightchild(leftchild(p)) / rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_Mod: if (rightchild(rightchild(p)) != 0) replace_with_int_const(p, rightchild(leftchild(p)) % rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_LE: replace_with_int_const(p, rightchild(leftchild(p)) <= rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_GT: replace_with_int_const(p, rightchild(leftchild(p)) > rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_LT: replace_with_int_const(p, rightchild(leftchild(p)) < rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_GE: replace_with_int_const(p, rightchild(leftchild(p)) >= rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_EQ: replace_with_int_const(p, rightchild(leftchild(p)) == rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_NE: replace_with_int_const(p, rightchild(leftchild(p)) != rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BoolAnd: replace_with_int_const(p, rightchild(leftchild(p)) && rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BoolOr: replace_with_int_const(p, rightchild(leftchild(p)) || rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_ShortcutBoolAnd: replace_with_int_const(p, rightchild(leftchild(p)) && rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_ShortcutBoolOr: replace_with_int_const(p, rightchild(leftchild(p)) || rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BitAnd: replace_with_int_const(p, rightchild(leftchild(p)) & rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BitOr: replace_with_int_const(p, rightchild(leftchild(p)) | rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BitXor: replace_with_int_const(p, rightchild(leftchild(p)) ^ rightchild(rightchild(p))); *count = *count + 1; return;
    case AST_BitLsh: replace_with_int_const(p, rightchild(leftchild(p)) << rightchild(rightchild(p))); *count = *count + 1; return; // shift by >= 32, or 0, or negative?
    case AST_BitRsh: replace_with_int_const(p, rightchild(leftchild(p)) >> rightchild(rightchild(p))); *count = *count + 1; return;

    default:
      return;
    }
    }
  } else if (op == AST_Cond) {
    if ((astop(leftchild(p)) == AST_Const)
     && (astop(rightchild(p)) == AST_Const)
     && (astop(nthchild(p, 2)) == AST_Const)
    ) {
      // case AST_Cond: ;
      replace_with_int_const(p, ( rightchild(leftchild(p)) ? rightchild(rightchild(p)) : rightchild(nthchild(p, 2)))); *count = *count + 1; return;
    }
  }
}

int fold_constant_expressions(int p)
{
  int count = 0;
  int i;
  int disp;
  int this;

  if ((p == -1) || (astop(p) == -1)) return 0;


  // recursively process all children, doing the folds on the way back up
  // once grandchildren constant expressions have already been folded:
  if ((astop(p) < AST_BASE) || (astop(p) > AST_TOP)) {
    fprintf(stderr, "* Internal Error: p=%d  astop(p) - %d\n", p, astop(p));
    exit(1);
  }
  disp = op[astop(p)-AST_BASE].Display_Children;
  this = 1<<(children(p)-1);
  for (i = 0; i < children(p); i++) {
    if ((nthchild(p, i) != -1) && (disp&this)) {
      count += fold_constant_expressions(nthchild(p, i));
      while (astop(nthchild(p, i)) == AST_REDIRECT) nthchild(p, i) = leftchild(nthchild(p,i));
      // perform redirection
    }
    this = this>>1;
  }

  Fold(p, &count);
  return count;
}
