void collectAffectedMarkersAndShiftSubtrees(@Nullable IntervalNode<T> root,
                                              int start, int end, int lengthDelta,
                                              @NotNull List<? super IntervalNode<T>> affected) {
    if (root == null) return;
    pushDelta(root);

    int maxEnd = root.maxEnd;
    assert root.isValid();

    boolean hasAliveKeys = root.hasAliveKey(false);
    if (!hasAliveKeys) {
      // marker was garbage collected
      affected.add(root);
    }
    if (start > maxEnd) {
      // no need to bother
    }
    else if (end < root.intervalStart()) {
      // shift entire subtree
      root.changeDelta(lengthDelta);
      IntervalNode<T> left = root.getLeft();
      if (left != null) {
        left.changeDelta(-lengthDelta);
      }
      pushDelta(root);
      collectAffectedMarkersAndShiftSubtrees(left, start, end, lengthDelta, affected);
      correctMax(root, 0);
    }
    else {
      if (start <= root.intervalEnd()) {
        // unlucky enough so that change affects the interval
        if (hasAliveKeys) affected.add(root); // otherwise, we've already added it
        root.setValid(false);  //make invisible
      }

      collectAffectedMarkersAndShiftSubtrees(root.getLeft(), start, end, lengthDelta, affected);
      collectAffectedMarkersAndShiftSubtrees(root.getRight(), start, end, lengthDelta, affected);
      correctMax(root, 0);
    }
  }
void collectAffectedMarkersAndShiftSubtrees(@Nullable IntervalNode<T> root,
                                              int start, int end, int lengthDelta,
                                              @NotNull List<? super IntervalNode<T>> affected)