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