@Override
  public boolean isSuperStateOf(@NotNull DfaMemoryState other) {
    if (!(other instanceof DfaMemoryStateImpl that)) return false;
    if (myEphemeral && !that.myEphemeral) return false;
    if (myStack.size() != that.myStack.size()) return false;
    for (int i = 0; i < myStack.size(); i++) {
      if (!isSuperValue(myStack.get(i), that.myStack.get(i))) return false;
    }
    int[] thisToThat = getClassesMap(that);
    if (thisToThat == null) return false;
    for (DistinctPairSet.DistinctPair pair : myDistinctClasses) {
      int firstIndex = thisToThat[pair.getFirstIndex()];
      int secondIndex = thisToThat[pair.getSecondIndex()];
      if (firstIndex == -1 || secondIndex == -1 || firstIndex == secondIndex) return false;
      RelationType relation = that.myDistinctClasses.getRelation(firstIndex, secondIndex);
      if (relation == null || pair.isOrdered() && relation != RelationType.LT) return false;
    }
    for (Map.Entry<DfaVariableValue, DfType> entry : this.myVariableTypes.entrySet()) {
      DfaVariableValue value = entry.getKey();
      DfType thisType = entry.getValue();
      // the inherent variable type is not always a superstate for any non-inherent type
      // (e.g. inherent can be nullable, but current type can be notnull)
      // so we cannot limit checking to myVariableTypes map only
      DfType thatType = that.getDfType(value);
      if(!thisType.isMergeable(thatType)) return false;
    }
    for (Map.Entry<DfaVariableValue, DfType> entry : that.myVariableTypes.entrySet()) {
      DfaVariableValue value = entry.getKey();
      if (this.myVariableTypes.containsKey(value)) continue; // already processed in the previous loop
      DfType thisType = this.getDfType(value);
      DfType thatType = entry.getValue();
      if(!thisType.isMergeable(thatType)) return false;
    }
    return true;
  }
@Override
  public boolean isSuperStateOf(@NotNull DfaMemoryState other)