1 /* 2 * Copyright (c) 2012-2019 The ANTLR Project. All rights reserved. 3 * Use of this file is governed by the BSD 3-clause license that 4 * can be found in the LICENSE.txt file in the project root. 5 */ 6 7 module antlr.v4.runtime.misc.IntervalSet; 8 9 import antlr.v4.runtime.TokenConstantDefinition; 10 import antlr.v4.runtime.Vocabulary; 11 import antlr.v4.runtime.misc; 12 import std.algorithm; 13 import std.array; 14 import std.container.rbtree; 15 import std.conv; 16 import std.stdio; 17 18 /** 19 * This class implements the {@link IntSet} backed by a sorted array of 20 * non-overlapping intervals. It is particularly efficient for representing 21 * large collections of numbers, where the majority of elements appear as part 22 * of a sequential range of numbers that are all part of the set. For example, 23 * the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }. 24 * 25 * <p> 26 * This class is able to represent sets containing any combination of values in 27 * the range {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE} 28 * (inclusive).</p> 29 */ 30 class IntervalSet : IntSet 31 { 32 33 public static IntervalSet COMPLETE_CHAR_SET; 34 35 public static IntervalSet EMPTY_SET; 36 37 private bool readonly; 38 39 /** 40 * The list of sorted, disjoint intervals. 41 */ 42 private Interval[] intervals_; 43 44 public this() 45 { 46 COMPLETE_CHAR_SET = IntervalSet.of(char.min, char.min); 47 COMPLETE_CHAR_SET.setReadonly(true); 48 EMPTY_SET = new IntervalSet(1); 49 EMPTY_SET.clear; 50 EMPTY_SET.setReadonly(true); 51 } 52 53 public this(Interval[] intervals) 54 { 55 this.intervals_ = intervals; 56 } 57 58 public this(IntervalSet set) 59 { 60 this(); 61 addAll(set); 62 } 63 64 public this(int[] els...) 65 { 66 foreach (int e; els) 67 add(e); 68 } 69 70 /** 71 * Create a set with a single element, el. 72 */ 73 public static IntervalSet of(int a) 74 { 75 IntervalSet s = new IntervalSet(); 76 s.add(a); 77 return s; 78 } 79 80 /** 81 * Create a set with all ints within range [a..b] (inclusive) 82 */ 83 public static IntervalSet of(int a, int b) 84 { 85 IntervalSet s = new IntervalSet(1); 86 s.clear; 87 s.add(a, b); 88 return s; 89 } 90 91 public void clear() 92 { 93 assert(!readonly, "can't alter readonly IntervalSet"); 94 intervals_.length = 0; 95 } 96 97 /** 98 * Add a single element to the set. An isolated element is stored 99 * as a range el..el. 100 * @uml 101 * @override 102 */ 103 public override void add(int el) 104 { 105 assert(!readonly, "can't alter readonly IntervalSet"); 106 add(el, el); 107 } 108 109 /** 110 * Add interval; i.e., add all integers from a to b to set. 111 * If b<a, do nothing. 112 * Keep list in sorted order (by left range value). 113 * If overlap, combine ranges. For example, 114 * If this is {1..5, 10..20}, adding 6..7 yields 115 * {1..5, 6..7, 10..20}. Adding 4..8 yields {1..8, 10..20}. 116 */ 117 public void add(int a, int b) 118 { 119 assert(!readonly, "can't alter readonly IntervalSet"); 120 add(Interval.of(a, b)); 121 } 122 123 /** 124 * copy on write so we can cache a..a intervals and sets of that 125 */ 126 protected void add(Interval addition) 127 { 128 assert(!readonly, "can't alter readonly IntervalSet"); 129 debug (IntervalSet) 130 writefln("add %1$s to %2$s", addition, intervals_); 131 if (addition.b < addition.a) 132 { 133 return; 134 } 135 // find position in list 136 // Use iterators as we modify list in place 137 foreach (index, ref el; intervals_) 138 { 139 Interval r = el; 140 if (addition.equals(r)) 141 { 142 return; 143 } 144 if (addition.adjacent(r) || !addition.disjoint(r)) 145 { 146 // next to each other, make a single larger interval 147 Interval bigger = addition.unionInterval(r); 148 el = bigger; 149 // make sure we didn't just create an interval that 150 // should be merged with next interval in list 151 while (++index < intervals_.length) 152 { 153 Interval next = intervals_[index]; 154 if (!bigger.adjacent(next) && bigger.disjoint(next)) 155 { 156 continue; 157 } 158 // if we bump up against or overlap next, merge 159 intervals_ = intervals_.remove(index); 160 // move backwards to what we just set 161 intervals_[--index] = bigger.unionInterval(next); // set to 3 merged ones 162 } 163 return; 164 } 165 166 if (addition.startsBeforeDisjoint(r)) 167 { 168 // insert before r 169 intervals_ = intervals_[0 .. index] ~ addition ~ intervals_[index .. $]; 170 return; 171 } 172 // if disjoint and after r, a future iteration will handle it 173 } 174 // ok, must be after last interval (and disjoint from last interval) 175 // just add it 176 intervals_ ~= addition; 177 } 178 179 public IntervalSet addAll(IntSet set) 180 { 181 if (set is null) 182 { 183 return this; 184 } 185 if (typeid(typeof(set)) == typeid(IntervalSet*)) 186 { 187 IntervalSet other = cast(IntervalSet) set; 188 // walk set and add each interval 189 auto n = other.intervals_.length; 190 for (auto i = 0; i < n; i++) 191 { 192 Interval I = other.intervals_[i]; 193 this.add(I.a, I.b); 194 } 195 } 196 else 197 { 198 foreach (int value; set.toList) 199 { 200 add(value); 201 } 202 } 203 return this; 204 } 205 206 public IntervalSet complement(int minElement, int maxElement) 207 { 208 return this.complement(IntervalSet.of(minElement, maxElement)); 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 public IntervalSet complement(IntSet vocabulary) 215 { 216 if (vocabulary is null || vocabulary.isNil) 217 { 218 return null; // nothing in common with null set 219 } 220 IntervalSet vocabularyIS = new IntervalSet(); 221 vocabularyIS.addAll(vocabulary); 222 return vocabularyIS.subtract(this); 223 } 224 225 public IntervalSet complement(IntervalSet vocabulary) 226 { 227 if (vocabulary is null || vocabulary.isNil) 228 { 229 return null; // nothing in common with null set 230 } 231 IntervalSet vocabularyIS = vocabulary; 232 return vocabularyIS.subtract(this); 233 } 234 235 public IntervalSet subtract(IntSet a) 236 { 237 if (!a) 238 { 239 return new IntervalSet(this); 240 } 241 if (cast(IntervalSet) a) 242 { 243 return subtract(this, cast(IntervalSet) a); 244 } 245 246 IntervalSet other = new IntervalSet; 247 other.addAll(a); 248 return subtract(this, other); 249 } 250 251 public IntervalSet or(IntSet a) 252 { 253 IntervalSet o = new IntervalSet(); 254 o.addAll(this); 255 o.addAll(a); 256 return o; 257 } 258 259 public IntervalSet and(IntSet other) 260 { 261 if (other is null) 262 { //|| !(other instanceof IntervalSet) ) { 263 return null; // nothing in common with null set 264 } 265 266 auto myIntervals = this.intervals_; 267 auto theirIntervals = (cast(IntervalSet) other).intervals_; 268 IntervalSet intersection; 269 auto mySize = myIntervals.length; 270 auto theirSize = theirIntervals.length; 271 int i = 0; 272 int j = 0; 273 // iterate down both interval lists looking for nondisjoint intervals 274 while (i < mySize && j < theirSize) 275 { 276 Interval mine = myIntervals[i]; 277 Interval theirs = theirIntervals[j]; 278 //System.out.println("mine="+mine+" and theirs="+theirs); 279 if (mine.startsBeforeDisjoint(theirs)) 280 { 281 // move this iterator looking for interval that might overlap 282 i++; 283 } 284 else if (theirs.startsBeforeDisjoint(mine)) 285 { 286 // move other iterator looking for interval that might overlap 287 j++; 288 } 289 else if (mine.properlyContains(theirs)) 290 { 291 // overlap, add intersection, get next theirs 292 if (intersection is null) 293 { 294 intersection = new IntervalSet(); 295 } 296 intersection.add(mine.intersection(theirs)); 297 j++; 298 } 299 else if (theirs.properlyContains(mine)) 300 { 301 // overlap, add intersection, get next mine 302 if (intersection is null) 303 { 304 intersection = new IntervalSet(); 305 } 306 intersection.add(mine.intersection(theirs)); 307 i++; 308 } 309 else if (!mine.disjoint(theirs)) 310 { 311 // overlap, add intersection 312 if (intersection is null) 313 { 314 intersection = new IntervalSet(); 315 } 316 intersection.add(mine.intersection(theirs)); 317 // Move the iterator of lower range [a..b], but not 318 // the upper range as it may contain elements that will collide 319 // with the next iterator. So, if mine=[0..115] and 320 // theirs=[115..200], then intersection is 115 and move mine 321 // but not theirs as theirs may collide with the next range 322 // in thisIter. 323 // move both iterators to next ranges 324 if (mine.startsAfterNonDisjoint(theirs)) 325 { 326 j++; 327 } 328 else if (theirs.startsAfterNonDisjoint(mine)) 329 { 330 i++; 331 } 332 } 333 } 334 if (intersection is null) 335 { 336 return new IntervalSet(); 337 } 338 return intersection; 339 } 340 341 public bool contains(int el) 342 { 343 foreach (I; intervals_) 344 { 345 int a = I.a; 346 int b = I.b; 347 if (el < a) 348 { 349 break; // list is sorted and el is before this interval; not here 350 } 351 if (el >= a && el <= b) 352 { 353 return true; // found in this interval 354 } 355 } 356 return false; 357 } 358 359 public bool isNil() 360 { 361 return intervals_ is null || intervals_.length == 0; 362 } 363 364 public int getSingleElement() 365 { 366 if (intervals_ !is null && intervals_.length == 1) 367 { 368 Interval I = intervals_[0]; 369 if (I.a == I.b) 370 { 371 return I.a; 372 } 373 } 374 return TokenConstantDefinition.INVALID_TYPE; 375 } 376 377 /** 378 * Returns the maximum value contained in the set. 379 * 380 * @return the maximum value contained in the set. If the set is empty, this 381 * method returns {@link Token#INVALID_TYPE}. 382 */ 383 public int getMaxElement() 384 { 385 if (isNil) 386 { 387 return TokenConstantDefinition.INVALID_TYPE; 388 } 389 Interval last = intervals_[$ - 1]; 390 return last.b; 391 } 392 393 /** 394 * Returns the minimum value contained in the set. 395 * 396 * @return the minimum value contained in the set. If the set is empty, this 397 * method returns {@link Token#INVALID_TYPE}. 398 */ 399 public int getMinElement() 400 { 401 if (isNil) 402 { 403 return TokenConstantDefinition.INVALID_TYPE; 404 } 405 return intervals_[0].a; 406 } 407 408 /** 409 * combine all sets in the array returned the or'd value 410 */ 411 public static IntervalSet or(IntervalSet[] sets) 412 { 413 IntervalSet r = new IntervalSet(1); 414 r.clear; 415 foreach (IntervalSet s; sets) 416 { 417 r.addAll(s); 418 } 419 return r; 420 } 421 422 /** 423 * Compute the set difference between two interval sets. The specific 424 * operation is {@code left - right}. If either of the input sets is 425 * {@code null}, it is treated as though it was an empty set. 426 */ 427 public IntervalSet subtract(IntervalSet left, IntervalSet right) 428 { 429 if (left is null || left.size == 0) 430 { 431 return new IntervalSet(); 432 } 433 IntervalSet result = new IntervalSet(left); 434 if (right is null || right.isNil) 435 { 436 // right set has no elements; just return the copy of the current set 437 return result; 438 } 439 440 int resultI = 0; 441 int rightI = 0; 442 while (resultI < result.intervals_.length && rightI < right.intervals_.length) 443 { 444 Interval resultInterval = result.intervals_[resultI]; 445 Interval rightInterval = right.intervals_[rightI]; 446 447 // operation: (resultInterval - rightInterval) and update indexes 448 449 if (rightInterval.b < resultInterval.a) 450 { 451 rightI++; 452 continue; 453 } 454 455 if (rightInterval.a > resultInterval.b) 456 { 457 resultI++; 458 continue; 459 } 460 461 Interval beforeCurrent = null; 462 Interval afterCurrent = null; 463 if (rightInterval.a > resultInterval.a) 464 { 465 beforeCurrent = new Interval(resultInterval.a, rightInterval.a - 1); 466 } 467 468 if (rightInterval.b < resultInterval.b) 469 { 470 afterCurrent = new Interval(rightInterval.b + 1, resultInterval.b); 471 } 472 473 if (beforeCurrent !is null) 474 { 475 if (afterCurrent !is null) 476 { 477 // split the current interval into two 478 result.intervals_[resultI] = beforeCurrent; 479 result.intervals_ ~= afterCurrent; 480 resultI++; 481 rightI++; 482 continue; 483 } 484 else 485 { 486 // replace the current interval 487 result.intervals_[resultI] = beforeCurrent; 488 resultI++; 489 continue; 490 } 491 } 492 else 493 { 494 if (afterCurrent !is null) 495 { 496 // replace the current interval 497 result.intervals_[resultI] = afterCurrent; 498 rightI++; 499 continue; 500 } 501 else 502 { 503 // remove the current interval (thus no need to increment resultI) 504 //result.intervals.remove(resultI); 505 result.intervals_ = result.intervals_[0 .. resultI].dup 506 ~ result.intervals_[resultI + 1 .. $].dup; 507 continue; 508 } 509 } 510 } 511 // If rightI reached right.intervals.size(), no more intervals to subtract from result. 512 // If resultI reached result.intervals.size(), we would be subtracting from an empty set. 513 // Either way, we are done. 514 return result; 515 } 516 517 public string elementName(Vocabulary vocabulary, int a) 518 { 519 if (a == TokenConstantDefinition.EOF) 520 { 521 return "<EOF>"; 522 } 523 else if (a == TokenConstantDefinition.EPSILON) 524 { 525 return "<EPSILON>"; 526 } 527 else 528 { 529 return vocabulary.getDisplayName(a); 530 } 531 } 532 533 public int size() 534 { 535 int n = 0; 536 auto numIntervals = intervals_.length; 537 if (numIntervals == 1) 538 { 539 Interval firstInterval = intervals_[0]; 540 return firstInterval.b - firstInterval.a + 1; 541 } 542 for (auto i = 0; i < numIntervals; i++) 543 { 544 Interval I = intervals_[i]; 545 n += (I.b - I.a + 1); 546 } 547 return n; 548 } 549 550 public IntegerList toIntegerList() 551 { 552 IntegerList values = new IntegerList(); 553 auto n = intervals_.length; 554 for (auto i = 0; i < n; i++) 555 { 556 Interval I = intervals_[i]; 557 int a = I.a; 558 int b = I.b; 559 for (int v = a; v <= b; v++) 560 { 561 values.add(v); 562 } 563 } 564 return values; 565 } 566 567 public int[] toList() 568 { 569 int[] values; 570 auto n = intervals_.length; 571 for (auto i = 0; i < n; i++) 572 { 573 Interval I = intervals_[i]; 574 int a = I.a; 575 int b = I.b; 576 for (int v = a; v <= b; v++) 577 { 578 values ~= v; 579 } 580 } 581 return values; 582 } 583 584 public RedBlackTree!int toSet() 585 { 586 auto s = redBlackTree!int(); 587 foreach (Interval I; intervals_) 588 { 589 int a = I.a; 590 int b = I.b; 591 for (int v = a; v <= b; v++) 592 { 593 s.insert(v); 594 } 595 } 596 return s; 597 } 598 599 /** 600 * Get the ith element of ordered set. Used only by RandomPhrase so 601 * don't bother to implement if you're not doing that for a new 602 * ANTLR code gen target. 603 * @uml 604 * @safe 605 * @pure 606 */ 607 public int get(int i) @safe pure 608 { 609 auto n = intervals_.length; 610 ulong index = 0; 611 for (auto j = 0; j < n; j++) 612 { 613 Interval I = intervals_[j]; 614 int a = I.a; 615 int b = I.b; 616 for (int v = a; v <= b; v++) 617 { 618 if (to!int(index) == i) 619 { 620 return v; 621 } 622 index++; 623 } 624 } 625 return -1; 626 } 627 628 public int[] toArray() 629 { 630 return toIntegerList().toArray; 631 } 632 633 public void remove(int el) 634 { 635 assert(!readonly, "can't alter readonly IntervalSet"); 636 auto n = intervals_.length; 637 for (auto i = 0; i < n; i++) 638 { 639 Interval I = intervals_[i]; 640 int a = I.a; 641 int b = I.b; 642 if (el < a) 643 { 644 break; // list is sorted and el is before this interval; not here 645 } 646 // if whole interval x..x, remove i 647 if (el == a && el == b) 648 { 649 intervals_ = intervals_[0 .. i] ~ intervals_[i + 1 .. $]; 650 break; 651 } 652 // if on left edge x..b, adjust left 653 if (el == a) 654 { 655 I.a++; 656 break; 657 } 658 // if on right edge a..x, adjust right 659 if (el == b) 660 { 661 I.b--; 662 break; 663 } 664 // if in middle a..x..b, split interval 665 if (el > a && el < b) 666 { // found in this interval 667 int oldb = I.b; 668 I.b = el - 1; // [a..x-1] 669 add(el + 1, oldb); // add [x+1..b] 670 } 671 } 672 } 673 674 /** 675 * @uml 676 * @safe 677 * @pure 678 */ 679 public bool isReadonly() @safe pure 680 { 681 return readonly; 682 } 683 684 /** 685 * @uml 686 * @safe 687 * @pure 688 */ 689 public void setReadonly(bool readonly) @safe pure 690 { 691 assert(!this.readonly, "can't alter readonly IntervalSet"); 692 this.readonly = readonly; 693 } 694 695 /** 696 * @uml 697 * @override 698 */ 699 public override bool opEquals(Object obj) 700 { 701 IntervalSet other = cast(IntervalSet) obj; 702 return intervals_ == other.intervals_; 703 } 704 705 /** 706 * @uml 707 * @override 708 */ 709 public override string toString() 710 { 711 return toString(false); 712 } 713 714 public string toString(bool elemAreChar) 715 { 716 auto buf = appender!string; 717 if (intervals_ is null || intervals_.length == 0) 718 { 719 return "{}"; 720 } 721 if (this.size() > 1) 722 { 723 buf.put("{"); 724 } 725 foreach (index, I; this.intervals_) 726 { 727 int a = I.a; 728 int b = I.b; 729 if (a == b) 730 { 731 if (a == TokenConstantDefinition.EOF) 732 buf.put("<EOF>"); 733 else if (elemAreChar) 734 buf.put("'" ~ to!string(a) ~ "'"); 735 else 736 buf.put(to!string(a)); 737 } 738 else 739 { 740 if (elemAreChar) 741 buf.put("'" ~ to!string(a) ~ "'..'" ~ to!string(b) ~ "'"); 742 else 743 buf.put(to!string(a) ~ ".." ~ to!string(b)); 744 } 745 if (index + 1 < intervals_.length) 746 { 747 buf.put(", "); // not last element 748 } 749 } 750 if (this.size() > 1) 751 { 752 buf.put("}"); 753 } 754 return buf.data; 755 756 } 757 758 public string toString(Vocabulary vocabulary) 759 { 760 auto buf = appender!string; 761 if (intervals_ is null || intervals_.length == 0) 762 { 763 return "{}"; 764 } 765 if (size() > 1) 766 { 767 buf.put("{"); 768 } 769 foreach (index, I; this.intervals_) 770 { 771 int a = I.a; 772 int b = I.b; 773 if (a == b) 774 { 775 buf.put(elementName(vocabulary, a)); 776 } 777 else 778 { 779 for (int i = a; i <= b; i++) 780 { 781 if (i > a) 782 buf.put(", "); 783 buf.put(elementName(vocabulary, i)); 784 } 785 } 786 if (index + 1 < intervals_.length) 787 { 788 buf.put(", "); 789 } 790 } 791 if (size() > 1) 792 { 793 buf.put("}"); 794 } 795 return buf.data; 796 797 } 798 799 /** 800 * @uml 801 * @final 802 */ 803 public final Interval[] intervals() 804 { 805 return this.intervals_; 806 } 807 808 } 809 810 version (unittest) 811 { 812 import dshould : be, equal, not, should; 813 import std.typecons : tuple; 814 import unit_threaded; 815 816 class Test 817 { 818 819 @Tags("IntervalSet") 820 @("Empty") 821 unittest 822 { 823 IntervalSet s = new IntervalSet; 824 s.should.not.be(null); 825 s.intervals.length.should.equal(0); 826 s.isNil.should.equal(true); 827 } 828 829 @Tags("IntervalSet") 830 @("One") 831 unittest 832 { 833 IntervalSet s = new IntervalSet; 834 s.add(30); 835 s.intervals.length.should.equal(1); 836 s.isNil.should.equal(false); 837 s.contains(30).should.equal(true); 838 s.contains(29).should.equal(false); 839 s.contains(31).should.equal(false); 840 } 841 842 @Tags("IntervalSet") 843 @("Two") 844 unittest 845 { 846 IntervalSet s = new IntervalSet; 847 s.add(30); 848 s.add(40); 849 s.intervals.length.should.equal(2); 850 s.isNil.should.equal(false); 851 s.contains(30).should.equal(true); 852 s.contains(40).should.equal(true); 853 s.contains(35).should.equal(false); 854 } 855 856 @Tags("IntervalSet") 857 @("Range") 858 unittest 859 { 860 IntervalSet s = new IntervalSet; 861 s.add(30, 41); 862 s.intervals.length.should.equal(1); 863 s.isNil.should.equal(false); 864 s.contains(30).should.equal(true); 865 s.contains(40).should.equal(true); 866 s.contains(35).should.equal(true); 867 } 868 869 @Tags("IntervalSet") 870 @("Distinct1") 871 unittest 872 { 873 IntervalSet s = new IntervalSet; 874 s.add(30, 32); 875 s.add(40, 42); 876 s.intervals.length.should.equal(2); 877 s.isNil.should.equal(false); 878 s.contains(30).should.equal(true); 879 s.contains(40).should.equal(true); 880 s.contains(35).should.equal(false); 881 } 882 883 @Tags("IntervalSet") 884 @("Distinct2") 885 unittest 886 { 887 IntervalSet s = new IntervalSet; 888 s.add(40, 42); 889 s.add(30, 32); 890 s.intervals.length.should.equal(2); 891 s.isNil.should.equal(false); 892 s.contains(30).should.equal(true); 893 s.contains(40).should.equal(true); 894 s.contains(35).should.equal(false); 895 } 896 897 @Tags("IntervalSet") 898 @("Contiguous1") 899 unittest 900 { 901 IntervalSet s = new IntervalSet; 902 s.add(30, 36); 903 s.add(36, 41); 904 s.add(41, 44); 905 s.intervals.length.should.equal(1); 906 s.isNil.should.equal(false); 907 s.contains(30).should.equal(true); 908 s.contains(40).should.equal(true); 909 s.contains(43).should.equal(true); 910 s.contains(44).should.equal(true); 911 s.contains(45).should.equal(false); 912 } 913 914 @Tags("IntervalSet") 915 @("Contiguous2") 916 unittest 917 { 918 IntervalSet s = new IntervalSet; 919 s.add(41, 44); 920 s.add(36, 41); 921 s.add(30, 36); 922 s.intervals.length.should.equal(1); 923 s.isNil.should.equal(false); 924 s.contains(30).should.equal(true); 925 s.contains(40).should.equal(true); 926 s.contains(43).should.equal(true); 927 s.contains(44).should.equal(true); 928 s.contains(45).should.equal(false); 929 } 930 931 @Tags("IntervalSet") 932 @("Overlapping1") 933 unittest 934 { 935 IntervalSet s = new IntervalSet; 936 s.add(30, 40); 937 s.add(35, 44); 938 s.add(31, 36); 939 s.intervals.length.should.equal(1); 940 s.isNil.should.equal(false); 941 s.contains(30).should.equal(true); 942 s.contains(40).should.equal(true); 943 s.contains(43).should.equal(true); 944 s.contains(44).should.equal(true); 945 s.contains(45).should.equal(false); 946 } 947 948 @Tags("IntervalSet") 949 @("Overlapping2") 950 unittest 951 { 952 IntervalSet s = new IntervalSet; 953 s.add(35, 44); 954 s.add(31, 36); 955 s.add(30, 40); 956 s.intervals.length.should.equal(1); 957 s.isNil.should.equal(false); 958 s.contains(30).should.equal(true); 959 s.contains(40).should.equal(true); 960 s.contains(43).should.equal(true); 961 s.contains(44).should.equal(true); 962 s.contains(45).should.equal(false); 963 } 964 965 @Tags("IntervalSet") 966 @("Overlapping3") 967 unittest 968 { 969 IntervalSet s = new IntervalSet; 970 s.add(30, 32); 971 s.add(40, 42); 972 s.add(140, 144); 973 s.add(50, 52); 974 s.add(20, 61); 975 s.add(50, 52); 976 s.intervals.length.should.equal(2); 977 s.isNil.should.equal(false); 978 s.contains(20).should.equal(true); 979 s.contains(40).should.equal(true); 980 s.contains(43).should.equal(true); 981 s.contains(61).should.equal(true); 982 s.contains(4).should.equal(false); 983 s.contains(62).should.equal(false); 984 s.contains(139).should.equal(false); 985 s.contains(140).should.equal(true); 986 s.contains(144).should.equal(true); 987 s.contains(145).should.equal(false); 988 s.toString.should.equal("{20..61, 140..144}"); 989 } 990 991 @Tags("IntervalSet") 992 @("Complement") 993 unittest 994 { 995 IntervalSet s = new IntervalSet; 996 s.add(10, 21); 997 auto c = s.complement(1, 100); 998 c.intervals.length.should.equal(2); 999 c.toString.should.equal("{1..9, 22..100}"); 1000 c.contains(1).should.equal(true); 1001 c.contains(40).should.equal(true); 1002 c.contains(22).should.equal(true); 1003 c.contains(10).should.equal(false); 1004 c.contains(20).should.equal(false); 1005 } 1006 } 1007 }