1 /* 2 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft 3 Permission is hereby granted, free of charge, to any person obtaining a copy 4 of this software and associated documentation files (the "Software"), to deal 5 in the Software without restriction, including without limitation the rights 6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 copies of the Software, and to permit persons to whom the Software is 8 furnished to do so, subject to the following conditions: 9 10 The above copyright notice and this permission notice shall be included in 11 all copies or substantial portions of the Software. 12 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 SOFTWARE. 20 */ 21 22 #define LINEARPROBINGnot 23 #define REFBUCKET 24 #define SHRINKnot 25 #define INTERHASHINGnot 26 #define RANDOMINTERHASHING 27 28 using System; 29 using System.Diagnostics; 30 using SCG = System.Collections.Generic; 31 32 namespace C5 33 { 34 /// <summary> 35 /// A set collection class based on linear hashing 36 /// </summary> 37 [Serializable] 38 public class HashSet<T> : CollectionBase<T>, ICollection<T> 39 { 40 #region Feature 41 /// <summary> 42 /// Enum class to assist printing of compilation alternatives. 43 /// </summary> 44 [Flags] 45 public enum Feature : short 46 { 47 /// <summary> 48 /// Nothing 49 /// </summary> 50 Dummy = 0, 51 /// <summary> 52 /// Buckets are of reference type 53 /// </summary> 54 RefTypeBucket = 1, 55 /// <summary> 56 /// Primary buckets are of value type 57 /// </summary> 58 ValueTypeBucket = 2, 59 /// <summary> 60 /// Using linear probing to resolve index clashes 61 /// </summary> 62 LinearProbing = 4, 63 /// <summary> 64 /// Shrink table when very sparsely filled 65 /// </summary> 66 ShrinkTable = 8, 67 /// <summary> 68 /// Use chaining to resolve index clashes 69 /// </summary> 70 Chaining = 16, 71 /// <summary> 72 /// Use hash function on item hash code 73 /// </summary> 74 InterHashing = 32, 75 /// <summary> 76 /// Use a universal family of hash functions on item hash code 77 /// </summary> 78 RandomInterHashing = 64 79 } 80 81 82 83 static Feature features = Feature.Dummy 84 #if REFBUCKET 85 | Feature.RefTypeBucket 86 #else 87 | Feature.ValueTypeBucket 88 #endif 89 #if SHRINK 90 | Feature.ShrinkTable 91 #endif 92 #if LINEARPROBING 93 | Feature.LinearProbing 94 #else 95 | Feature.Chaining 96 #endif 97 #if INTERHASHING 98 | Feature.InterHashing 99 #elif RANDOMINTERHASHING 100 | Feature.RandomInterHashing 101 #endif 102 ; 103 104 105 /// <summary> 106 /// Show which implementation features was chosen at compilation time 107 /// </summary> 108 public static Feature Features { get { return features; } } 109 110 #endregion 111 112 #region Fields 113 114 int indexmask, bits, bitsc, origbits, lastchosen; //bitsc==32-bits; indexmask==(1<<bits)-1; 115 116 Bucket[] table; 117 118 #if !REFBUCKET 119 bool defaultvalid = false; 120 121 T defaultitem; 122 #endif 123 double fillfactor = 0.66; 124 125 int resizethreshhold; 126 127 #if RANDOMINTERHASHING 128 #if DEBUG 129 const uint randomhashfactor = 1529784659; 130 #else 131 private static readonly Random random = new Random(); 132 uint randomhashfactor = (2 * (uint)random.Next() + 1) * 1529784659; 133 #endif 134 #endif 135 136 #endregion 137 138 #region Events 139 140 /// <summary> 141 /// 142 /// </summary> 143 /// <value></value> 144 public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } } 145 146 #endregion 147 148 #region Bucket nested class(es) 149 #if REFBUCKET 150 [Serializable] 151 class Bucket 152 { 153 internal T item; 154 155 internal int hashval; //Cache! 156 157 #if LINEARPROBING Bucket(T item, int hashval)158 internal Bucket(T item, int hashval) 159 { 160 this.item = item; 161 this.hashval = hashval; 162 } 163 #else 164 internal Bucket overflow; 165 Bucket(T item, int hashval, Bucket overflow)166 internal Bucket(T item, int hashval, Bucket overflow) 167 { 168 this.item = item; 169 this.hashval = hashval; 170 this.overflow = overflow; 171 } 172 #endif 173 } 174 #else 175 struct Bucket 176 { 177 internal T item; 178 179 internal int hashval; //Cache! 180 181 #if LINEARPROBING BucketC5.HashSet.Bucket182 internal Bucket(T item, int hashval) 183 { 184 this.item = item; 185 this.hashval = hashval; 186 } 187 #else 188 internal OverflowBucket overflow; 189 190 BucketC5.HashSet.Bucket191 internal Bucket(T item, int hashval) 192 { 193 this.item = item; 194 this.hashval = hashval; 195 this.overflow = default(OverflowBucket); 196 } 197 #endif 198 } 199 200 201 #if !LINEARPROBING 202 class OverflowBucket 203 { 204 internal T item; 205 206 internal int hashval; //Cache! 207 208 internal OverflowBucket overflow; 209 210 OverflowBucket(T item, int hashval, OverflowBucket overflow)211 internal OverflowBucket(T item, int hashval, OverflowBucket overflow) 212 { 213 this.item = item; 214 this.hashval = hashval; 215 this.overflow = overflow; 216 } 217 } 218 #endif 219 #endif 220 221 #endregion 222 223 #region Basic Util 224 equals(T i1, T i2)225 bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); } 226 227 #if !REFBUCKET isnull(T item)228 bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); } 229 #endif 230 gethashcode(T item)231 int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); } 232 233 hv2i(int hashval)234 int hv2i(int hashval) 235 { 236 #if INTERHASHING 237 //Note: *inverse mod 2^32 is -1503427877 238 return (int)(((uint)hashval * 1529784659) >>bitsc); 239 #elif RANDOMINTERHASHING 240 return (int)(((uint)hashval * randomhashfactor) >> bitsc); 241 #else 242 return indexmask & hashval; 243 #endif 244 } 245 246 expand()247 void expand() 248 { 249 //Console.WriteLine(String.Format("Expand to {0} bits", bits+1)); 250 resize(bits + 1); 251 } 252 253 shrink()254 void shrink() 255 { 256 if (bits > 3) 257 { 258 //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1)); 259 resize(bits - 1); 260 } 261 } 262 263 resize(int bits)264 void resize(int bits) 265 { 266 //Console.WriteLine(String.Format("Resize to {0} bits", bits)); 267 this.bits = bits; 268 bitsc = 32 - bits; 269 indexmask = (1 << bits) - 1; 270 271 Bucket[] newtable = new Bucket[indexmask + 1]; 272 273 for (int i = 0, s = table.Length; i < s; i++) 274 { 275 Bucket b = table[i]; 276 277 #if LINEARPROBING 278 #if REFBUCKET 279 if (b != null) 280 { 281 int j = hv2i(b.hashval); 282 283 while (newtable[j] != null) { j = indexmask & (j + 1); } 284 285 newtable[j] = b; 286 } 287 #else 288 if (!isnull(b.item)) 289 { 290 int j = hv2i(b.hashval); 291 292 while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); } 293 294 newtable[j] = b; 295 } 296 #endif 297 #else 298 #if REFBUCKET 299 while (b != null) 300 { 301 int j = hv2i(b.hashval); 302 303 newtable[j] = new Bucket(b.item, b.hashval, newtable[j]); 304 b = b.overflow; 305 } 306 #else 307 if (!isnull(b.item)) 308 { 309 insert(b.item, b.hashval, newtable); 310 311 OverflowBucket ob = b.overflow; 312 313 while (ob != null) 314 { 315 insert(ob.item, ob.hashval, newtable); 316 ob = ob.overflow; 317 } 318 } 319 #endif 320 #endif 321 } 322 323 table = newtable; 324 resizethreshhold = (int)(table.Length * fillfactor); 325 //Console.WriteLine(String.Format("Resize to {0} bits done", bits)); 326 } 327 328 #if REFBUCKET 329 #else 330 #if LINEARPROBING 331 #else 332 //Only for resize!!! insert(T item, int hashval, Bucket[] t)333 private void insert(T item, int hashval, Bucket[] t) 334 { 335 int i = hv2i(hashval); 336 Bucket b = t[i]; 337 338 if (!isnull(b.item)) 339 { 340 t[i].overflow = new OverflowBucket(item, hashval, b.overflow); 341 } 342 else 343 t[i] = new Bucket(item, hashval); 344 } 345 #endif 346 #endif 347 348 /// <summary> 349 /// Search for an item equal (according to itemequalityComparer) to the supplied item. 350 /// </summary> 351 /// <param name="item"></param> 352 /// <param name="add">If true, add item to table if not found.</param> 353 /// <param name="update">If true, update table entry if item found.</param> 354 /// <param name="raise">If true raise events</param> 355 /// <returns>True if found</returns> searchoradd(ref T item, bool add, bool update, bool raise)356 private bool searchoradd(ref T item, bool add, bool update, bool raise) 357 { 358 359 #if LINEARPROBING 360 #if REFBUCKET 361 int hashval = gethashcode(item); 362 int i = hv2i(hashval); 363 Bucket b = table[i]; 364 365 while (b != null) 366 { 367 T olditem = b.item; 368 if (equals(olditem, item)) 369 { 370 if (update) 371 b.item = item; 372 else 373 item = olditem; 374 375 if (raise && update) 376 raiseForUpdate(item, olditem); 377 return true; 378 } 379 380 b = table[i = indexmask & (i + 1)]; 381 } 382 383 if (!add) goto notfound; 384 385 table[i] = new Bucket(item, hashval); 386 387 #else 388 if (isnull(item)) 389 { 390 if (defaultvalid) 391 { 392 T olditem = defaultitem; 393 if (update) 394 defaultitem = item; 395 else 396 item = defaultitem; 397 398 if (raise && update) 399 raiseForUpdate(item, olditem); 400 return true; 401 } 402 403 if (!add) goto notfound; 404 405 defaultvalid = true; 406 defaultitem = item; 407 } 408 else 409 { 410 int hashval = gethashcode(item); 411 int i = hv2i(hashval); 412 T t = table[i].item; 413 414 while (!isnull(t)) 415 { 416 if (equals(t, item)) 417 { 418 if (update) 419 table[i].item = item; 420 else 421 item = t; 422 423 if (raise && update) 424 raiseForUpdate(item, t); 425 return true; 426 } 427 428 t = table[i = indexmask & (i + 1)].item; 429 } 430 431 if (!add) goto notfound; 432 433 table[i] = new Bucket(item, hashval); 434 } 435 #endif 436 #else 437 #if REFBUCKET 438 int hashval = gethashcode(item); 439 int i = hv2i(hashval); 440 Bucket b = table[i], bold = null; 441 442 if (b != null) 443 { 444 while (b != null) 445 { 446 T olditem = b.item; 447 if (equals(olditem, item)) 448 { 449 if (update) 450 { 451 b.item = item; 452 } 453 if (raise && update) 454 raiseForUpdate(item, olditem); 455 // bug20071112: 456 item = olditem; 457 return true; 458 } 459 460 bold = b; 461 b = b.overflow; 462 } 463 464 if (!add) goto notfound; 465 466 bold.overflow = new Bucket(item, hashval, null); 467 } 468 else 469 { 470 if (!add) goto notfound; 471 472 table[i] = new Bucket(item, hashval, null); 473 } 474 #else 475 if (isnull(item)) 476 { 477 if (defaultvalid) 478 { 479 T olditem = defaultitem; 480 if (update) 481 defaultitem = item; 482 else 483 item = defaultitem; 484 485 if (raise && update) 486 raiseForUpdate(item, olditem); 487 return true; 488 } 489 490 if (!add) goto notfound; 491 492 defaultvalid = true; 493 defaultitem = item; 494 } 495 else 496 { 497 int hashval = gethashcode(item); 498 int i = hv2i(hashval); 499 Bucket b = table[i]; 500 501 if (!isnull(b.item)) 502 { 503 if (equals(b.item, item)) 504 { 505 if (update) 506 table[i].item = item; 507 else 508 item = b.item; 509 510 if (raise && update) 511 raiseForUpdate(item, b.item); 512 return true; 513 } 514 515 OverflowBucket ob = table[i].overflow; 516 517 if (ob == null) 518 { 519 if (!add) goto notfound; 520 521 table[i].overflow = new OverflowBucket(item, hashval, null); 522 } 523 else 524 { 525 T olditem = ob.item; 526 while (ob.overflow != null) 527 { 528 if (equals(item, olditem)) 529 { 530 if (update) 531 ob.item = item; 532 else 533 item = olditem; 534 535 if (raise && update) 536 raiseForUpdate(item, olditem); 537 return true; 538 } 539 540 ob = ob.overflow; 541 olditem = ob.item; 542 } 543 544 if (equals(item, olditem)) 545 { 546 if (update) 547 ob.item = item; 548 else 549 item = olditem; 550 551 if (raise && update) 552 raiseForUpdate(item, olditem); 553 return true; 554 } 555 556 if (!add) goto notfound; 557 558 ob.overflow = new OverflowBucket(item, hashval, null); 559 } 560 } 561 else 562 { 563 if (!add) goto notfound; 564 565 table[i] = new Bucket(item, hashval); 566 } 567 } 568 #endif 569 #endif 570 size++; 571 if (size > resizethreshhold) 572 expand(); 573 notfound: 574 if (raise && add) 575 raiseForAdd(item); 576 if (update) 577 item = default(T); 578 return false; 579 } 580 581 remove(ref T item)582 private bool remove(ref T item) 583 { 584 585 if (size == 0) 586 return false; 587 #if LINEARPROBING 588 #if REFBUCKET 589 int hashval = gethashcode(item); 590 int index = hv2i(hashval); 591 Bucket b = table[index]; 592 593 while (b != null) 594 { 595 if (equals(item, b.item)) 596 { 597 //ref 598 item = table[index].item; 599 table[index] = null; 600 601 //Algorithm R 602 int j = (index + 1) & indexmask; 603 604 b = table[j]; 605 while (b != null) 606 { 607 int k = hv2i(b.hashval); 608 609 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index)) 610 //if (index > j ? (j < k && k <= index): (k <= index || j < k) ) 611 { 612 table[index] = b; 613 table[j] = null; 614 index = j; 615 } 616 617 j = (j + 1) & indexmask; 618 b = table[j]; 619 } 620 621 goto found; 622 } 623 624 b = table[index = indexmask & (index + 1)]; 625 } 626 return false; 627 #else 628 if (isnull(item)) 629 { 630 if (!defaultvalid) 631 return false; 632 633 //ref 634 item = defaultitem; 635 defaultvalid = false; 636 defaultitem = default(T); //No spaceleaks! 637 } 638 else 639 { 640 int hashval = gethashcode(item); 641 int index = hv2i(hashval); 642 T t = table[index].item; 643 644 while (!isnull(t)) 645 { 646 if (equals(item, t)) 647 { 648 //ref 649 item = table[index].item; 650 table[index].item = default(T); 651 652 //algorithm R 653 int j = (index + 1) & indexmask; 654 Bucket b = table[j]; 655 656 while (!isnull(b.item)) 657 { 658 int k = hv2i(b.hashval); 659 660 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index)) 661 { 662 table[index] = b; 663 table[j].item = default(T); 664 index = j; 665 } 666 667 j = (j + 1) & indexmask; 668 b = table[j]; 669 } 670 671 goto found; 672 } 673 674 t = table[index = indexmask & (index + 1)].item; 675 } 676 677 return false; 678 } 679 #endif 680 found: 681 #else 682 #if REFBUCKET 683 int hashval = gethashcode(item); 684 int index = hv2i(hashval); 685 Bucket b = table[index], bold; 686 687 if (b == null) 688 return false; 689 690 if (equals(item, b.item)) 691 { 692 //ref 693 item = b.item; 694 table[index] = b.overflow; 695 } 696 else 697 { 698 bold = b; 699 b = b.overflow; 700 while (b != null && !equals(item, b.item)) 701 { 702 bold = b; 703 b = b.overflow; 704 } 705 706 if (b == null) 707 return false; 708 709 //ref 710 item = b.item; 711 bold.overflow = b.overflow; 712 } 713 714 #else 715 if (isnull(item)) 716 { 717 if (!defaultvalid) 718 return false; 719 720 //ref 721 item = defaultitem; 722 defaultvalid = false; 723 defaultitem = default(T); //No spaceleaks! 724 } 725 else 726 { 727 int hashval = gethashcode(item); 728 int index = hv2i(hashval); 729 Bucket b = table[index]; 730 OverflowBucket ob = b.overflow; 731 732 if (equals(item, b.item)) 733 { 734 //ref 735 item = b.item; 736 if (ob == null) 737 { 738 table[index] = new Bucket(); 739 } 740 else 741 { 742 b = new Bucket(ob.item, ob.hashval); 743 b.overflow = ob.overflow; 744 table[index] = b; 745 } 746 } 747 else 748 { 749 if (ob == null) 750 return false; 751 752 if (equals(item, ob.item)) 753 { 754 //ref 755 item=ob.item; 756 table[index].overflow = ob.overflow; 757 } 758 else 759 { 760 while (ob.overflow != null) 761 if (equals(item, ob.overflow.item)) 762 { 763 //ref 764 item = ob.overflow.item; 765 break; 766 } 767 else 768 ob = ob.overflow; 769 770 if (ob.overflow == null) 771 return false; 772 773 ob.overflow = ob.overflow.overflow; 774 } 775 } 776 } 777 #endif 778 #endif 779 size--; 780 781 return true; 782 } 783 784 clear()785 private void clear() 786 { 787 bits = origbits; 788 bitsc = 32 - bits; 789 indexmask = (1 << bits) - 1; 790 size = 0; 791 table = new Bucket[indexmask + 1]; 792 resizethreshhold = (int)(table.Length * fillfactor); 793 #if !REFBUCKET 794 defaultitem = default(T); 795 defaultvalid = false; 796 #endif 797 } 798 799 #endregion 800 801 #region Constructors 802 /// <summary> 803 /// Create a hash set with natural item equalityComparer and default fill threshold (66%) 804 /// and initial table size (16). 805 /// </summary> HashSet()806 public HashSet() 807 : this(EqualityComparer<T>.Default) { } 808 809 810 /// <summary> 811 /// Create a hash set with external item equalityComparer and default fill threshold (66%) 812 /// and initial table size (16). 813 /// </summary> 814 /// <param name="itemequalityComparer">The external item equalityComparer</param> HashSet(SCG.IEqualityComparer<T> itemequalityComparer)815 public HashSet(SCG.IEqualityComparer<T> itemequalityComparer) 816 : this(16, itemequalityComparer) { } 817 818 819 /// <summary> 820 /// Create a hash set with external item equalityComparer and default fill threshold (66%) 821 /// </summary> 822 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> 823 /// <param name="itemequalityComparer">The external item equalityComparer</param> HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)824 public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer) 825 : this(capacity, 0.66, itemequalityComparer) { } 826 827 828 /// <summary> 829 /// Create a hash set with external item equalityComparer. 830 /// </summary> 831 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> 832 /// <param name="fill">Fill threshold (in range 10% to 90%)</param> 833 /// <param name="itemequalityComparer">The external item equalityComparer</param> HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer)834 public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer) 835 : base(itemequalityComparer) 836 { 837 if (fill < 0.1 || fill > 0.9) 838 throw new ArgumentException("Fill outside valid range [0.1, 0.9]"); 839 if (capacity <= 0) 840 throw new ArgumentException("Capacity must be non-negative"); 841 //this.itemequalityComparer = itemequalityComparer; 842 origbits = 4; 843 while (capacity - 1 >> origbits > 0) origbits++; 844 clear(); 845 } 846 847 848 849 #endregion 850 851 #region IEditableCollection<T> Members 852 853 /// <summary> 854 /// The complexity of the Contains operation 855 /// </summary> 856 /// <value>Always returns Speed.Constant</value> 857 [Tested] 858 public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } } 859 860 /// <summary> 861 /// Check if an item is in the set 862 /// </summary> 863 /// <param name="item">The item to look for</param> 864 /// <returns>True if set contains item</returns> 865 [Tested] Contains(T item)866 public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); } 867 868 869 /// <summary> 870 /// Check if an item (collection equal to a given one) is in the set and 871 /// if so report the actual item object found. 872 /// </summary> 873 /// <param name="item">On entry, the item to look for. 874 /// On exit the item found, if any</param> 875 /// <returns>True if set contains item</returns> 876 [Tested] Find(ref T item)877 public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); } 878 879 880 /// <summary> 881 /// Check if an item (collection equal to a given one) is in the set and 882 /// if so replace the item object in the set with the supplied one. 883 /// </summary> 884 /// <param name="item">The item object to update with</param> 885 /// <returns>True if item was found (and updated)</returns> 886 [Tested] Update(T item)887 public virtual bool Update(T item) 888 { updatecheck(); return searchoradd(ref item, false, true, true); } 889 890 /// <summary> 891 /// Check if an item (collection equal to a given one) is in the set and 892 /// if so replace the item object in the set with the supplied one. 893 /// </summary> 894 /// <param name="item">The item object to update with</param> 895 /// <param name="olditem"></param> 896 /// <returns>True if item was found (and updated)</returns> Update(T item, out T olditem)897 public virtual bool Update(T item, out T olditem) 898 { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); } 899 900 901 /// <summary> 902 /// Check if an item (collection equal to a given one) is in the set. 903 /// If found, report the actual item object in the set, 904 /// else add the supplied one. 905 /// </summary> 906 /// <param name="item">On entry, the item to look for or add. 907 /// On exit the actual object found, if any.</param> 908 /// <returns>True if item was found</returns> 909 [Tested] FindOrAdd(ref T item)910 public virtual bool FindOrAdd(ref T item) 911 { updatecheck(); return searchoradd(ref item, true, false, true); } 912 913 914 /// <summary> 915 /// Check if an item (collection equal to a supplied one) is in the set and 916 /// if so replace the item object in the set with the supplied one; else 917 /// add the supplied one. 918 /// </summary> 919 /// <param name="item">The item to look for and update or add</param> 920 /// <returns>True if item was updated</returns> 921 [Tested] UpdateOrAdd(T item)922 public virtual bool UpdateOrAdd(T item) 923 { updatecheck(); return searchoradd(ref item, true, true, true); } 924 925 926 /// <summary> 927 /// Check if an item (collection equal to a supplied one) is in the set and 928 /// if so replace the item object in the set with the supplied one; else 929 /// add the supplied one. 930 /// </summary> 931 /// <param name="item">The item to look for and update or add</param> 932 /// <param name="olditem"></param> 933 /// <returns>True if item was updated</returns> UpdateOrAdd(T item, out T olditem)934 public virtual bool UpdateOrAdd(T item, out T olditem) 935 { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); } 936 937 938 /// <summary> 939 /// Remove an item from the set 940 /// </summary> 941 /// <param name="item">The item to remove</param> 942 /// <returns>True if item was (found and) removed </returns> 943 [Tested] Remove(T item)944 public virtual bool Remove(T item) 945 { 946 updatecheck(); 947 if (remove(ref item)) 948 { 949 #if SHRINK 950 if (size<resizethreshhold/2 && resizethreshhold>8) 951 shrink(); 952 #endif 953 raiseForRemove(item); 954 return true; 955 } 956 else 957 return false; 958 } 959 960 961 /// <summary> 962 /// Remove an item from the set, reporting the actual matching item object. 963 /// </summary> 964 /// <param name="item">The value to remove.</param> 965 /// <param name="removeditem">The removed value.</param> 966 /// <returns>True if item was found.</returns> 967 [Tested] Remove(T item, out T removeditem)968 public virtual bool Remove(T item, out T removeditem) 969 { 970 updatecheck(); 971 removeditem = item; 972 if (remove(ref removeditem)) 973 { 974 #if SHRINK 975 if (size<resizethreshhold/2 && resizethreshhold>8) 976 shrink(); 977 #endif 978 raiseForRemove(removeditem); 979 return true; 980 } 981 else 982 return false; 983 } 984 985 986 /// <summary> 987 /// Remove all items in a supplied collection from this set. 988 /// </summary> 989 /// <typeparam name="U"></typeparam> 990 /// <param name="items">The items to remove.</param> 991 [Tested] 992 public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T 993 { 994 updatecheck(); 995 RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this); 996 bool raise = raiseHandler.MustFire; 997 T jtem; 998 foreach (U item in items) 999 { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); } 1000 #if SHRINK 1001 if (size < resizethreshhold / 2 && resizethreshhold > 16) 1002 { 1003 int newlength = table.Length; 1004 1005 while (newlength >= 32 && newlength * fillfactor / 2 > size) 1006 newlength /= 2; 1007 1008 resize(newlength - 1); 1009 } 1010 #endif 1011 if (raise) raiseHandler.Raise(); 1012 } 1013 1014 /// <summary> 1015 /// Remove all items from the set, resetting internal table to initial size. 1016 /// </summary> 1017 [Tested] Clear()1018 public virtual void Clear() 1019 { 1020 updatecheck(); 1021 int oldsize = size; 1022 clear(); 1023 if (ActiveEvents != 0 && oldsize > 0) 1024 { 1025 raiseCollectionCleared(true, oldsize); 1026 raiseCollectionChanged(); 1027 } 1028 } 1029 1030 1031 /// <summary> 1032 /// Remove all items *not* in a supplied collection from this set. 1033 /// </summary> 1034 /// <typeparam name="U"></typeparam> 1035 /// <param name="items">The items to retain</param> 1036 [Tested] 1037 public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T 1038 { 1039 updatecheck(); 1040 1041 HashSet<T> aux = new HashSet<T>(EqualityComparer); 1042 1043 //This only works for sets: 1044 foreach (U item in items) 1045 if (Contains(item)) 1046 { 1047 T jtem = item; 1048 1049 aux.searchoradd(ref jtem, true, false, false); 1050 } 1051 1052 if (size == aux.size) 1053 return; 1054 1055 CircularQueue<T> wasRemoved = null; 1056 if ((ActiveEvents & EventTypeEnum.Removed) != 0) 1057 { 1058 wasRemoved = new CircularQueue<T>(); 1059 foreach (T item in this) 1060 if (!aux.Contains(item)) 1061 wasRemoved.Enqueue(item); 1062 } 1063 1064 table = aux.table; 1065 size = aux.size; 1066 #if !REFBUCKET 1067 defaultvalid = aux.defaultvalid; 1068 defaultitem = aux.defaultitem; 1069 #endif 1070 indexmask = aux.indexmask; 1071 resizethreshhold = aux.resizethreshhold; 1072 bits = aux.bits; 1073 bitsc = aux.bitsc; 1074 #if DEBUG 1075 #else 1076 randomhashfactor = aux.randomhashfactor; 1077 #endif 1078 1079 if ((ActiveEvents & EventTypeEnum.Removed) != 0) 1080 raiseForRemoveAll(wasRemoved); 1081 else if ((ActiveEvents & EventTypeEnum.Changed) != 0) 1082 raiseCollectionChanged(); 1083 } 1084 1085 /// <summary> 1086 /// Check if all items in a supplied collection is in this set 1087 /// (ignoring multiplicities). 1088 /// </summary> 1089 /// <param name="items">The items to look for.</param> 1090 /// <typeparam name="U"></typeparam> 1091 /// <returns>True if all items are found.</returns> 1092 [Tested] 1093 public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T 1094 { 1095 foreach (U item in items) 1096 if (!Contains(item)) 1097 return false; 1098 return true; 1099 } 1100 1101 1102 /// <summary> 1103 /// Create an array containing all items in this set (in enumeration order). 1104 /// </summary> 1105 /// <returns>The array</returns> 1106 [Tested] ToArray()1107 public override T[] ToArray() 1108 { 1109 T[] res = new T[size]; 1110 int index = 0; 1111 1112 #if !REFBUCKET 1113 if (defaultvalid) 1114 res[index++] = defaultitem; 1115 #endif 1116 for (int i = 0; i < table.Length; i++) 1117 { 1118 Bucket b = table[i]; 1119 #if LINEARPROBING 1120 #if REFBUCKET 1121 if (b != null) 1122 res[index++] = b.item; 1123 #else 1124 if (!isnull(b.item)) 1125 res[index++] = b.item; 1126 #endif 1127 #else 1128 #if REFBUCKET 1129 while (b != null) 1130 { 1131 res[index++] = b.item; 1132 b = b.overflow; 1133 } 1134 #else 1135 if (!isnull(b.item)) 1136 { 1137 res[index++] = b.item; 1138 1139 OverflowBucket ob = b.overflow; 1140 1141 while (ob != null) 1142 { 1143 res[index++] = ob.item; 1144 ob = ob.overflow; 1145 } 1146 } 1147 #endif 1148 #endif 1149 } 1150 1151 Debug.Assert(size == index); 1152 return res; 1153 } 1154 1155 1156 /// <summary> 1157 /// Count the number of times an item is in this set (either 0 or 1). 1158 /// </summary> 1159 /// <param name="item">The item to look for.</param> 1160 /// <returns>1 if item is in set, 0 else</returns> 1161 [Tested] ContainsCount(T item)1162 public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; } 1163 1164 /// <summary> 1165 /// 1166 /// </summary> 1167 /// <returns></returns> UniqueItems()1168 public virtual ICollectionValue<T> UniqueItems() { return this; } 1169 1170 /// <summary> 1171 /// 1172 /// </summary> 1173 /// <returns></returns> ItemMultiplicities()1174 public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() 1175 { 1176 return new MultiplicityOne<T>(this); 1177 } 1178 1179 /// <summary> 1180 /// Remove all (at most 1) copies of item from this set. 1181 /// </summary> 1182 /// <param name="item">The item to remove</param> 1183 [Tested] RemoveAllCopies(T item)1184 public virtual void RemoveAllCopies(T item) { Remove(item); } 1185 1186 #endregion 1187 1188 #region IEnumerable<T> Members 1189 1190 1191 /// <summary> 1192 /// Choose some item of this collection. 1193 /// </summary> 1194 /// <exception cref="NoSuchItemException">if collection is empty.</exception> 1195 /// <returns></returns> 1196 [Tested] Choose()1197 public override T Choose() 1198 { 1199 int len = table.Length; 1200 if (size == 0) 1201 throw new NoSuchItemException(); 1202 #if REFBUCKET 1203 do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null); 1204 #else 1205 if (defaultvalid) return defaultitem; 1206 do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item)); 1207 #endif 1208 return table[lastchosen].item; 1209 } 1210 1211 /// <summary> 1212 /// Create an enumerator for this set. 1213 /// </summary> 1214 /// <returns>The enumerator</returns> 1215 [Tested] GetEnumerator()1216 public override SCG.IEnumerator<T> GetEnumerator() 1217 { 1218 int index = -1; 1219 int mystamp = stamp; 1220 int len = table.Length; 1221 1222 #if LINEARPROBING 1223 #if REFBUCKET 1224 while (++index < len) 1225 { 1226 if (mystamp != stamp) throw new CollectionModifiedException(); 1227 1228 if (table[index] != null) yield return table[index].item; 1229 } 1230 #else 1231 if (defaultvalid) 1232 yield return defaultitem; 1233 1234 while (++index < len) 1235 { 1236 if (mystamp != stamp) throw new CollectionModifiedException(); 1237 1238 T item = table[index].item; 1239 1240 if (!isnull(item)) yield return item; 1241 } 1242 #endif 1243 #else 1244 #if REFBUCKET 1245 Bucket b = null; 1246 #else 1247 OverflowBucket ob = null; 1248 1249 if (defaultvalid) 1250 yield return defaultitem; 1251 #endif 1252 while (true) 1253 { 1254 if (mystamp != stamp) 1255 throw new CollectionModifiedException(); 1256 1257 #if REFBUCKET 1258 if (b == null || b.overflow == null) 1259 { 1260 do 1261 { 1262 if (++index >= len) yield break; 1263 } while (table[index] == null); 1264 1265 b = table[index]; 1266 yield return b.item; 1267 } 1268 else 1269 { 1270 b = b.overflow; 1271 yield return b.item; 1272 } 1273 #else 1274 if (ob != null && ob.overflow != null) 1275 { 1276 ob = ob.overflow; 1277 yield return ob.item; 1278 } 1279 else if (index >= 0 && ob == null && (ob = table[index].overflow) != null) 1280 { 1281 yield return ob.item; 1282 } 1283 else 1284 { 1285 do 1286 { 1287 if (++index >= len) yield break; 1288 } while (isnull(table[index].item)); 1289 1290 yield return table[index].item; 1291 ob = null; 1292 } 1293 #endif 1294 } 1295 #endif 1296 } 1297 1298 #endregion 1299 1300 #region ISink<T> Members 1301 /// <summary> 1302 /// Report if this is a set collection. 1303 /// </summary> 1304 /// <value>Always false</value> 1305 [Tested] 1306 public virtual bool AllowsDuplicates { [Tested]get { return false; } } 1307 1308 /// <summary> 1309 /// By convention this is true for any collection with set semantics. 1310 /// </summary> 1311 /// <value>True if only one representative of a group of equal items 1312 /// is kept in the collection together with the total count.</value> 1313 public virtual bool DuplicatesByCounting { get { return true; } } 1314 1315 /// <summary> 1316 /// Add an item to this set. 1317 /// </summary> 1318 /// <param name="item">The item to add.</param> 1319 /// <returns>True if item was added (i.e. not found)</returns> 1320 [Tested] Add(T item)1321 public virtual bool Add(T item) 1322 { 1323 updatecheck(); 1324 return !searchoradd(ref item, true, false, true); 1325 } 1326 1327 /// <summary> 1328 /// Add an item to this set. 1329 /// </summary> 1330 /// <param name="item">The item to add.</param> 1331 [Tested] Add(T item)1332 void SCG.ICollection<T>.Add(T item) 1333 { 1334 Add(item); 1335 } 1336 1337 /// <summary> 1338 /// Add the elements from another collection with a more specialized item type 1339 /// to this collection. Since this 1340 /// collection has set semantics, only items not already in the collection 1341 /// will be added. 1342 /// </summary> 1343 /// <typeparam name="U">The type of items to add</typeparam> 1344 /// <param name="items">The items to add</param> 1345 [Tested] 1346 public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T 1347 { 1348 updatecheck(); 1349 bool wasChanged = false; 1350 bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0; 1351 CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null; 1352 foreach (T item in items) 1353 { 1354 T jtem = item; 1355 1356 if (!searchoradd(ref jtem, true, false, false)) 1357 { 1358 wasChanged = true; 1359 if (raiseAdded) 1360 wasAdded.Enqueue(item); 1361 } 1362 } 1363 //TODO: implement a RaiseForAddAll() method 1364 if (raiseAdded & wasChanged) 1365 foreach (T item in wasAdded) 1366 raiseItemsAdded(item, 1); 1367 if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged)) 1368 raiseCollectionChanged(); 1369 } 1370 1371 1372 #endregion 1373 1374 #region Diagnostics 1375 1376 /// <summary> 1377 /// Test internal structure of data (invariants) 1378 /// </summary> 1379 /// <returns>True if pass</returns> 1380 [Tested] Check()1381 public virtual bool Check() 1382 { 1383 int count = 0; 1384 #if LINEARPROBING 1385 int lasthole = table.Length - 1; 1386 1387 #if REFBUCKET 1388 while (lasthole >= 0 && table[lasthole] != null) 1389 #else 1390 while (lasthole >= 0 && !isnull(table[lasthole].item)) 1391 #endif 1392 { 1393 lasthole--; 1394 count++; 1395 } 1396 1397 if (lasthole < 0) 1398 { 1399 Console.WriteLine("Table is completely filled!"); 1400 return false; 1401 } 1402 1403 for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++) 1404 { 1405 Bucket b = table[cellindex]; 1406 int hashindex = hv2i(b.hashval); 1407 1408 if (hashindex <= lasthole || hashindex > cellindex) 1409 { 1410 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole); 1411 return false; 1412 } 1413 } 1414 1415 int latesthole = -1; 1416 1417 for (int cellindex = 0; cellindex < lasthole; cellindex++) 1418 { 1419 Bucket b = table[cellindex]; 1420 1421 #if REFBUCKET 1422 if (b != null) 1423 #else 1424 if (!isnull(b.item)) 1425 #endif 1426 { 1427 count++; 1428 1429 int hashindex = hv2i(b.hashval); 1430 1431 if (cellindex < hashindex && hashindex <= lasthole) 1432 { 1433 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole); 1434 return false; 1435 } 1436 } 1437 else 1438 { 1439 latesthole = cellindex; 1440 break; 1441 } 1442 } 1443 1444 for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++) 1445 { 1446 Bucket b = table[cellindex]; 1447 1448 #if REFBUCKET 1449 if (b != null) 1450 #else 1451 if (!isnull(b.item)) 1452 #endif 1453 { 1454 count++; 1455 1456 int hashindex = hv2i(b.hashval); 1457 1458 if (hashindex <= latesthole || cellindex < hashindex) 1459 { 1460 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole); 1461 return false; 1462 } 1463 } 1464 else 1465 { 1466 latesthole = cellindex; 1467 } 1468 } 1469 1470 return true; 1471 #else 1472 bool retval = true; 1473 1474 if (bitsc != 32 - bits) 1475 { 1476 Console.WriteLine("bitsc != 32 - bits ({0}, {1})", bitsc, bits); 1477 retval = false; 1478 } 1479 if (indexmask != (1 << bits) - 1) 1480 { 1481 Console.WriteLine("indexmask != (1 << bits) - 1 ({0}, {1})", indexmask, bits); 1482 retval = false; 1483 } 1484 if (table.Length != indexmask + 1) 1485 { 1486 Console.WriteLine("table.Length != indexmask + 1 ({0}, {1})", table.Length, indexmask); 1487 retval = false; 1488 } 1489 if (bitsc != 32 - bits) 1490 { 1491 Console.WriteLine("resizethreshhold != (int)(table.Length * fillfactor) ({0}, {1}, {2})", resizethreshhold, table.Length, fillfactor); 1492 retval = false; 1493 } 1494 1495 for (int i = 0, s = table.Length; i < s; i++) 1496 { 1497 int level = 0; 1498 Bucket b = table[i]; 1499 #if REFBUCKET 1500 while (b != null) 1501 { 1502 if (i != hv2i(b.hashval)) 1503 { 1504 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); 1505 retval = false; 1506 } 1507 1508 count++; 1509 level++; 1510 b = b.overflow; 1511 } 1512 #else 1513 if (!isnull(b.item)) 1514 { 1515 count++; 1516 if (i != hv2i(b.hashval)) 1517 { 1518 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); 1519 retval = false; 1520 } 1521 1522 OverflowBucket ob = b.overflow; 1523 1524 while (ob != null) 1525 { 1526 level++; 1527 count++; 1528 if (i != hv2i(ob.hashval)) 1529 { 1530 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); 1531 retval = false; 1532 } 1533 1534 ob = ob.overflow; 1535 } 1536 } 1537 #endif 1538 } 1539 1540 if (count != size) 1541 { 1542 Console.WriteLine("size({0}) != count({1})", size, count); 1543 retval = false; 1544 } 1545 1546 return retval; 1547 #endif 1548 } 1549 1550 1551 /// <summary> 1552 /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete. 1553 /// </summary> 1554 /// <returns>Histogram data.</returns> 1555 [Tested(via = "Manually")] BucketCostDistribution()1556 public ISortedDictionary<int, int> BucketCostDistribution() 1557 { 1558 TreeDictionary<int, int> res = new TreeDictionary<int, int>(); 1559 #if LINEARPROBING 1560 int count = 0; 1561 #if REFBUCKET 1562 while (table[count] != null) 1563 #else 1564 while (!isnull(table[count].item)) 1565 #endif 1566 count++; 1567 for (int i = table.Length - 1; i >= 0; i--) 1568 { 1569 #if REFBUCKET 1570 if (table[i] != null) 1571 #else 1572 if (!isnull(table[i].item)) 1573 #endif 1574 count++; 1575 else 1576 count = 0; 1577 if (res.Contains(count)) 1578 res[count]++; 1579 else 1580 res[count] = 1; 1581 } 1582 1583 return res; 1584 #else 1585 for (int i = 0, s = table.Length; i < s; i++) 1586 { 1587 int count = 0; 1588 #if REFBUCKET 1589 Bucket b = table[i]; 1590 1591 while (b != null) 1592 { 1593 count++; 1594 b = b.overflow; 1595 } 1596 #else 1597 Bucket b = table[i]; 1598 1599 if (!isnull(b.item)) 1600 { 1601 count = 1; 1602 1603 OverflowBucket ob = b.overflow; 1604 1605 while (ob != null) 1606 { 1607 count++; 1608 ob = ob.overflow; 1609 } 1610 } 1611 #endif 1612 if (res.Contains(count)) 1613 res[count]++; 1614 else 1615 res[count] = 1; 1616 } 1617 1618 return res; 1619 #endif 1620 } 1621 1622 #endregion 1623 1624 #region ICloneable Members 1625 1626 /// <summary> 1627 /// Make a shallow copy of this HashSet. 1628 /// </summary> 1629 /// <returns></returns> Clone()1630 public virtual object Clone() 1631 { 1632 HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer); 1633 //TODO: make sure this really adds in the counting bag way! 1634 clone.AddAll(this); 1635 return clone; 1636 } 1637 1638 #endregion 1639 1640 } 1641 } 1642