• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.collect;
16 
17 import static com.google.common.collect.BoundType.OPEN;
18 import static com.google.common.collect.testing.Helpers.mapEntry;
19 import static org.junit.Assert.assertThrows;
20 
21 import com.google.common.annotations.GwtIncompatible;
22 import com.google.common.collect.testing.MapTestSuiteBuilder;
23 import com.google.common.collect.testing.SampleElements;
24 import com.google.common.collect.testing.TestMapGenerator;
25 import com.google.common.collect.testing.features.CollectionFeature;
26 import com.google.common.collect.testing.features.CollectionSize;
27 import com.google.common.collect.testing.features.MapFeature;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.NoSuchElementException;
32 import junit.framework.Test;
33 import junit.framework.TestCase;
34 import junit.framework.TestSuite;
35 
36 /**
37  * Tests for {@code TreeRangeMap}.
38  *
39  * @author Louis Wasserman
40  */
41 @GwtIncompatible // NavigableMap
42 public class TreeRangeMapTest extends TestCase {
suite()43   public static Test suite() {
44     TestSuite suite = new TestSuite();
45     suite.addTestSuite(TreeRangeMapTest.class);
46     suite.addTest(
47         MapTestSuiteBuilder.using(
48                 new TestMapGenerator<Range<Integer>, String>() {
49                   @Override
50                   public SampleElements<Entry<Range<Integer>, String>> samples() {
51                     return new SampleElements<>(
52                         mapEntry(Range.singleton(0), "banana"),
53                         mapEntry(Range.closedOpen(3, 5), "frisbee"),
54                         mapEntry(Range.atMost(-1), "fruitcake"),
55                         mapEntry(Range.open(10, 15), "elephant"),
56                         mapEntry(Range.closed(20, 22), "umbrella"));
57                   }
58 
59                   @Override
60                   public Map<Range<Integer>, String> create(Object... elements) {
61                     RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
62                     for (Object o : elements) {
63                       @SuppressWarnings("unchecked")
64                       Entry<Range<Integer>, String> entry = (Entry<Range<Integer>, String>) o;
65                       rangeMap.put(entry.getKey(), entry.getValue());
66                     }
67                     return rangeMap.asMapOfRanges();
68                   }
69 
70                   @SuppressWarnings("unchecked")
71                   @Override
72                   public Entry<Range<Integer>, String>[] createArray(int length) {
73                     return new Entry[length];
74                   }
75 
76                   @Override
77                   public Iterable<Entry<Range<Integer>, String>> order(
78                       List<Entry<Range<Integer>, String>> insertionOrder) {
79                     return Range.<Integer>rangeLexOrdering().onKeys().sortedCopy(insertionOrder);
80                   }
81 
82                   @SuppressWarnings("unchecked")
83                   @Override
84                   public Range<Integer>[] createKeyArray(int length) {
85                     return new Range[length];
86                   }
87 
88                   @Override
89                   public String[] createValueArray(int length) {
90                     return new String[length];
91                   }
92                 })
93             .named("TreeRangeMap.asMapOfRanges")
94             .withFeatures(
95                 CollectionSize.ANY,
96                 MapFeature.SUPPORTS_REMOVE,
97                 MapFeature.ALLOWS_ANY_NULL_QUERIES,
98                 CollectionFeature.KNOWN_ORDER,
99                 CollectionFeature.SUPPORTS_ITERATOR_REMOVE)
100             .createTestSuite());
101 
102     suite.addTest(
103         MapTestSuiteBuilder.using(
104                 new TestMapGenerator<Range<Integer>, String>() {
105                   @Override
106                   public SampleElements<Entry<Range<Integer>, String>> samples() {
107                     return new SampleElements<>(
108                         mapEntry(Range.singleton(0), "banana"),
109                         mapEntry(Range.closedOpen(3, 5), "frisbee"),
110                         mapEntry(Range.atMost(-1), "fruitcake"),
111                         mapEntry(Range.open(10, 15), "elephant"),
112                         mapEntry(Range.closed(20, 22), "umbrella"));
113                   }
114 
115                   @Override
116                   public Map<Range<Integer>, String> create(Object... elements) {
117                     RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
118                     for (Object o : elements) {
119                       @SuppressWarnings("unchecked")
120                       Entry<Range<Integer>, String> entry = (Entry<Range<Integer>, String>) o;
121                       rangeMap.put(entry.getKey(), entry.getValue());
122                     }
123                     return rangeMap.subRangeMap(Range.atMost(22)).asMapOfRanges();
124                   }
125 
126                   @SuppressWarnings("unchecked")
127                   @Override
128                   public Entry<Range<Integer>, String>[] createArray(int length) {
129                     return new Entry[length];
130                   }
131 
132                   @Override
133                   public Iterable<Entry<Range<Integer>, String>> order(
134                       List<Entry<Range<Integer>, String>> insertionOrder) {
135                     return Range.<Integer>rangeLexOrdering().onKeys().sortedCopy(insertionOrder);
136                   }
137 
138                   @SuppressWarnings("unchecked")
139                   @Override
140                   public Range<Integer>[] createKeyArray(int length) {
141                     return new Range[length];
142                   }
143 
144                   @Override
145                   public String[] createValueArray(int length) {
146                     return new String[length];
147                   }
148                 })
149             .named("TreeRangeMap.subRangeMap.asMapOfRanges")
150             .withFeatures(
151                 CollectionSize.ANY,
152                 MapFeature.SUPPORTS_REMOVE,
153                 MapFeature.ALLOWS_ANY_NULL_QUERIES,
154                 CollectionFeature.KNOWN_ORDER)
155             .createTestSuite());
156 
157     suite.addTest(
158         MapTestSuiteBuilder.using(
159                 new TestMapGenerator<Range<Integer>, String>() {
160                   @Override
161                   public SampleElements<Entry<Range<Integer>, String>> samples() {
162                     return new SampleElements<>(
163                         mapEntry(Range.singleton(0), "banana"),
164                         mapEntry(Range.closedOpen(3, 5), "frisbee"),
165                         mapEntry(Range.atMost(-1), "fruitcake"),
166                         mapEntry(Range.open(10, 15), "elephant"),
167                         mapEntry(Range.closed(20, 22), "umbrella"));
168                   }
169 
170                   @Override
171                   public Map<Range<Integer>, String> create(Object... elements) {
172                     RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
173                     for (Object o : elements) {
174                       @SuppressWarnings("unchecked")
175                       Entry<Range<Integer>, String> entry = (Entry<Range<Integer>, String>) o;
176                       rangeMap.put(entry.getKey(), entry.getValue());
177                     }
178                     return rangeMap.asDescendingMapOfRanges();
179                   }
180 
181                   @SuppressWarnings("unchecked")
182                   @Override
183                   public Entry<Range<Integer>, String>[] createArray(int length) {
184                     return new Entry[length];
185                   }
186 
187                   @Override
188                   public Iterable<Entry<Range<Integer>, String>> order(
189                       List<Entry<Range<Integer>, String>> insertionOrder) {
190                     return Range.<Integer>rangeLexOrdering()
191                         .reverse()
192                         .onKeys()
193                         .sortedCopy(insertionOrder);
194                   }
195 
196                   @SuppressWarnings("unchecked")
197                   @Override
198                   public Range<Integer>[] createKeyArray(int length) {
199                     return new Range[length];
200                   }
201 
202                   @Override
203                   public String[] createValueArray(int length) {
204                     return new String[length];
205                   }
206                 })
207             .named("TreeRangeMap.asDescendingMapOfRanges")
208             .withFeatures(
209                 CollectionSize.ANY,
210                 MapFeature.SUPPORTS_REMOVE,
211                 MapFeature.ALLOWS_ANY_NULL_QUERIES,
212                 CollectionFeature.KNOWN_ORDER,
213                 CollectionFeature.SUPPORTS_ITERATOR_REMOVE)
214             .createTestSuite());
215 
216     suite.addTest(
217         MapTestSuiteBuilder.using(
218                 new TestMapGenerator<Range<Integer>, String>() {
219                   @Override
220                   public SampleElements<Entry<Range<Integer>, String>> samples() {
221                     return new SampleElements<>(
222                         mapEntry(Range.singleton(0), "banana"),
223                         mapEntry(Range.closedOpen(3, 5), "frisbee"),
224                         mapEntry(Range.atMost(-1), "fruitcake"),
225                         mapEntry(Range.open(10, 15), "elephant"),
226                         mapEntry(Range.closed(20, 22), "umbrella"));
227                   }
228 
229                   @Override
230                   public Map<Range<Integer>, String> create(Object... elements) {
231                     RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
232                     for (Object o : elements) {
233                       @SuppressWarnings("unchecked")
234                       Entry<Range<Integer>, String> entry = (Entry<Range<Integer>, String>) o;
235                       rangeMap.put(entry.getKey(), entry.getValue());
236                     }
237                     return rangeMap.subRangeMap(Range.atMost(22)).asDescendingMapOfRanges();
238                   }
239 
240                   @SuppressWarnings("unchecked")
241                   @Override
242                   public Entry<Range<Integer>, String>[] createArray(int length) {
243                     return new Entry[length];
244                   }
245 
246                   @Override
247                   public Iterable<Entry<Range<Integer>, String>> order(
248                       List<Entry<Range<Integer>, String>> insertionOrder) {
249                     return Range.<Integer>rangeLexOrdering()
250                         .reverse()
251                         .onKeys()
252                         .sortedCopy(insertionOrder);
253                   }
254 
255                   @SuppressWarnings("unchecked")
256                   @Override
257                   public Range<Integer>[] createKeyArray(int length) {
258                     return new Range[length];
259                   }
260 
261                   @Override
262                   public String[] createValueArray(int length) {
263                     return new String[length];
264                   }
265                 })
266             .named("TreeRangeMap.subRangeMap.asDescendingMapOfRanges")
267             .withFeatures(
268                 CollectionSize.ANY,
269                 MapFeature.SUPPORTS_REMOVE,
270                 MapFeature.ALLOWS_ANY_NULL_QUERIES,
271                 CollectionFeature.KNOWN_ORDER)
272             .createTestSuite());
273     return suite;
274   }
275 
276   private static final ImmutableList<Range<Integer>> RANGES;
277   private static final int MIN_BOUND = -1;
278   private static final int MAX_BOUND = 1;
279 
280   static {
281     ImmutableList.Builder<Range<Integer>> builder = ImmutableList.builder();
282 
all()283     builder.add(Range.<Integer>all());
284 
285     // Add one-ended ranges
286     for (int i = MIN_BOUND; i <= MAX_BOUND; i++) {
287       for (BoundType type : BoundType.values()) {
Range.upTo(i, type)288         builder.add(Range.upTo(i, type));
Range.downTo(i, type)289         builder.add(Range.downTo(i, type));
290       }
291     }
292 
293     // Add two-ended ranges
294     for (int i = MIN_BOUND; i <= MAX_BOUND; i++) {
295       for (int j = i; j <= MAX_BOUND; j++) {
296         for (BoundType lowerType : BoundType.values()) {
297           for (BoundType upperType : BoundType.values()) {
298             if (i == j & lowerType == OPEN & upperType == OPEN) {
299               continue;
300             }
Range.range(i, lowerType, j, upperType)301             builder.add(Range.range(i, lowerType, j, upperType));
302           }
303         }
304       }
305     }
306     RANGES = builder.build();
307   }
308 
testSpanSingleRange()309   public void testSpanSingleRange() {
310     for (Range<Integer> range : RANGES) {
311       RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
312       rangeMap.put(range, 1);
313 
314       try {
315         assertEquals(range, rangeMap.span());
316         assertFalse(range.isEmpty());
317       } catch (NoSuchElementException e) {
318         assertTrue(range.isEmpty());
319       }
320     }
321   }
322 
testSpanTwoRanges()323   public void testSpanTwoRanges() {
324     for (Range<Integer> range1 : RANGES) {
325       for (Range<Integer> range2 : RANGES) {
326         RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
327         rangeMap.put(range1, 1);
328         rangeMap.put(range2, 2);
329 
330         Range<Integer> expected;
331         if (range1.isEmpty()) {
332           if (range2.isEmpty()) {
333             expected = null;
334           } else {
335             expected = range2;
336           }
337         } else {
338           if (range2.isEmpty()) {
339             expected = range1;
340           } else {
341             expected = range1.span(range2);
342           }
343         }
344 
345         try {
346           assertEquals(expected, rangeMap.span());
347           assertNotNull(expected);
348         } catch (NoSuchElementException e) {
349           assertNull(expected);
350         }
351       }
352     }
353   }
354 
testAllRangesAlone()355   public void testAllRangesAlone() {
356     for (Range<Integer> range : RANGES) {
357       Map<Integer, Integer> model = Maps.newHashMap();
358       putModel(model, range, 1);
359       RangeMap<Integer, Integer> test = TreeRangeMap.create();
360       test.put(range, 1);
361       verify(model, test);
362     }
363   }
364 
testAllRangePairs()365   public void testAllRangePairs() {
366     for (Range<Integer> range1 : RANGES) {
367       for (Range<Integer> range2 : RANGES) {
368         Map<Integer, Integer> model = Maps.newHashMap();
369         putModel(model, range1, 1);
370         putModel(model, range2, 2);
371         RangeMap<Integer, Integer> test = TreeRangeMap.create();
372         test.put(range1, 1);
373         test.put(range2, 2);
374         verify(model, test);
375       }
376     }
377   }
378 
testAllRangeTriples()379   public void testAllRangeTriples() {
380     for (Range<Integer> range1 : RANGES) {
381       for (Range<Integer> range2 : RANGES) {
382         for (Range<Integer> range3 : RANGES) {
383           Map<Integer, Integer> model = Maps.newHashMap();
384           putModel(model, range1, 1);
385           putModel(model, range2, 2);
386           putModel(model, range3, 3);
387           RangeMap<Integer, Integer> test = TreeRangeMap.create();
388           test.put(range1, 1);
389           test.put(range2, 2);
390           test.put(range3, 3);
391           verify(model, test);
392         }
393       }
394     }
395   }
396 
testPutAll()397   public void testPutAll() {
398     for (Range<Integer> range1 : RANGES) {
399       for (Range<Integer> range2 : RANGES) {
400         for (Range<Integer> range3 : RANGES) {
401           Map<Integer, Integer> model = Maps.newHashMap();
402           putModel(model, range1, 1);
403           putModel(model, range2, 2);
404           putModel(model, range3, 3);
405           RangeMap<Integer, Integer> test = TreeRangeMap.create();
406           RangeMap<Integer, Integer> test2 = TreeRangeMap.create();
407           // put range2 and range3 into test2, and then put test2 into test
408           test.put(range1, 1);
409           test2.put(range2, 2);
410           test2.put(range3, 3);
411           test.putAll(test2);
412           verify(model, test);
413         }
414       }
415     }
416   }
417 
testPutAndRemove()418   public void testPutAndRemove() {
419     for (Range<Integer> rangeToPut : RANGES) {
420       for (Range<Integer> rangeToRemove : RANGES) {
421         Map<Integer, Integer> model = Maps.newHashMap();
422         putModel(model, rangeToPut, 1);
423         removeModel(model, rangeToRemove);
424         RangeMap<Integer, Integer> test = TreeRangeMap.create();
425         test.put(rangeToPut, 1);
426         test.remove(rangeToRemove);
427         verify(model, test);
428       }
429     }
430   }
431 
testPutTwoAndRemove()432   public void testPutTwoAndRemove() {
433     for (Range<Integer> rangeToPut1 : RANGES) {
434       for (Range<Integer> rangeToPut2 : RANGES) {
435         for (Range<Integer> rangeToRemove : RANGES) {
436           Map<Integer, Integer> model = Maps.newHashMap();
437           putModel(model, rangeToPut1, 1);
438           putModel(model, rangeToPut2, 2);
439           removeModel(model, rangeToRemove);
440           RangeMap<Integer, Integer> test = TreeRangeMap.create();
441           test.put(rangeToPut1, 1);
442           test.put(rangeToPut2, 2);
443           test.remove(rangeToRemove);
444           verify(model, test);
445         }
446       }
447     }
448   }
449 
450   // identical to testPutTwoAndRemove,
451   // verifies that putCoalescing() doesn't cause any mappings to change relative to put()
testPutCoalescingTwoAndRemove()452   public void testPutCoalescingTwoAndRemove() {
453     for (Range<Integer> rangeToPut1 : RANGES) {
454       for (Range<Integer> rangeToPut2 : RANGES) {
455         for (Range<Integer> rangeToRemove : RANGES) {
456           Map<Integer, Integer> model = Maps.newHashMap();
457           putModel(model, rangeToPut1, 1);
458           putModel(model, rangeToPut2, 2);
459           removeModel(model, rangeToRemove);
460           RangeMap<Integer, Integer> test = TreeRangeMap.create();
461           test.putCoalescing(rangeToPut1, 1);
462           test.putCoalescing(rangeToPut2, 2);
463           test.remove(rangeToRemove);
464           verify(model, test);
465         }
466       }
467     }
468   }
469 
testPutCoalescing()470   public void testPutCoalescing() {
471     // {[0..1): 1, [1..2): 1, [2..3): 2} -> {[0..2): 1, [2..3): 2}
472     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
473     rangeMap.putCoalescing(Range.closedOpen(0, 1), 1);
474     rangeMap.putCoalescing(Range.closedOpen(1, 2), 1);
475     rangeMap.putCoalescing(Range.closedOpen(2, 3), 2);
476     assertEquals(
477         ImmutableMap.of(Range.closedOpen(0, 2), 1, Range.closedOpen(2, 3), 2),
478         rangeMap.asMapOfRanges());
479   }
480 
testPutCoalescingEmpty()481   public void testPutCoalescingEmpty() {
482     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
483     rangeMap.put(Range.closedOpen(0, 1), 1);
484     rangeMap.put(Range.closedOpen(1, 2), 1);
485     assertEquals(
486         ImmutableMap.of(Range.closedOpen(0, 1), 1, Range.closedOpen(1, 2), 1),
487         rangeMap.asMapOfRanges());
488 
489     rangeMap.putCoalescing(Range.closedOpen(1, 1), 1); // empty range coalesces connected ranges
490     assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), rangeMap.asMapOfRanges());
491   }
492 
testPutCoalescingSubmapEmpty()493   public void testPutCoalescingSubmapEmpty() {
494     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
495     rangeMap.put(Range.closedOpen(0, 1), 1);
496     rangeMap.put(Range.closedOpen(1, 2), 1);
497     assertEquals(
498         ImmutableMap.of(Range.closedOpen(0, 1), 1, Range.closedOpen(1, 2), 1),
499         rangeMap.asMapOfRanges());
500 
501     RangeMap<Integer, Integer> subRangeMap = rangeMap.subRangeMap(Range.closedOpen(0, 2));
502     subRangeMap.putCoalescing(Range.closedOpen(1, 1), 1); // empty range coalesces connected ranges
503     assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), subRangeMap.asMapOfRanges());
504     assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), rangeMap.asMapOfRanges());
505   }
506 
testPutCoalescingComplex()507   public void testPutCoalescingComplex() {
508     // {[0..1): 1, [1..3): 1, [3..5): 1, [7..10): 2, [12..15): 2, [18..19): 3}
509     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
510     rangeMap.put(Range.closedOpen(0, 1), 1);
511     rangeMap.put(Range.closedOpen(1, 3), 1);
512     rangeMap.put(Range.closedOpen(3, 5), 1);
513     rangeMap.put(Range.closedOpen(7, 10), 2);
514     rangeMap.put(Range.closedOpen(12, 15), 2);
515     rangeMap.put(Range.closedOpen(18, 19), 3);
516 
517     rangeMap.putCoalescing(Range.closedOpen(-5, -4), 0); // disconnected
518     rangeMap.putCoalescing(Range.closedOpen(-6, -5), 0); // lower than minimum
519 
520     rangeMap.putCoalescing(Range.closedOpen(2, 4), 1); // between
521     rangeMap.putCoalescing(Range.closedOpen(9, 14), 0); // different value
522     rangeMap.putCoalescing(Range.closedOpen(17, 20), 3); // enclosing
523 
524     rangeMap.putCoalescing(Range.closedOpen(22, 23), 4); // disconnected
525     rangeMap.putCoalescing(Range.closedOpen(23, 25), 4); // greater than minimum
526 
527     // {[-6..-4): 0, [0..1): 1, [1..5): 1, [7..9): 2,
528     //  [9..14): 0, [14..15): 2, [17..20): 3, [22..25): 4}
529     assertEquals(
530         new ImmutableMap.Builder<>()
531             .put(Range.closedOpen(-6, -4), 0)
532             .put(Range.closedOpen(0, 1), 1) // not coalesced
533             .put(Range.closedOpen(1, 5), 1)
534             .put(Range.closedOpen(7, 9), 2)
535             .put(Range.closedOpen(9, 14), 0)
536             .put(Range.closedOpen(14, 15), 2)
537             .put(Range.closedOpen(17, 20), 3)
538             .put(Range.closedOpen(22, 25), 4)
539             .build(),
540         rangeMap.asMapOfRanges());
541   }
542 
543 
testSubRangeMapExhaustive()544   public void testSubRangeMapExhaustive() {
545     for (Range<Integer> range1 : RANGES) {
546       for (Range<Integer> range2 : RANGES) {
547         RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
548         rangeMap.put(range1, 1);
549         rangeMap.put(range2, 2);
550 
551         for (Range<Integer> subRange : RANGES) {
552           RangeMap<Integer, Integer> expected = TreeRangeMap.create();
553           for (Entry<Range<Integer>, Integer> entry : rangeMap.asMapOfRanges().entrySet()) {
554             if (entry.getKey().isConnected(subRange)) {
555               expected.put(entry.getKey().intersection(subRange), entry.getValue());
556             }
557           }
558           RangeMap<Integer, Integer> subRangeMap = rangeMap.subRangeMap(subRange);
559           assertEquals(expected, subRangeMap);
560           assertEquals(expected.asMapOfRanges(), subRangeMap.asMapOfRanges());
561           assertEquals(expected.asDescendingMapOfRanges(), subRangeMap.asDescendingMapOfRanges());
562           assertEquals(
563               ImmutableList.copyOf(subRangeMap.asMapOfRanges().entrySet()).reverse(),
564               ImmutableList.copyOf(subRangeMap.asDescendingMapOfRanges().entrySet()));
565 
566           if (!expected.asMapOfRanges().isEmpty()) {
567             assertEquals(expected.span(), subRangeMap.span());
568           }
569 
570           for (int i = MIN_BOUND; i <= MAX_BOUND; i++) {
571             assertEquals(expected.get(i), subRangeMap.get(i));
572           }
573 
574           for (Range<Integer> query : RANGES) {
575             assertEquals(
576                 expected.asMapOfRanges().get(query), subRangeMap.asMapOfRanges().get(query));
577           }
578         }
579       }
580     }
581   }
582 
testSubSubRangeMap()583   public void testSubSubRangeMap() {
584     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
585     rangeMap.put(Range.open(3, 7), 1);
586     rangeMap.put(Range.closed(9, 10), 2);
587     rangeMap.put(Range.closed(12, 16), 3);
588     RangeMap<Integer, Integer> sub1 = rangeMap.subRangeMap(Range.closed(5, 11));
589     assertEquals(
590         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.closed(9, 10), 2), sub1.asMapOfRanges());
591     RangeMap<Integer, Integer> sub2 = sub1.subRangeMap(Range.open(6, 15));
592     assertEquals(
593         ImmutableMap.of(Range.open(6, 7), 1, Range.closed(9, 10), 2), sub2.asMapOfRanges());
594   }
595 
testSubRangeMapPut()596   public void testSubRangeMapPut() {
597     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
598     rangeMap.put(Range.open(3, 7), 1);
599     rangeMap.put(Range.closed(9, 10), 2);
600     rangeMap.put(Range.closed(12, 16), 3);
601     RangeMap<Integer, Integer> sub = rangeMap.subRangeMap(Range.closed(5, 11));
602     assertEquals(
603         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.closed(9, 10), 2), sub.asMapOfRanges());
604     sub.put(Range.closed(7, 9), 4);
605     assertEquals(
606         ImmutableMap.of(
607             Range.closedOpen(5, 7), 1, Range.closed(7, 9), 4, Range.openClosed(9, 10), 2),
608         sub.asMapOfRanges());
609     assertEquals(
610         ImmutableMap.of(
611             Range.open(3, 7),
612             1,
613             Range.closed(7, 9),
614             4,
615             Range.openClosed(9, 10),
616             2,
617             Range.closed(12, 16),
618             3),
619         rangeMap.asMapOfRanges());
620 
621     assertThrows(IllegalArgumentException.class, () -> sub.put(Range.open(9, 12), 5));
622 
623     RangeMap<Integer, Integer> subSub = sub.subRangeMap(Range.closedOpen(5, 5));
624     subSub.put(Range.closedOpen(5, 5), 6); // should be a no-op
625     assertEquals(
626         ImmutableMap.of(
627             Range.open(3, 7),
628             1,
629             Range.closed(7, 9),
630             4,
631             Range.openClosed(9, 10),
632             2,
633             Range.closed(12, 16),
634             3),
635         rangeMap.asMapOfRanges());
636   }
637 
testSubRangeMapPutCoalescing()638   public void testSubRangeMapPutCoalescing() {
639     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
640     rangeMap.put(Range.open(3, 7), 1);
641     rangeMap.put(Range.closed(9, 10), 2);
642     rangeMap.put(Range.closed(12, 16), 3);
643     RangeMap<Integer, Integer> sub = rangeMap.subRangeMap(Range.closed(5, 11));
644     assertEquals(
645         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.closed(9, 10), 2), sub.asMapOfRanges());
646     sub.putCoalescing(Range.closed(7, 9), 2);
647     assertEquals(
648         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.closed(7, 10), 2), sub.asMapOfRanges());
649     assertEquals(
650         ImmutableMap.of(Range.open(3, 7), 1, Range.closed(7, 10), 2, Range.closed(12, 16), 3),
651         rangeMap.asMapOfRanges());
652 
653     sub.putCoalescing(Range.singleton(7), 1);
654     assertEquals(
655         ImmutableMap.of(Range.closed(5, 7), 1, Range.openClosed(7, 10), 2), sub.asMapOfRanges());
656     assertEquals(
657         ImmutableMap.of(
658             Range.open(3, 5),
659             1,
660             Range.closed(5, 7),
661             1,
662             Range.openClosed(7, 10),
663             2,
664             Range.closed(12, 16),
665             3),
666         rangeMap.asMapOfRanges());
667 
668     assertThrows(IllegalArgumentException.class, () -> sub.putCoalescing(Range.open(9, 12), 5));
669   }
670 
testSubRangeMapRemove()671   public void testSubRangeMapRemove() {
672     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
673     rangeMap.put(Range.open(3, 7), 1);
674     rangeMap.put(Range.closed(9, 10), 2);
675     rangeMap.put(Range.closed(12, 16), 3);
676     RangeMap<Integer, Integer> sub = rangeMap.subRangeMap(Range.closed(5, 11));
677     assertEquals(
678         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.closed(9, 10), 2), sub.asMapOfRanges());
679     sub.remove(Range.closed(7, 9));
680     assertEquals(
681         ImmutableMap.of(Range.closedOpen(5, 7), 1, Range.openClosed(9, 10), 2),
682         sub.asMapOfRanges());
683     assertEquals(
684         ImmutableMap.of(Range.open(3, 7), 1, Range.openClosed(9, 10), 2, Range.closed(12, 16), 3),
685         rangeMap.asMapOfRanges());
686 
687     sub.remove(Range.closed(3, 9));
688     assertEquals(ImmutableMap.of(Range.openClosed(9, 10), 2), sub.asMapOfRanges());
689     assertEquals(
690         ImmutableMap.of(Range.open(3, 5), 1, Range.openClosed(9, 10), 2, Range.closed(12, 16), 3),
691         rangeMap.asMapOfRanges());
692   }
693 
testSubRangeMapClear()694   public void testSubRangeMapClear() {
695     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
696     rangeMap.put(Range.open(3, 7), 1);
697     rangeMap.put(Range.closed(9, 10), 2);
698     rangeMap.put(Range.closed(12, 16), 3);
699     RangeMap<Integer, Integer> sub = rangeMap.subRangeMap(Range.closed(5, 11));
700     sub.clear();
701     assertEquals(
702         ImmutableMap.of(Range.open(3, 5), 1, Range.closed(12, 16), 3), rangeMap.asMapOfRanges());
703   }
704 
verify(Map<Integer, Integer> model, RangeMap<Integer, Integer> test)705   private void verify(Map<Integer, Integer> model, RangeMap<Integer, Integer> test) {
706     for (int i = MIN_BOUND - 1; i <= MAX_BOUND + 1; i++) {
707       assertEquals(model.get(i), test.get(i));
708 
709       Entry<Range<Integer>, Integer> entry = test.getEntry(i);
710       assertEquals(model.containsKey(i), entry != null);
711       if (entry != null) {
712         assertTrue(test.asMapOfRanges().entrySet().contains(entry));
713       }
714     }
715     for (Range<Integer> range : test.asMapOfRanges().keySet()) {
716       assertFalse(range.isEmpty());
717     }
718   }
719 
putModel(Map<Integer, Integer> model, Range<Integer> range, int value)720   private static void putModel(Map<Integer, Integer> model, Range<Integer> range, int value) {
721     for (int i = MIN_BOUND - 1; i <= MAX_BOUND + 1; i++) {
722       if (range.contains(i)) {
723         model.put(i, value);
724       }
725     }
726   }
727 
removeModel(Map<Integer, Integer> model, Range<Integer> range)728   private static void removeModel(Map<Integer, Integer> model, Range<Integer> range) {
729     for (int i = MIN_BOUND - 1; i <= MAX_BOUND + 1; i++) {
730       if (range.contains(i)) {
731         model.remove(i);
732       }
733     }
734   }
735 }
736