private Collection<List<AbstractState>> callTransferRelation(
      final CompositeState compositeState,
      final CompositePrecision compositePrecision,
      final CFAEdge cfaEdge)
      throws CPATransferException, InterruptedException {
    int resultCount = 1;
    List<AbstractState> componentElements = compositeState.getWrappedStates();
    checkArgument(
        componentElements.size() == size, "State with wrong number of component states given");
    List<Collection<? extends AbstractState>> allComponentsSuccessors = new ArrayList<>(size);

    for (int i = 0; i < size; i++) {
      TransferRelation lCurrentTransfer = transferRelations.get(i);
      AbstractState lCurrentElement = componentElements.get(i);
      Precision lCurrentPrecision = compositePrecision.get(i);

      Collection<? extends AbstractState> componentSuccessors;
      componentSuccessors =
          lCurrentTransfer.getAbstractSuccessorsForEdge(
              lCurrentElement, lCurrentPrecision, cfaEdge);
      resultCount *= componentSuccessors.size();

      if (resultCount == 0) {
        // shortcut
        break;
      }

      allComponentsSuccessors.add(componentSuccessors);
    }

    // create cartesian product of all elements we got
    return createCartesianProduct(allComponentsSuccessors, resultCount);
  }
  private Collection<List<AbstractState>> callStrengthen(
      final List<AbstractState> reachedState,
      final CompositePrecision compositePrecision,
      final CFAEdge cfaEdge)
      throws CPATransferException, InterruptedException {
    List<Collection<? extends AbstractState>> lStrengthenResults = new ArrayList<>(size);
    int resultCount = 1;

    for (int i = 0; i < size; i++) {

      TransferRelation lCurrentTransfer = transferRelations.get(i);
      AbstractState lCurrentElement = reachedState.get(i);
      Precision lCurrentPrecision = compositePrecision.get(i);

      Collection<? extends AbstractState> lResultsList =
          lCurrentTransfer.strengthen(lCurrentElement, reachedState, cfaEdge, lCurrentPrecision);

      if (lResultsList == null) {
        lStrengthenResults.add(Collections.singleton(lCurrentElement));
      } else {
        resultCount *= lResultsList.size();

        if (resultCount == 0) {
          // shortcut
          break;
        }

        lStrengthenResults.add(lResultsList);
      }
    }

    // special case handling if we have predicate and assumption cpas
    // TODO remove as soon as we call strengthen in a fixpoint loop
    if (predicatesIndex >= 0 && assumptionIndex >= 0 && resultCount > 0) {
      AbstractState predElement = Iterables.getOnlyElement(lStrengthenResults.get(predicatesIndex));
      AbstractState assumptionElement =
          Iterables.getOnlyElement(lStrengthenResults.get(assumptionIndex));
      Precision predPrecision = compositePrecision.get(predicatesIndex);
      TransferRelation predTransfer = transferRelations.get(predicatesIndex);

      Collection<? extends AbstractState> predResult =
          predTransfer.strengthen(
              predElement, Collections.singletonList(assumptionElement), cfaEdge, predPrecision);
      resultCount *= predResult.size();

      lStrengthenResults.set(predicatesIndex, predResult);
    }

    // create cartesian product
    Collection<List<AbstractState>> strengthenedStates =
        createCartesianProduct(lStrengthenResults, resultCount);

    // If state was not a target state before but a target state was found during strengthening,
    // we call strengthen again such that the other CPAs can act on this information.
    // Note that this terminates because in the inner call the input state
    // is already a target state and this branch won't be taken.
    // TODO Generalize this into a full fixpoint algorithm.
    if (!any(reachedState, IS_TARGET_STATE)) {
      Collection<List<AbstractState>> newStrengthenedStates = new ArrayList<>(resultCount);

      for (List<AbstractState> strengthenedState : strengthenedStates) {
        if (any(strengthenedState, IS_TARGET_STATE)) {
          newStrengthenedStates.addAll(
              callStrengthen(strengthenedState, compositePrecision, cfaEdge));
        } else {
          newStrengthenedStates.add(strengthenedState);
        }
      }
      return newStrengthenedStates;

    } else {
      return strengthenedStates;
    }
  }