/**
   * @param arrexp
   * @param rhs
   * @return
   */
  private ProverExpr arrayWrite2ProverExpr(Expression arrexp, ProverExpr rhs) {
    if (arrexp instanceof SimpleHeapAccess || arrexp instanceof ArrayReadExpression) {
      /*
       * This is needed here only for one purpose: a[x] = 5 in Boogie is
       * translated to a__i[x__j] = 5 in SSA-Boogie but in the VC we have
       * to write (= a__i (store a__(i-1) x__j 5), so we need to be able
       * to access the previous incarnation of array typed variables
       * Probably, we should settle this already during SSA and introduce
       * uninterpreted procedures for store and select
       */
      Expression base;
      LinkedList<Expression> args = new LinkedList<Expression>();
      if (arrexp instanceof SimpleHeapAccess) {
        SimpleHeapAccess exp = (SimpleHeapAccess) arrexp;
        base = exp.getHeapVariable();
        args.add(exp.getBaseExpression());
        args.add(exp.getFieldExpression());
      } else {
        ArrayReadExpression exp = (ArrayReadExpression) arrexp;
        base = exp.getBaseExpression();
        args.add(exp.getIndexExpression());
      }
      ProverExpr newbase = expression2ProverExpr(base);
      ProverExpr lhs =
          arrayExpression2ProverExpr(base, args, rhs, new HashMap<Variable, ProverExpr>());

      return theoremProver.mkEq(newbase, lhs);
    }
    Log.error("CRAAAAAAAAAASH");
    return null;
  }
  /**
   * generates a prover expression from an expression e
   *
   * @param e
   * @param boundvars
   * @return
   */
  private ProverExpr expression2ProverExpr(Expression e, HashMap<Variable, ProverExpr> boundvars) {
    try {
      if (e instanceof ArrayReadExpression) {
        ArrayReadExpression exp = (ArrayReadExpression) e;

        return arrayExpression2ProverExpr(
            exp.getBaseExpression(),
            new LinkedList<Expression>(Arrays.asList(new Expression[] {exp.getIndexExpression()})),
            null,
            boundvars);
      } else if (e instanceof InvokeExpression) {
        InvokeExpression exp = (InvokeExpression) e;
        ProverFun fun = procedure2ProverExpr(exp.getInvokedProcedure());
        LinkedList<ProverExpr> args = new LinkedList<ProverExpr>();
        for (Expression e2 : exp.getArguments()) {
          args.add(expression2ProverExpr(e2, boundvars));
        }
        return fun.mkExpr((ProverExpr[]) args.toArray(new ProverExpr[args.size()]));
      } else if (e instanceof SimpleHeapAccess) {
        SimpleHeapAccess exp = (SimpleHeapAccess) e;
        return arrayExpression2ProverExpr(
            exp.getHeapVariable(),
            new LinkedList<Expression>(
                Arrays.asList(
                    new Expression[] {exp.getBaseExpression(), exp.getFieldExpression()})),
            null,
            boundvars);
      } else if (e instanceof TypeExpression) {
        TypeExpression exp = (TypeExpression) e;
        return expression2ProverExpr(exp.getTypeVariable(), boundvars);
      } else if (e instanceof Variable) {
        Variable exp = (Variable) e;
        if (boundvars.containsKey(exp)) {
          return boundvars.get(exp);
        }
        if (!usedVariable.containsKey(exp)) {
          ProverExpr pe =
              theoremProver.mkVariable(
                  getProverFriendlyName(exp.getName()), type2ProverSort(exp.getType()));
          usedVariable.put(exp, pe);
        }
        return usedVariable.get(exp);
      } else if (e instanceof UboundedIntConstant) {
        UboundedIntConstant exp = (UboundedIntConstant) e;
        return theoremProver.mkLiteral(BigInteger.valueOf(exp.getValue()));
      } else if (e instanceof Constant) {
        Log.error("DEBUG-ERROR, there should not be any constant but Integers! VC will be wrong!");
        return expression2ProverExpr(BoogieProgram.v().getNullReference(), boundvars);
      } else if (e instanceof QuantifiedExpression) {
        // TODO: right now, Quantifiers can only be used in Axioms
        QuantifiedExpression qe = (QuantifiedExpression) e;
        HashMap<Variable, ProverExpr> bvars = new HashMap<Variable, ProverExpr>(boundvars);
        // LinkedList<ProverExpr> localBound = new
        // LinkedList<ProverExpr>();
        int idx = 0;
        for (Variable v : qe.getBoundVariables()) {
          ProverExpr boundvar = theoremProver.mkBoundVariable(idx++, type2ProverSort(v.getType()));
          bvars.put(v, boundvar);
          // localBound.add(boundvar);
        }
        ProverExpr boundExp = expression2ProverExpr(qe.getExpression(), bvars);
        if (qe.getQuantifier() == Quantifier.ForAll) {
          return theoremProver.mkAll(boundExp, theoremProver.getBooleanType());
        } else {
          return theoremProver.mkEx(boundExp, theoremProver.getBooleanType());
        }
      } else if (e instanceof BinOpExpression) {
        BinOpExpression be = (BinOpExpression) e;
        ProverExpr left = (expression2ProverExpr(be.getLhs(), boundvars));
        ProverExpr right = (expression2ProverExpr(be.getRhs(), boundvars));

        switch (be.getOp()) {
          case Eq:
            {
              return theoremProver.mkEq(left, right);
            }
          case Lt:
            {
              return theoremProver.mkLt(left, right);
            }
          case Le:
            {
              return theoremProver.mkLeq(left, right);
            }
          case Gt:
            {
              return theoremProver.mkGt(left, right);
            }
          case Ge:
            {
              return theoremProver.mkGeq(left, right);
            }
          case Neq:
            {
              return theoremProver.mkNot(theoremProver.mkEq(left, right));
            }
          case Implies:
            {
              return theoremProver.mkOr(theoremProver.mkNot(left), right);
            }
          case LAnd:
            {
              return theoremProver.mkAnd(left, right);
            }
          case LOr:
            {
              return theoremProver.mkOr(left, right);
            }
          case Plus:
            {
              return theoremProver.mkPlus(left, right);
            }
          case Minus:
            {
              return theoremProver.mkMinus(left, right);
            }
          case Mul:
            {
              return theoremProver.mkMult(left, right);
            }
          default:
            {
              throw new RuntimeException("Unmatched binop: " + be.getOp().toString());
              // TODO: a proper exception might be nice.
              // However, this case can only occur if we screw up the
              // implementation
            }
        }
      } else if (e instanceof IteExpression) {
        IteExpression ite = (IteExpression) e;
        ProverExpr condE = expression2ProverExpr(ite.getCond(), boundvars);
        ProverExpr thenE = expression2ProverExpr(ite.getThen(), boundvars);
        ProverExpr elseE = expression2ProverExpr(ite.getElse(), boundvars);
        return theoremProver.mkIte(condE, thenE, elseE);
      } else {
        Log.error("Unmatched Expression " + e.toString());
        return null;
      }
    } catch (Exception exc) {
      Log.error("expression2ProverExpr failed: " + exc.toString());
      Log.error("Called on expression: " + e.toString());
      if (e instanceof BinOpExpression) {
        BinOpExpression boe = (BinOpExpression) e;
        Log.error(boe.getLhs().getType().toString() + " and " + boe.getRhs().getType());
      }
      return null;
    }
  }