@Override
    public @NotNull LongRangeSet join(@NotNull LongRangeSet other) {
      if (other.isEmpty() || other == this) return this;
      if (other instanceof Point) {
        return other.join(this);
      }
      if (other instanceof Range) {
        if (other.min() <= max() && min() <= other.max() ||
            (other.max() < min() && other.max() + 1 == min()) ||
            (other.min() > max() && max() + 1 == other.min())) {
          return range(Math.min(min(), other.min()), Math.max(max(), other.max()));
        }
        if (other.max() < min()) {
          return new RangeSet(new long[]{other.min(), other.max(), min(), max()});
        }
        return new RangeSet(new long[]{min(), max(), other.min(), other.max()});
      }
      long[] longs = other.asRangeArray();
      int minIndex = Arrays.binarySearch(longs, min());
      if (minIndex < 0) {
        minIndex = -minIndex - 1;
        if (minIndex % 2 == 0 && minIndex > 0 && longs[minIndex - 1] + 1 == min()) {
          minIndex--;
        }
      }
      else if (minIndex % 2 == 0) {
        minIndex++;
      }
      int maxIndex = Arrays.binarySearch(longs, max());
      if (maxIndex < 0) {
        maxIndex = -maxIndex - 1;
        if (maxIndex % 2 == 0 && maxIndex < longs.length && max() + 1 == longs[maxIndex]) {
          maxIndex++;
        }
      }
      else if (maxIndex % 2 == 0) {
        maxIndex++;
      }
      long[] result = new long[longs.length + 2];
      System.arraycopy(longs, 0, result, 0, minIndex);
      int pos = minIndex;
      if (minIndex % 2 == 0) {
        result[pos++] = min();
      }
      if (maxIndex % 2 == 0) {
        result[pos++] = max();
      }
      System.arraycopy(longs, maxIndex, result, pos, longs.length - maxIndex);
      return fromRanges(result, longs.length + pos - maxIndex);
    }
@Override
    public @NotNull LongRangeSet join(@NotNull LongRangeSet other)