private @NotNull IntTrinity checkMax(@Nullable IntervalNode<T> root,
                              int deltaUpToRootExclusive,
                              boolean assertInvalid,
                              @NotNull AtomicBoolean allValid,
                              int @NotNull [] keyCounter,
                              int @NotNull [] nodeCounter,
                              @NotNull LongSet ids,
                              boolean allDeltasUpAreNull) {
    if (root == null) return new IntTrinity(Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MIN_VALUE);
    long packedOffsets = root.cachedDeltaUpToRoot;
    if (IntervalNode.modCount(packedOffsets) == getModCount()) {
      assert IntervalNode.allDeltasUpAreNull(packedOffsets) == (root.delta == 0 && allDeltasUpAreNull);
      assert IntervalNode.deltaUpToRoot(packedOffsets) == root.delta + deltaUpToRootExclusive;
    }
    T liveInterval = null;
    for (int i = root.intervals.size() - 1; i >= 0; i--) {
      T t = root.intervals.get(i).get();
      if (t == null) continue;
      liveInterval = t;
      checkBelongsToTheTree(t, false);
      if (((RangeMarkerImpl)t).isValid()) {
        long id = ((RangeMarkerImpl)t).getId();
        boolean added = ids.add(id);
        assert added : t +"\nids:"+ids+"; id="+id+"\n; root.intervals="+root.intervals;
      }
    }
    if (assertInvalid && liveInterval != null) {
      checkBelongsToTheTree(liveInterval, true);
    }

    keyCounter[0]+= root.intervals.size();
    nodeCounter[0]++;
    int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0);
    IntTrinity l = checkMax(root.getLeft(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids, root.delta == 0 && allDeltasUpAreNull);
    int minLeftStart = l.minStart;
    int maxLeftStart = l.maxStart;
    int maxLeftEnd = l.maxEnd;
    IntTrinity r = checkMax(root.getRight(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids, root.delta == 0 && allDeltasUpAreNull);
    int maxRightEnd = r.maxEnd;
    int minRightStart = r.minStart;
    int maxRightStart = r.maxStart;
    if (!root.isValid()) {
      allValid.set(false);
      if (assertInvalid) assert false : root;
      return new IntTrinity(Math.min(minLeftStart, minRightStart), Math.max(maxLeftStart, maxRightStart), Math.max(maxRightEnd, maxLeftEnd));
    }
    IntervalNode<T> parent = root.getParent();
    if (parent != null && assertInvalid && root.hasAliveKey(false)) {
      int c = compareNodes(root, delta, parent, delta - root.delta, new SmartList<>());
      assert c != 0;
      assert c < 0 && parent.getLeft() == root || c > 0 && parent.getRight() == root;
    }
    assert delta + root.maxEnd == Math.max(maxLeftEnd, Math.max(maxRightEnd, delta + root.intervalEnd()));
    int myStartOffset = delta + root.intervalStart();
    assert maxLeftStart <= myStartOffset;
    assert minRightStart >= myStartOffset;
    assert myStartOffset >= 0;
    assert minLeftStart == Integer.MAX_VALUE || minLeftStart <= myStartOffset;
    assert maxRightStart == Integer.MIN_VALUE || maxRightStart >= myStartOffset;
    int minStart = Math.min(minLeftStart, myStartOffset);
    int maxStart = Math.max(myStartOffset, Math.max(maxLeftStart, maxRightStart));
    assert minStart <= maxStart;
    return new IntTrinity(minStart, maxStart, root.maxEnd + delta);
  }
private @NotNull IntTrinity checkMax(@Nullable IntervalNode<T> root,
                              int deltaUpToRootExclusive,
                              boolean assertInvalid,
                              @NotNull AtomicBoolean allValid,
                              int @NotNull [] keyCounter,
                              int @NotNull [] nodeCounter,
                              @NotNull LongSet ids,
                              boolean allDeltasUpAreNull)