@Override
  public AbstractState getVariableExpandedStateForProofChecking(
      AbstractState pRootState, Block pReducedContext, AbstractState pReducedState) {

    PredicateAbstractState rootState = (PredicateAbstractState) pRootState;
    PredicateAbstractState reducedState = (PredicateAbstractState) pReducedState;

    if (!reducedState.isAbstractionState()) {
      return reducedState;
    }

    AbstractionFormula rootAbstraction = rootState.getAbstractionFormula();
    AbstractionFormula reducedAbstraction = reducedState.getAbstractionFormula();

    // create region predicates for every atom in formula
    pamgr.extractPredicates(reducedAbstraction.asInstantiatedFormula());

    Collection<AbstractionPredicate> rootPredicates =
        pamgr.extractPredicates(rootAbstraction.asInstantiatedFormula());
    Collection<AbstractionPredicate> relevantRootPredicates =
        cpa.getRelevantPredicatesComputer().getRelevantPredicates(pReducedContext, rootPredicates);
    // for each removed predicate, we have to lookup the old (expanded) value and insert it to the
    // reducedStates region

    PathFormula oldPathFormula = reducedState.getPathFormula();
    SSAMap oldSSA = oldPathFormula.getSsa();

    // pathFormula.getSSa() might not contain index for the newly added variables in predicates;
    // while the actual index is not really important at this point,
    // there still should be at least _some_ index for each variable of the abstraction formula.
    SSAMapBuilder builder = oldSSA.builder();
    SSAMap rootSSA = rootState.getPathFormula().getSsa();
    for (String var : rootSSA.allVariables()) {
      // if we do not have the index in the reduced map..
      if (!oldSSA.containsVariable(var)) {
        // add an index (with the value of rootSSA)
        builder.setIndex(var, rootSSA.getType(var), rootSSA.getIndex(var));
      }
    }
    SSAMap newSSA = builder.build();
    PathFormula newPathFormula = pmgr.makeNewPathFormula(pmgr.makeEmptyPathFormula(), newSSA);

    Region reducedRegion = pamgr.buildRegionFromFormula(reducedAbstraction.asFormula());
    Region rootRegion = pamgr.buildRegionFromFormula(rootAbstraction.asFormula());

    AbstractionFormula newAbstractionFormula =
        pamgr.expand(
            reducedRegion,
            rootRegion,
            relevantRootPredicates,
            newSSA,
            reducedAbstraction.getBlockFormula());

    PersistentMap<CFANode, Integer> abstractionLocations =
        rootState.getAbstractionLocationsOnPath();

    return PredicateAbstractState.mkAbstractionState(
        newPathFormula, newAbstractionFormula, abstractionLocations);
  }
  /**
   * rootSSA might not contain correct indices for the local variables of calling function-scope. so
   * lets build a new SSA from: - local variables from rootSSA, -> update indices (their indices
   * will have "holes") - local variables from expandedSSA, -> ignore indices (their indices are the
   * "holes") - global variables from expandedSSA, -> update indices (we have to keep them) - the
   * local return variables from expandedState. -> update indices (we have to keep them, there can
   * be several ret-vars from distinct functions, ignore them, they are created new, if needed) we
   * copy expandedState and override all local values.
   *
   * @param rootSSA SSA before function-call
   * @param expandedSSA SSA before function-return
   * @param functionExitNode the function-return-location
   * @return new SSAMap
   */
  protected static SSAMap updateIndices(
      final SSAMap rootSSA, final SSAMap expandedSSA, FunctionExitNode functionExitNode) {

    final SSAMapBuilder rootBuilder = rootSSA.builder();

    for (String var : expandedSSA.allVariables()) {
      // Depending on the scope of vars, set either only the lastUsedIndex or the default index.

      if (expandedSSA.containsVariable(var)) { // var was used and maybe overridden inside the block
        final CType type = expandedSSA.getType(var);
        if (var.contains("::")
            && !isReturnVar(var, functionExitNode)) { // var is scoped -> not global

          if (!rootSSA.containsVariable(
              var)) { // inner local variable, never seen before, use fresh index as basis for
                      // further assignments
            rootBuilder.setIndex(var, type, expandedSSA.builder().getFreshIndex(var));

          } else { // outer variable or inner variable from previous function call
            setFreshValueBasis(
                rootBuilder,
                var,
                Math.max(expandedSSA.builder().getFreshIndex(var), rootSSA.getIndex(var)));
          }

        } else {
          // global variable in rootSSA is outdated, the correct index is in expandedSSA.
          // return-variable in rootSSA is outdated, the correct index is in expandedSSA
          // (this is the return-variable of the current function-return).

          // small trick:
          // If MAX(expIndex, rootIndex) is not expIndex,
          // we are in the rebuilding-phase of the recursive BAM-algorithm and leave a cached block.
          // in this case the index is irrelevant and can be set to expIndex (TODO really?).
          // Otherwise (the important case, MAX == expIndex)
          // we are in the refinement step and build the CEX-path.
          rootBuilder.setIndex(var, type, expandedSSA.getIndex(var));
        }
      }
    }

    return rootBuilder.build();
  }
  @Override
  public AbstractState rebuildStateAfterFunctionCall(
      AbstractState pRootState,
      AbstractState pEntryState,
      AbstractState pExpandedState,
      FunctionExitNode exitLocation) {
    final PredicateAbstractState rootState = (PredicateAbstractState) pRootState;
    final PredicateAbstractState entryState = (PredicateAbstractState) pEntryState;
    final PredicateAbstractState expandedState = (PredicateAbstractState) pExpandedState;
    final PersistentMap<CFANode, Integer> abstractionLocations =
        expandedState.getAbstractionLocationsOnPath();

    // TODO why did I copy the next if-statement? when is it used?
    if (!expandedState.isAbstractionState()) {
      return expandedState;
    }

    // we have:
    // - abstraction of rootState with ssa                --> use as it is
    // - callEdge-pathFormula with ssa (from rootState)   --> use as it is, with updated SSAMap
    // - abstraction of functioncall (expandedSSA)        --> instantiate, with updated SSAMap, so
    // that:
    //           - only param and return-var overlap to callEdge
    //           - all other vars are distinct
    final String calledFunction = exitLocation.getFunctionName();
    final PathFormula functionCall = entryState.getAbstractionFormula().getBlockFormula();
    final SSAMap entrySsaWithRet = functionCall.getSsa();
    final SSAMapBuilder entrySsaWithRetBuilder = entrySsaWithRet.builder();
    final SSAMapBuilder summSsa =
        rootState.getAbstractionFormula().getBlockFormula().getSsa().builder();

    final SSAMap expandedSSA = expandedState.getAbstractionFormula().getBlockFormula().getSsa();
    for (String var : expandedSSA.allVariables()) {
      final CType type = expandedSSA.getType(var);
      if (var.startsWith(calledFunction + "::") && var.endsWith(PARAM_VARIABLE_NAME)) {
        int newIndex = entrySsaWithRet.getIndex(var);
        assert entrySsaWithRet.containsVariable(var)
            : "param for function is not used in functioncall";
        entrySsaWithRetBuilder.setIndex(var, type, newIndex);
        setFreshValueBasis(summSsa, var, newIndex);

      } else if (exitLocation.getEntryNode().getReturnVariable().isPresent()
          && exitLocation.getEntryNode().getReturnVariable().get().getQualifiedName().equals(var)) {
        // var.startsWith(calledFunction + "::") && var.endsWith(RETURN_VARIABLE_NAME)
        final int newIndex =
            Math.max(expandedSSA.getIndex(var), entrySsaWithRetBuilder.getFreshIndex(var));
        entrySsaWithRetBuilder.setIndex(var, type, newIndex);
        summSsa.setIndex(var, type, newIndex);

      } else if (!entrySsaWithRet.containsVariable(var)) {
        // non-existent index for variable only used in functioncall, just copy
        final int newIndex = expandedSSA.getIndex(var);
        entrySsaWithRetBuilder.setIndex(var, type, newIndex);
        summSsa.setIndex(var, type, newIndex);

      } else {
        final int newIndex = entrySsaWithRetBuilder.getFreshIndex(var);
        entrySsaWithRetBuilder.setIndex(var, type, newIndex);
        setFreshValueBasis(summSsa, var, newIndex);
      }
    }

    final SSAMap newEntrySsaWithRet = entrySsaWithRetBuilder.build();
    final SSAMap newSummSsa = summSsa.build();

    // function-call needs have new retvars-indices.
    PathFormula functionCallWithSSA =
        new PathFormula(
            functionCall.getFormula(),
            newEntrySsaWithRet,
            functionCall.getPointerTargetSet(),
            functionCall.getLength());

    // concat function-call with function-summary,
    // function-summary will be instantiated with indices for params and retvars.
    PathFormula executedFunction =
        pmgr.makeAnd(functionCallWithSSA, expandedState.getAbstractionFormula().asFormula());

    // after function-execution we have to re-use the previous indices (fromouter scope),
    // thus lets change the SSAmap.
    PathFormula executedFunctionWithSSA =
        new PathFormula(
            executedFunction.getFormula(),
            newSummSsa,
            executedFunction.getPointerTargetSet(),
            executedFunction.getLength());

    // everything is prepared, so build a new AbstractionState.
    // we do this as 'future abstraction', because we do not have enough information
    // (necessary classes and managers) for the abstraction-process at this place.
    PredicateAbstractState rebuildState =
        new PredicateAbstractState.ComputeAbstractionState(
            executedFunctionWithSSA,
            rootState.getAbstractionFormula(),
            exitLocation,
            abstractionLocations);

    logger.log(
        Level.ALL,
        "\noldAbs: ",
        rootState.getAbstractionFormula().asInstantiatedFormula(),
        "\ncall: ",
        functionCallWithSSA,
        "\nsumm: ",
        expandedState.getAbstractionFormula().asFormula(),
        "\nexe: ",
        executedFunction,
        "\nentrySsaRet",
        newEntrySsaWithRet,
        "\nsummSsaRet",
        newSummSsa);

    return rebuildState;
  }