001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.collect;
016
017import static com.google.common.base.Preconditions.checkArgument;
018import static com.google.common.base.Preconditions.checkElementIndex;
019import static com.google.common.base.Preconditions.checkNotNull;
020import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER;
021import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER;
022import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT;
023
024import com.google.common.annotations.Beta;
025import com.google.common.annotations.GwtIncompatible;
026import com.google.common.collect.SortedLists.KeyAbsentBehavior;
027import com.google.common.collect.SortedLists.KeyPresentBehavior;
028import com.google.common.primitives.Ints;
029import com.google.errorprone.annotations.CanIgnoreReturnValue;
030import com.google.errorprone.annotations.DoNotCall;
031import com.google.errorprone.annotations.concurrent.LazyInit;
032import java.io.Serializable;
033import java.util.Collections;
034import java.util.Iterator;
035import java.util.List;
036import java.util.NoSuchElementException;
037import java.util.Set;
038import java.util.stream.Collector;
039import org.checkerframework.checker.nullness.qual.Nullable;
040
041/**
042 * A {@link RangeSet} whose contents will never change, with many other important properties
043 * detailed at {@link ImmutableCollection}.
044 *
045 * @author Louis Wasserman
046 * @since 14.0
047 */
048@Beta
049@GwtIncompatible
050public final class ImmutableRangeSet<C extends Comparable> extends AbstractRangeSet<C>
051    implements Serializable {
052
053  private static final ImmutableRangeSet<Comparable<?>> EMPTY =
054      new ImmutableRangeSet<>(ImmutableList.<Range<Comparable<?>>>of());
055
056  private static final ImmutableRangeSet<Comparable<?>> ALL =
057      new ImmutableRangeSet<>(ImmutableList.of(Range.<Comparable<?>>all()));
058
059  /**
060   * Returns a {@code Collector} that accumulates the input elements into a new {@code
061   * ImmutableRangeSet}. As in {@link Builder}, overlapping ranges are not permitted and adjacent
062   * ranges will be merged.
063   *
064   * @since 23.1
065   */
066  public static <E extends Comparable<? super E>>
067      Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
068    return CollectCollectors.toImmutableRangeSet();
069  }
070
071  /** Returns an empty immutable range set. */
072  @SuppressWarnings("unchecked")
073  public static <C extends Comparable> ImmutableRangeSet<C> of() {
074    return (ImmutableRangeSet<C>) EMPTY;
075  }
076
077  /**
078   * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty()
079   * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}.
080   */
081  public static <C extends Comparable> ImmutableRangeSet<C> of(Range<C> range) {
082    checkNotNull(range);
083    if (range.isEmpty()) {
084      return of();
085    } else if (range.equals(Range.all())) {
086      return all();
087    } else {
088      return new ImmutableRangeSet<C>(ImmutableList.of(range));
089    }
090  }
091
092  /** Returns an immutable range set containing the single range {@link Range#all()}. */
093  @SuppressWarnings("unchecked")
094  static <C extends Comparable> ImmutableRangeSet<C> all() {
095    return (ImmutableRangeSet<C>) ALL;
096  }
097
098  /** Returns an immutable copy of the specified {@code RangeSet}. */
099  public static <C extends Comparable> ImmutableRangeSet<C> copyOf(RangeSet<C> rangeSet) {
100    checkNotNull(rangeSet);
101    if (rangeSet.isEmpty()) {
102      return of();
103    } else if (rangeSet.encloses(Range.<C>all())) {
104      return all();
105    }
106
107    if (rangeSet instanceof ImmutableRangeSet) {
108      ImmutableRangeSet<C> immutableRangeSet = (ImmutableRangeSet<C>) rangeSet;
109      if (!immutableRangeSet.isPartialView()) {
110        return immutableRangeSet;
111      }
112    }
113    return new ImmutableRangeSet<C>(ImmutableList.copyOf(rangeSet.asRanges()));
114  }
115
116  /**
117   * Returns an {@code ImmutableRangeSet} containing each of the specified disjoint ranges.
118   * Overlapping ranges and empty ranges are forbidden, though adjacent ranges are permitted and
119   * will be merged.
120   *
121   * @throws IllegalArgumentException if any ranges overlap or are empty
122   * @since 21.0
123   */
124  public static <C extends Comparable<?>> ImmutableRangeSet<C> copyOf(Iterable<Range<C>> ranges) {
125    return new ImmutableRangeSet.Builder<C>().addAll(ranges).build();
126  }
127
128  /**
129   * Returns an {@code ImmutableRangeSet} representing the union of the specified ranges.
130   *
131   * <p>This is the smallest {@code RangeSet} which encloses each of the specified ranges. Duplicate
132   * or connected ranges are permitted, and will be coalesced in the result.
133   *
134   * @since 21.0
135   */
136  public static <C extends Comparable<?>> ImmutableRangeSet<C> unionOf(Iterable<Range<C>> ranges) {
137    return copyOf(TreeRangeSet.create(ranges));
138  }
139
140  ImmutableRangeSet(ImmutableList<Range<C>> ranges) {
141    this.ranges = ranges;
142  }
143
144  private ImmutableRangeSet(ImmutableList<Range<C>> ranges, ImmutableRangeSet<C> complement) {
145    this.ranges = ranges;
146    this.complement = complement;
147  }
148
149  private final transient ImmutableList<Range<C>> ranges;
150
151  @Override
152  public boolean intersects(Range<C> otherRange) {
153    int ceilingIndex =
154        SortedLists.binarySearch(
155            ranges,
156            Range.<C>lowerBoundFn(),
157            otherRange.lowerBound,
158            Ordering.natural(),
159            ANY_PRESENT,
160            NEXT_HIGHER);
161    if (ceilingIndex < ranges.size()
162        && ranges.get(ceilingIndex).isConnected(otherRange)
163        && !ranges.get(ceilingIndex).intersection(otherRange).isEmpty()) {
164      return true;
165    }
166    return ceilingIndex > 0
167        && ranges.get(ceilingIndex - 1).isConnected(otherRange)
168        && !ranges.get(ceilingIndex - 1).intersection(otherRange).isEmpty();
169  }
170
171  @Override
172  public boolean encloses(Range<C> otherRange) {
173    int index =
174        SortedLists.binarySearch(
175            ranges,
176            Range.<C>lowerBoundFn(),
177            otherRange.lowerBound,
178            Ordering.natural(),
179            ANY_PRESENT,
180            NEXT_LOWER);
181    return index != -1 && ranges.get(index).encloses(otherRange);
182  }
183
184  @Override
185  public Range<C> rangeContaining(C value) {
186    int index =
187        SortedLists.binarySearch(
188            ranges,
189            Range.<C>lowerBoundFn(),
190            Cut.belowValue(value),
191            Ordering.natural(),
192            ANY_PRESENT,
193            NEXT_LOWER);
194    if (index != -1) {
195      Range<C> range = ranges.get(index);
196      return range.contains(value) ? range : null;
197    }
198    return null;
199  }
200
201  @Override
202  public Range<C> span() {
203    if (ranges.isEmpty()) {
204      throw new NoSuchElementException();
205    }
206    return Range.create(ranges.get(0).lowerBound, ranges.get(ranges.size() - 1).upperBound);
207  }
208
209  @Override
210  public boolean isEmpty() {
211    return ranges.isEmpty();
212  }
213
214  /**
215   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
216   *
217   * @throws UnsupportedOperationException always
218   * @deprecated Unsupported operation.
219   */
220  @Deprecated
221  @Override
222  @DoNotCall("Always throws UnsupportedOperationException")
223  public void add(Range<C> range) {
224    throw new UnsupportedOperationException();
225  }
226
227  /**
228   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
229   *
230   * @throws UnsupportedOperationException always
231   * @deprecated Unsupported operation.
232   */
233  @Deprecated
234  @Override
235  @DoNotCall("Always throws UnsupportedOperationException")
236  public void addAll(RangeSet<C> other) {
237    throw new UnsupportedOperationException();
238  }
239
240  /**
241   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
242   *
243   * @throws UnsupportedOperationException always
244   * @deprecated Unsupported operation.
245   */
246  @Deprecated
247  @Override
248  @DoNotCall("Always throws UnsupportedOperationException")
249  public void addAll(Iterable<Range<C>> other) {
250    throw new UnsupportedOperationException();
251  }
252
253  /**
254   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
255   *
256   * @throws UnsupportedOperationException always
257   * @deprecated Unsupported operation.
258   */
259  @Deprecated
260  @Override
261  @DoNotCall("Always throws UnsupportedOperationException")
262  public void remove(Range<C> range) {
263    throw new UnsupportedOperationException();
264  }
265
266  /**
267   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
268   *
269   * @throws UnsupportedOperationException always
270   * @deprecated Unsupported operation.
271   */
272  @Deprecated
273  @Override
274  @DoNotCall("Always throws UnsupportedOperationException")
275  public void removeAll(RangeSet<C> other) {
276    throw new UnsupportedOperationException();
277  }
278
279  /**
280   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
281   *
282   * @throws UnsupportedOperationException always
283   * @deprecated Unsupported operation.
284   */
285  @Deprecated
286  @Override
287  @DoNotCall("Always throws UnsupportedOperationException")
288  public void removeAll(Iterable<Range<C>> other) {
289    throw new UnsupportedOperationException();
290  }
291
292  @Override
293  public ImmutableSet<Range<C>> asRanges() {
294    if (ranges.isEmpty()) {
295      return ImmutableSet.of();
296    }
297    return new RegularImmutableSortedSet<>(ranges, Range.<C>rangeLexOrdering());
298  }
299
300  @Override
301  public ImmutableSet<Range<C>> asDescendingSetOfRanges() {
302    if (ranges.isEmpty()) {
303      return ImmutableSet.of();
304    }
305    return new RegularImmutableSortedSet<>(ranges.reverse(), Range.<C>rangeLexOrdering().reverse());
306  }
307
308  @LazyInit private transient ImmutableRangeSet<C> complement;
309
310  private final class ComplementRanges extends ImmutableList<Range<C>> {
311    // True if the "positive" range set is empty or bounded below.
312    private final boolean positiveBoundedBelow;
313
314    // True if the "positive" range set is empty or bounded above.
315    private final boolean positiveBoundedAbove;
316
317    private final int size;
318
319    ComplementRanges() {
320      this.positiveBoundedBelow = ranges.get(0).hasLowerBound();
321      this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound();
322
323      int size = ranges.size() - 1;
324      if (positiveBoundedBelow) {
325        size++;
326      }
327      if (positiveBoundedAbove) {
328        size++;
329      }
330      this.size = size;
331    }
332
333    @Override
334    public int size() {
335      return size;
336    }
337
338    @Override
339    public Range<C> get(int index) {
340      checkElementIndex(index, size);
341
342      Cut<C> lowerBound;
343      if (positiveBoundedBelow) {
344        lowerBound = (index == 0) ? Cut.<C>belowAll() : ranges.get(index - 1).upperBound;
345      } else {
346        lowerBound = ranges.get(index).upperBound;
347      }
348
349      Cut<C> upperBound;
350      if (positiveBoundedAbove && index == size - 1) {
351        upperBound = Cut.<C>aboveAll();
352      } else {
353        upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound;
354      }
355
356      return Range.create(lowerBound, upperBound);
357    }
358
359    @Override
360    boolean isPartialView() {
361      return true;
362    }
363  }
364
365  @Override
366  public ImmutableRangeSet<C> complement() {
367    ImmutableRangeSet<C> result = complement;
368    if (result != null) {
369      return result;
370    } else if (ranges.isEmpty()) {
371      return complement = all();
372    } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) {
373      return complement = of();
374    } else {
375      ImmutableList<Range<C>> complementRanges = new ComplementRanges();
376      result = complement = new ImmutableRangeSet<C>(complementRanges, this);
377    }
378    return result;
379  }
380
381  /**
382   * Returns a new range set consisting of the union of this range set and {@code other}.
383   *
384   * <p>This is essentially the same as {@code TreeRangeSet.create(this).addAll(other)} except it
385   * returns an {@code ImmutableRangeSet}.
386   *
387   * @since 21.0
388   */
389  public ImmutableRangeSet<C> union(RangeSet<C> other) {
390    return unionOf(Iterables.concat(asRanges(), other.asRanges()));
391  }
392
393  /**
394   * Returns a new range set consisting of the intersection of this range set and {@code other}.
395   *
396   * <p>This is essentially the same as {@code
397   * TreeRangeSet.create(this).removeAll(other.complement())} except it returns an {@code
398   * ImmutableRangeSet}.
399   *
400   * @since 21.0
401   */
402  public ImmutableRangeSet<C> intersection(RangeSet<C> other) {
403    RangeSet<C> copy = TreeRangeSet.create(this);
404    copy.removeAll(other.complement());
405    return copyOf(copy);
406  }
407
408  /**
409   * Returns a new range set consisting of the difference of this range set and {@code other}.
410   *
411   * <p>This is essentially the same as {@code TreeRangeSet.create(this).removeAll(other)} except it
412   * returns an {@code ImmutableRangeSet}.
413   *
414   * @since 21.0
415   */
416  public ImmutableRangeSet<C> difference(RangeSet<C> other) {
417    RangeSet<C> copy = TreeRangeSet.create(this);
418    copy.removeAll(other);
419    return copyOf(copy);
420  }
421
422  /**
423   * Returns a list containing the nonempty intersections of {@code range} with the ranges in this
424   * range set.
425   */
426  private ImmutableList<Range<C>> intersectRanges(final Range<C> range) {
427    if (ranges.isEmpty() || range.isEmpty()) {
428      return ImmutableList.of();
429    } else if (range.encloses(span())) {
430      return ranges;
431    }
432
433    final int fromIndex;
434    if (range.hasLowerBound()) {
435      fromIndex =
436          SortedLists.binarySearch(
437              ranges,
438              Range.<C>upperBoundFn(),
439              range.lowerBound,
440              KeyPresentBehavior.FIRST_AFTER,
441              KeyAbsentBehavior.NEXT_HIGHER);
442    } else {
443      fromIndex = 0;
444    }
445
446    int toIndex;
447    if (range.hasUpperBound()) {
448      toIndex =
449          SortedLists.binarySearch(
450              ranges,
451              Range.<C>lowerBoundFn(),
452              range.upperBound,
453              KeyPresentBehavior.FIRST_PRESENT,
454              KeyAbsentBehavior.NEXT_HIGHER);
455    } else {
456      toIndex = ranges.size();
457    }
458    final int length = toIndex - fromIndex;
459    if (length == 0) {
460      return ImmutableList.of();
461    } else {
462      return new ImmutableList<Range<C>>() {
463        @Override
464        public int size() {
465          return length;
466        }
467
468        @Override
469        public Range<C> get(int index) {
470          checkElementIndex(index, length);
471          if (index == 0 || index == length - 1) {
472            return ranges.get(index + fromIndex).intersection(range);
473          } else {
474            return ranges.get(index + fromIndex);
475          }
476        }
477
478        @Override
479        boolean isPartialView() {
480          return true;
481        }
482      };
483    }
484  }
485
486  /** Returns a view of the intersection of this range set with the given range. */
487  @Override
488  public ImmutableRangeSet<C> subRangeSet(Range<C> range) {
489    if (!isEmpty()) {
490      Range<C> span = span();
491      if (range.encloses(span)) {
492        return this;
493      } else if (range.isConnected(span)) {
494        return new ImmutableRangeSet<C>(intersectRanges(range));
495      }
496    }
497    return of();
498  }
499
500  /**
501   * Returns an {@link ImmutableSortedSet} containing the same values in the given domain
502   * {@linkplain RangeSet#contains contained} by this range set.
503   *
504   * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For
505   * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty
506   * ranges {@code [3..3)} and {@code [4..4)}.
507   *
508   * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large
509   * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on
510   * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link
511   * Collections#frequency}) can cause major performance problems.
512   *
513   * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's
514   * contents, such as {@code "[1..100]}"}.
515   *
516   * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if
517   *     neither has an upper bound
518   */
519  public ImmutableSortedSet<C> asSet(DiscreteDomain<C> domain) {
520    checkNotNull(domain);
521    if (isEmpty()) {
522      return ImmutableSortedSet.of();
523    }
524    Range<C> span = span().canonical(domain);
525    if (!span.hasLowerBound()) {
526      // according to the spec of canonical, neither this ImmutableRangeSet nor
527      // the range have a lower bound
528      throw new IllegalArgumentException(
529          "Neither the DiscreteDomain nor this range set are bounded below");
530    } else if (!span.hasUpperBound()) {
531      try {
532        domain.maxValue();
533      } catch (NoSuchElementException e) {
534        throw new IllegalArgumentException(
535            "Neither the DiscreteDomain nor this range set are bounded above");
536      }
537    }
538
539    return new AsSet(domain);
540  }
541
542  private final class AsSet extends ImmutableSortedSet<C> {
543    private final DiscreteDomain<C> domain;
544
545    AsSet(DiscreteDomain<C> domain) {
546      super(Ordering.natural());
547      this.domain = domain;
548    }
549
550    private transient @Nullable Integer size;
551
552    @Override
553    public int size() {
554      // racy single-check idiom
555      Integer result = size;
556      if (result == null) {
557        long total = 0;
558        for (Range<C> range : ranges) {
559          total += ContiguousSet.create(range, domain).size();
560          if (total >= Integer.MAX_VALUE) {
561            break;
562          }
563        }
564        result = size = Ints.saturatedCast(total);
565      }
566      return result.intValue();
567    }
568
569    @Override
570    public UnmodifiableIterator<C> iterator() {
571      return new AbstractIterator<C>() {
572        final Iterator<Range<C>> rangeItr = ranges.iterator();
573        Iterator<C> elemItr = Iterators.emptyIterator();
574
575        @Override
576        protected C computeNext() {
577          while (!elemItr.hasNext()) {
578            if (rangeItr.hasNext()) {
579              elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator();
580            } else {
581              return endOfData();
582            }
583          }
584          return elemItr.next();
585        }
586      };
587    }
588
589    @Override
590    @GwtIncompatible("NavigableSet")
591    public UnmodifiableIterator<C> descendingIterator() {
592      return new AbstractIterator<C>() {
593        final Iterator<Range<C>> rangeItr = ranges.reverse().iterator();
594        Iterator<C> elemItr = Iterators.emptyIterator();
595
596        @Override
597        protected C computeNext() {
598          while (!elemItr.hasNext()) {
599            if (rangeItr.hasNext()) {
600              elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator();
601            } else {
602              return endOfData();
603            }
604          }
605          return elemItr.next();
606        }
607      };
608    }
609
610    ImmutableSortedSet<C> subSet(Range<C> range) {
611      return subRangeSet(range).asSet(domain);
612    }
613
614    @Override
615    ImmutableSortedSet<C> headSetImpl(C toElement, boolean inclusive) {
616      return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive)));
617    }
618
619    @Override
620    ImmutableSortedSet<C> subSetImpl(
621        C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) {
622      if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) {
623        return ImmutableSortedSet.of();
624      }
625      return subSet(
626          Range.range(
627              fromElement, BoundType.forBoolean(fromInclusive),
628              toElement, BoundType.forBoolean(toInclusive)));
629    }
630
631    @Override
632    ImmutableSortedSet<C> tailSetImpl(C fromElement, boolean inclusive) {
633      return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive)));
634    }
635
636    @Override
637    public boolean contains(@Nullable Object o) {
638      if (o == null) {
639        return false;
640      }
641      try {
642        @SuppressWarnings("unchecked") // we catch CCE's
643        C c = (C) o;
644        return ImmutableRangeSet.this.contains(c);
645      } catch (ClassCastException e) {
646        return false;
647      }
648    }
649
650    @Override
651    int indexOf(Object target) {
652      if (contains(target)) {
653        @SuppressWarnings("unchecked") // if it's contained, it's definitely a C
654        C c = (C) target;
655        long total = 0;
656        for (Range<C> range : ranges) {
657          if (range.contains(c)) {
658            return Ints.saturatedCast(total + ContiguousSet.create(range, domain).indexOf(c));
659          } else {
660            total += ContiguousSet.create(range, domain).size();
661          }
662        }
663        throw new AssertionError("impossible");
664      }
665      return -1;
666    }
667
668    @Override
669    ImmutableSortedSet<C> createDescendingSet() {
670      return new DescendingImmutableSortedSet<C>(this);
671    }
672
673    @Override
674    boolean isPartialView() {
675      return ranges.isPartialView();
676    }
677
678    @Override
679    public String toString() {
680      return ranges.toString();
681    }
682
683    @Override
684    Object writeReplace() {
685      return new AsSetSerializedForm<C>(ranges, domain);
686    }
687  }
688
689  private static class AsSetSerializedForm<C extends Comparable> implements Serializable {
690    private final ImmutableList<Range<C>> ranges;
691    private final DiscreteDomain<C> domain;
692
693    AsSetSerializedForm(ImmutableList<Range<C>> ranges, DiscreteDomain<C> domain) {
694      this.ranges = ranges;
695      this.domain = domain;
696    }
697
698    Object readResolve() {
699      return new ImmutableRangeSet<C>(ranges).asSet(domain);
700    }
701  }
702
703  /**
704   * Returns {@code true} if this immutable range set's implementation contains references to
705   * user-created objects that aren't accessible via this range set's methods. This is generally
706   * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid
707   * memory leaks.
708   */
709  boolean isPartialView() {
710    return ranges.isPartialView();
711  }
712
713  /** Returns a new builder for an immutable range set. */
714  public static <C extends Comparable<?>> Builder<C> builder() {
715    return new Builder<C>();
716  }
717
718  /**
719   * A builder for immutable range sets.
720   *
721   * @since 14.0
722   */
723  public static class Builder<C extends Comparable<?>> {
724    private final List<Range<C>> ranges;
725
726    public Builder() {
727      this.ranges = Lists.newArrayList();
728    }
729
730    // TODO(lowasser): consider adding union, in addition to add, that does allow overlap
731
732    /**
733     * Add the specified range to this builder. Adjacent ranges are permitted and will be merged,
734     * but overlapping ranges will cause an exception when {@link #build()} is called.
735     *
736     * @throws IllegalArgumentException if {@code range} is empty
737     */
738    @CanIgnoreReturnValue
739    public Builder<C> add(Range<C> range) {
740      checkArgument(!range.isEmpty(), "range must not be empty, but was %s", range);
741      ranges.add(range);
742      return this;
743    }
744
745    /**
746     * Add all ranges from the specified range set to this builder. Adjacent ranges are permitted
747     * and will be merged, but overlapping ranges will cause an exception when {@link #build()} is
748     * called.
749     */
750    @CanIgnoreReturnValue
751    public Builder<C> addAll(RangeSet<C> ranges) {
752      return addAll(ranges.asRanges());
753    }
754
755    /**
756     * Add all of the specified ranges to this builder. Adjacent ranges are permitted and will be
757     * merged, but overlapping ranges will cause an exception when {@link #build()} is called.
758     *
759     * @throws IllegalArgumentException if any inserted ranges are empty
760     * @since 21.0
761     */
762    @CanIgnoreReturnValue
763    public Builder<C> addAll(Iterable<Range<C>> ranges) {
764      for (Range<C> range : ranges) {
765        add(range);
766      }
767      return this;
768    }
769
770    @CanIgnoreReturnValue
771    Builder<C> combine(Builder<C> builder) {
772      addAll(builder.ranges);
773      return this;
774    }
775
776    /**
777     * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder.
778     *
779     * @throws IllegalArgumentException if any input ranges have nonempty overlap
780     */
781    public ImmutableRangeSet<C> build() {
782      ImmutableList.Builder<Range<C>> mergedRangesBuilder =
783          new ImmutableList.Builder<>(ranges.size());
784      Collections.sort(ranges, Range.<C>rangeLexOrdering());
785      PeekingIterator<Range<C>> peekingItr = Iterators.peekingIterator(ranges.iterator());
786      while (peekingItr.hasNext()) {
787        Range<C> range = peekingItr.next();
788        while (peekingItr.hasNext()) {
789          Range<C> nextRange = peekingItr.peek();
790          if (range.isConnected(nextRange)) {
791            checkArgument(
792                range.intersection(nextRange).isEmpty(),
793                "Overlapping ranges not permitted but found %s overlapping %s",
794                range,
795                nextRange);
796            range = range.span(peekingItr.next());
797          } else {
798            break;
799          }
800        }
801        mergedRangesBuilder.add(range);
802      }
803      ImmutableList<Range<C>> mergedRanges = mergedRangesBuilder.build();
804      if (mergedRanges.isEmpty()) {
805        return of();
806      } else if (mergedRanges.size() == 1
807          && Iterables.getOnlyElement(mergedRanges).equals(Range.all())) {
808        return all();
809      } else {
810        return new ImmutableRangeSet<C>(mergedRanges);
811      }
812    }
813  }
814
815  private static final class SerializedForm<C extends Comparable> implements Serializable {
816    private final ImmutableList<Range<C>> ranges;
817
818    SerializedForm(ImmutableList<Range<C>> ranges) {
819      this.ranges = ranges;
820    }
821
822    Object readResolve() {
823      if (ranges.isEmpty()) {
824        return of();
825      } else if (ranges.equals(ImmutableList.of(Range.all()))) {
826        return all();
827      } else {
828        return new ImmutableRangeSet<C>(ranges);
829      }
830    }
831  }
832
833  Object writeReplace() {
834    return new SerializedForm<C>(ranges);
835  }
836}