private ProverFun procedure2ProverExpr(BoogieProcedure proc) {
    HashMap<Variable, ProverExpr> localbound = new HashMap<Variable, ProverExpr>();
    if (!usedFunctions.containsKey(proc)) {
      LinkedList<ProverType> args = new LinkedList<ProverType>();
      int idx = 0;
      for (Variable v : proc.getParameterList()) {
        ProverExpr arg = theoremProver.mkBoundVariable(idx++, type2ProverSort(v.getType()));
        args.add(arg.getType());
        localbound.put(v, arg);
      }

      ProverFun vcf = null;
      ProverType[] arr = args.toArray(new ProverType[args.size()]);
      if (proc.isPure()
          && proc.getRootBlock() != null
          && proc.getRootBlock().getStatements().size() == 1) {
        // TODO: this is a hack:
        // we assume that this case only occurs for prelude functions
        // which have only one statement
        // this will not work in any other case
        Statement stmt = proc.getRootBlock().getStatements().get(0);
        ProverExpr b = null;
        if (stmt instanceof ExpressionStatement) {
          ExpressionStatement es = (ExpressionStatement) stmt;
          b = expression2ProverExpr(es.getExpression(), localbound);
        } else {
          throw new RuntimeException("procedure2ProverExpr failed");
        }
        vcf = theoremProver.mkDefinedFunction(getProverFriendlyName(proc.getName()), arr, b);
      } else {
        vcf =
            theoremProver.mkUnintFunction(
                getProverFriendlyName(proc.getName()),
                arr,
                type2ProverSort(proc.getReturnVariable().getType()));

        // TODO: uninterpreted functions?
      }
      usedFunctions.put(proc, vcf);
    }
    return usedFunctions.get(proc);
  }
  /**
   * 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;
    }
  }