@Override
  public AbstractState join(AbstractState successor, AbstractState reached) {
    Pair<OctagonState, OctagonState> shrinkedStates =
        getShrinkedStates((OctagonState) successor, (OctagonState) reached);
    Octagon newOctagon =
        shrinkedStates
            .getFirst()
            .getOctagon()
            .getManager()
            .union(shrinkedStates.getFirst().getOctagon(), shrinkedStates.getSecond().getOctagon());

    // TODO this should not be necessary however it occurs that a widened state is bottom
    if (shrinkedStates.getFirst().getOctagon().getManager().isEmpty(newOctagon)) {
      throw new AssertionError("bottom state occured where it should not be");
    }

    OctagonState newState =
        new OctagonState(
            newOctagon,
            shrinkedStates.getFirst().getVariableToIndexMap(),
            shrinkedStates.getFirst().getVariableToTypeMap(),
            logger);
    if (((OctagonState) reached).isLoopHead()) {
      newState = newState.asLoopHead();
    }
    if (newState.equals(reached)) {
      return reached;
    } else if (newState.equals(successor)) {
      return successor;
    } else {
      return newState;
    }
  }
  public AbstractState widening(OctagonState successorOct, OctagonState reachedOct) {
    Pair<OctagonState, OctagonState> shrinkedStates = getShrinkedStates(successorOct, reachedOct);
    successorOct = shrinkedStates.getFirst();
    reachedOct = shrinkedStates.getSecond();

    Octagon newOctagon =
        reachedOct
            .getOctagon()
            .getManager()
            .widening(reachedOct.getOctagon(), successorOct.getOctagon());

    // TODO this should not be necessary however it occurs that a widened state is bottom
    if (reachedOct.getOctagon().getManager().isEmpty(newOctagon)) {
      newOctagon =
          reachedOct
              .getOctagon()
              .getManager()
              .union(reachedOct.getOctagon(), successorOct.getOctagon());
      logger.log(
          Level.WARNING,
          "bottom state occured where it should not be, using union instead of widening as a fallback");
      if (reachedOct.getOctagon().getManager().isEmpty(newOctagon)) {
        throw new AssertionError("bottom state occured where it should not be");
      }
    }

    OctagonState newState =
        new OctagonState(
            newOctagon,
            successorOct.getVariableToIndexMap(),
            successorOct.getVariableToTypeMap(),
            logger);
    if (reachedOct.isLoopHead()) {
      newState = newState.asLoopHead();
    }
    if (newState.equals(successorOct)) {
      return successorOct;
    } else if (newState.equals(reachedOct)) {
      return reachedOct;
    } else {
      return newState;
    }
  }