3 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
\r
4 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
5 of this software and associated documentation files (the "Software"), to deal
\r
6 in the Software without restriction, including without limitation the rights
\r
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
8 copies of the Software, and to permit persons to whom the Software is
\r
9 furnished to do so, subject to the following conditions:
\r
11 The above copyright notice and this permission notice shall be included in
\r
12 all copies or substantial portions of the Software.
\r
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
23 #define LINEARPROBINGnot
\r
26 #define INTERHASHINGnot
\r
27 #define RANDOMINTERHASHING
\r
30 using System.Diagnostics;
\r
31 using SCG = System.Collections.Generic;
\r
36 /// A set collection class based on linear hashing
\r
39 public class HashSet<T> : CollectionBase<T>, ICollection<T>
\r
43 /// Enum class to assist printing of compilation alternatives.
\r
46 public enum Feature : short
\r
53 /// Buckets are of reference type
\r
57 /// Primary buckets are of value type
\r
59 ValueTypeBucket = 2,
\r
61 /// Using linear probing to resolve index clashes
\r
65 /// Shrink table when very sparsely filled
\r
69 /// Use chaining to resolve index clashes
\r
73 /// Use hash function on item hash code
\r
77 /// Use a universal family of hash functions on item hash code
\r
79 RandomInterHashing = 64
\r
84 static Feature features = Feature.Dummy
\r
86 | Feature.RefTypeBucket
\r
88 | Feature.ValueTypeBucket
\r
91 | Feature.ShrinkTable
\r
94 | Feature.LinearProbing
\r
99 | Feature.InterHashing
\r
100 #elif RANDOMINTERHASHING
\r
101 | Feature.RandomInterHashing
\r
107 /// Show which implementation features was chosen at compilation time
\r
109 public static Feature Features { get { return features; } }
\r
115 int indexmask, bits, bitsc, origbits, lastchosen; //bitsc==32-bits; indexmask==(1<<bits)-1;
\r
120 bool defaultvalid = false;
\r
124 double fillfactor = 0.66;
\r
126 int resizethreshhold;
\r
128 #if RANDOMINTERHASHING
\r
130 const uint randomhashfactor = 1529784659;
\r
132 uint randomhashfactor = (2 * (uint)(new Random()).Next() + 1) * 1529784659;
\r
143 /// <value></value>
\r
144 public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
\r
148 #region Bucket nested class(es)
\r
155 internal int hashval; //Cache!
\r
158 internal Bucket(T item, int hashval)
\r
161 this.hashval = hashval;
\r
164 internal Bucket overflow;
\r
166 internal Bucket(T item, int hashval, Bucket overflow)
\r
169 this.hashval = hashval;
\r
170 this.overflow = overflow;
\r
179 internal int hashval; //Cache!
\r
182 internal Bucket(T item, int hashval)
\r
185 this.hashval = hashval;
\r
188 internal OverflowBucket overflow;
\r
191 internal Bucket(T item, int hashval)
\r
194 this.hashval = hashval;
\r
195 this.overflow = default(OverflowBucket);
\r
202 class OverflowBucket
\r
206 internal int hashval; //Cache!
\r
208 internal OverflowBucket overflow;
\r
211 internal OverflowBucket(T item, int hashval, OverflowBucket overflow)
\r
214 this.hashval = hashval;
\r
215 this.overflow = overflow;
\r
225 bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
\r
228 bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); }
\r
231 int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); }
\r
234 int hv2i(int hashval)
\r
237 //Note: *inverse mod 2^32 is -1503427877
\r
238 return (int)(((uint)hashval * 1529784659) >>bitsc);
\r
239 #elif RANDOMINTERHASHING
\r
240 return (int)(((uint)hashval * randomhashfactor) >> bitsc);
\r
242 return indexmask & hashval;
\r
249 //Console.WriteLine(String.Format("Expand to {0} bits", bits+1));
\r
258 //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1));
\r
264 void resize(int bits)
\r
266 //Console.WriteLine(String.Format("Resize to {0} bits", bits));
\r
269 indexmask = (1 << bits) - 1;
\r
271 Bucket[] newtable = new Bucket[indexmask + 1];
\r
273 for (int i = 0, s = table.Length; i < s; i++)
\r
275 Bucket b = table[i];
\r
281 int j = hv2i(b.hashval);
\r
283 while (newtable[j] != null) { j = indexmask & (j + 1); }
\r
288 if (!isnull(b.item))
\r
290 int j = hv2i(b.hashval);
\r
292 while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); }
\r
301 int j = hv2i(b.hashval);
\r
303 newtable[j] = new Bucket(b.item, b.hashval, newtable[j]);
\r
307 if (!isnull(b.item))
\r
309 insert(b.item, b.hashval, newtable);
\r
311 OverflowBucket ob = b.overflow;
\r
315 insert(ob.item, ob.hashval, newtable);
\r
324 resizethreshhold = (int)(table.Length * fillfactor);
\r
325 //Console.WriteLine(String.Format("Resize to {0} bits done", bits));
\r
332 //Only for resize!!!
\r
333 private void insert(T item, int hashval, Bucket[] t)
\r
335 int i = hv2i(hashval);
\r
338 if (!isnull(b.item))
\r
340 t[i].overflow = new OverflowBucket(item, hashval, b.overflow);
\r
343 t[i] = new Bucket(item, hashval);
\r
349 /// Search for an item equal (according to itemequalityComparer) to the supplied item.
\r
351 /// <param name="item"></param>
\r
352 /// <param name="add">If true, add item to table if not found.</param>
\r
353 /// <param name="update">If true, update table entry if item found.</param>
\r
354 /// <param name="raise">If true raise events</param>
\r
355 /// <returns>True if found</returns>
\r
356 private bool searchoradd(ref T item, bool add, bool update, bool raise)
\r
361 int hashval = gethashcode(item);
\r
362 int i = hv2i(hashval);
\r
363 Bucket b = table[i];
\r
367 T olditem = b.item;
\r
368 if (equals(olditem, item))
\r
375 if (raise && update)
\r
376 raiseForUpdate(item, olditem);
\r
380 b = table[i = indexmask & (i + 1)];
\r
383 if (!add) goto notfound;
\r
385 table[i] = new Bucket(item, hashval);
\r
392 T olditem = defaultitem;
\r
394 defaultitem = item;
\r
396 item = defaultitem;
\r
398 if (raise && update)
\r
399 raiseForUpdate(item, olditem);
\r
403 if (!add) goto notfound;
\r
405 defaultvalid = true;
\r
406 defaultitem = item;
\r
410 int hashval = gethashcode(item);
\r
411 int i = hv2i(hashval);
\r
412 T t = table[i].item;
\r
416 if (equals(t, item))
\r
419 table[i].item = item;
\r
423 if (raise && update)
\r
424 raiseForUpdate(item, t);
\r
428 t = table[i = indexmask & (i + 1)].item;
\r
431 if (!add) goto notfound;
\r
433 table[i] = new Bucket(item, hashval);
\r
438 int hashval = gethashcode(item);
\r
439 int i = hv2i(hashval);
\r
440 Bucket b = table[i], bold = null;
\r
446 T olditem = b.item;
\r
447 if (equals(olditem, item))
\r
455 if (raise && update)
\r
456 raiseForUpdate(item, olditem);
\r
464 if (!add) goto notfound;
\r
466 bold.overflow = new Bucket(item, hashval, null);
\r
470 if (!add) goto notfound;
\r
472 table[i] = new Bucket(item, hashval, null);
\r
479 T olditem = defaultitem;
\r
481 defaultitem = item;
\r
483 item = defaultitem;
\r
485 if (raise && update)
\r
486 raiseForUpdate(item, olditem);
\r
490 if (!add) goto notfound;
\r
492 defaultvalid = true;
\r
493 defaultitem = item;
\r
497 int hashval = gethashcode(item);
\r
498 int i = hv2i(hashval);
\r
499 Bucket b = table[i];
\r
501 if (!isnull(b.item))
\r
503 if (equals(b.item, item))
\r
506 table[i].item = item;
\r
510 if (raise && update)
\r
511 raiseForUpdate(item, b.item);
\r
515 OverflowBucket ob = table[i].overflow;
\r
519 if (!add) goto notfound;
\r
521 table[i].overflow = new OverflowBucket(item, hashval, null);
\r
525 T olditem = ob.item;
\r
526 while (ob.overflow != null)
\r
528 if (equals(item, olditem))
\r
535 if (raise && update)
\r
536 raiseForUpdate(item, olditem);
\r
544 if (equals(item, olditem))
\r
551 if (raise && update)
\r
552 raiseForUpdate(item, olditem);
\r
556 if (!add) goto notfound;
\r
558 ob.overflow = new OverflowBucket(item, hashval, null);
\r
563 if (!add) goto notfound;
\r
565 table[i] = new Bucket(item, hashval);
\r
571 if (size > resizethreshhold)
\r
580 private bool remove(ref T item)
\r
587 int hashval = gethashcode(item);
\r
588 int index = hv2i(hashval);
\r
589 Bucket b = table[index];
\r
593 if (equals(item, b.item))
\r
596 item = table[index].item;
\r
597 table[index] = null;
\r
600 int j = (index + 1) & indexmask;
\r
605 int k = hv2i(b.hashval);
\r
607 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
\r
608 //if (index > j ? (j < k && k <= index): (k <= index || j < k) )
\r
615 j = (j + 1) & indexmask;
\r
622 b = table[index = indexmask & (index + 1)];
\r
632 item = defaultitem;
\r
633 defaultvalid = false;
\r
634 defaultitem = default(T); //No spaceleaks!
\r
638 int hashval = gethashcode(item);
\r
639 int index = hv2i(hashval);
\r
640 T t = table[index].item;
\r
644 if (equals(item, t))
\r
647 item = table[index].item;
\r
648 table[index].item = default(T);
\r
651 int j = (index + 1) & indexmask;
\r
652 Bucket b = table[j];
\r
654 while (!isnull(b.item))
\r
656 int k = hv2i(b.hashval);
\r
658 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
\r
661 table[j].item = default(T);
\r
665 j = (j + 1) & indexmask;
\r
672 t = table[index = indexmask & (index + 1)].item;
\r
681 int hashval = gethashcode(item);
\r
682 int index = hv2i(hashval);
\r
683 Bucket b = table[index], bold;
\r
688 if (equals(item, b.item))
\r
692 table[index] = b.overflow;
\r
698 while (b != null && !equals(item, b.item))
\r
709 bold.overflow = b.overflow;
\r
719 item = defaultitem;
\r
720 defaultvalid = false;
\r
721 defaultitem = default(T); //No spaceleaks!
\r
725 int hashval = gethashcode(item);
\r
726 int index = hv2i(hashval);
\r
727 Bucket b = table[index];
\r
728 OverflowBucket ob = b.overflow;
\r
730 if (equals(item, b.item))
\r
736 table[index] = new Bucket();
\r
740 b = new Bucket(ob.item, ob.hashval);
\r
741 b.overflow = ob.overflow;
\r
750 if (equals(item, ob.item))
\r
754 table[index].overflow = ob.overflow;
\r
758 while (ob.overflow != null)
\r
759 if (equals(item, ob.overflow.item))
\r
762 item = ob.overflow.item;
\r
768 if (ob.overflow == null)
\r
771 ob.overflow = ob.overflow.overflow;
\r
783 private void clear()
\r
787 indexmask = (1 << bits) - 1;
\r
789 table = new Bucket[indexmask + 1];
\r
790 resizethreshhold = (int)(table.Length * fillfactor);
\r
792 defaultitem = default(T);
\r
793 defaultvalid = false;
\r
799 #region Constructors
\r
801 /// Create a hash set with natural item equalityComparer and default fill threshold (66%)
\r
802 /// and initial table size (16).
\r
805 : this(EqualityComparer<T>.Default) { }
\r
809 /// Create a hash set with external item equalityComparer and default fill threshold (66%)
\r
810 /// and initial table size (16).
\r
812 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
813 public HashSet(SCG.IEqualityComparer<T> itemequalityComparer)
\r
814 : this(16, itemequalityComparer) { }
\r
818 /// Create a hash set with external item equalityComparer and default fill threshold (66%)
\r
820 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
\r
821 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
822 public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
\r
823 : this(capacity, 0.66, itemequalityComparer) { }
\r
827 /// Create a hash set with external item equalityComparer.
\r
829 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
\r
830 /// <param name="fill">Fill threshold (in range 10% to 90%)</param>
\r
831 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
832 public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer)
\r
834 if (fill < 0.1 || fill > 0.9)
\r
835 throw new ArgumentException("Fill outside valid range [0.1, 0.9]");
\r
837 throw new ArgumentException("Capacity must be non-negative");
\r
838 //this.itemequalityComparer = itemequalityComparer;
\r
840 while (capacity - 1 >> origbits > 0) origbits++;
\r
848 #region IEditableCollection<T> Members
\r
851 /// The complexity of the Contains operation
\r
853 /// <value>Always returns Speed.Constant</value>
\r
855 public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } }
\r
858 /// Check if an item is in the set
\r
860 /// <param name="item">The item to look for</param>
\r
861 /// <returns>True if set contains item</returns>
\r
863 public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); }
\r
867 /// Check if an item (collection equal to a given one) is in the set and
\r
868 /// if so report the actual item object found.
\r
870 /// <param name="item">On entry, the item to look for.
\r
871 /// On exit the item found, if any</param>
\r
872 /// <returns>True if set contains item</returns>
\r
874 public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); }
\r
878 /// Check if an item (collection equal to a given one) is in the set and
\r
879 /// if so replace the item object in the set with the supplied one.
\r
881 /// <param name="item">The item object to update with</param>
\r
882 /// <returns>True if item was found (and updated)</returns>
\r
884 public virtual bool Update(T item)
\r
885 { updatecheck(); return searchoradd(ref item, false, true, true); }
\r
888 /// Check if an item (collection equal to a given one) is in the set and
\r
889 /// if so replace the item object in the set with the supplied one.
\r
891 /// <param name="item">The item object to update with</param>
\r
892 /// <param name="olditem"></param>
\r
893 /// <returns>True if item was found (and updated)</returns>
\r
894 public virtual bool Update(T item, out T olditem)
\r
895 { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); }
\r
899 /// Check if an item (collection equal to a given one) is in the set.
\r
900 /// If found, report the actual item object in the set,
\r
901 /// else add the supplied one.
\r
903 /// <param name="item">On entry, the item to look for or add.
\r
904 /// On exit the actual object found, if any.</param>
\r
905 /// <returns>True if item was found</returns>
\r
907 public virtual bool FindOrAdd(ref T item)
\r
908 { updatecheck(); return searchoradd(ref item, true, false, true); }
\r
912 /// Check if an item (collection equal to a supplied one) is in the set and
\r
913 /// if so replace the item object in the set with the supplied one; else
\r
914 /// add the supplied one.
\r
916 /// <param name="item">The item to look for and update or add</param>
\r
917 /// <returns>True if item was updated</returns>
\r
919 public virtual bool UpdateOrAdd(T item)
\r
920 { updatecheck(); return searchoradd(ref item, true, true, true); }
\r
924 /// Check if an item (collection equal to a supplied one) is in the set and
\r
925 /// if so replace the item object in the set with the supplied one; else
\r
926 /// add the supplied one.
\r
928 /// <param name="item">The item to look for and update or add</param>
\r
929 /// <param name="olditem"></param>
\r
930 /// <returns>True if item was updated</returns>
\r
931 public virtual bool UpdateOrAdd(T item, out T olditem)
\r
932 { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); }
\r
936 /// Remove an item from the set
\r
938 /// <param name="item">The item to remove</param>
\r
939 /// <returns>True if item was (found and) removed </returns>
\r
941 public virtual bool Remove(T item)
\r
944 if (remove(ref item))
\r
947 if (size<resizethreshhold/2 && resizethreshhold>8)
\r
950 raiseForRemove(item);
\r
959 /// Remove an item from the set, reporting the actual matching item object.
\r
961 /// <param name="item">The value to remove.</param>
\r
962 /// <param name="removeditem">The removed value.</param>
\r
963 /// <returns>True if item was found.</returns>
\r
965 public virtual bool Remove(T item, out T removeditem)
\r
968 removeditem = item;
\r
969 if (remove(ref removeditem))
\r
972 if (size<resizethreshhold/2 && resizethreshhold>8)
\r
975 raiseForRemove(removeditem);
\r
984 /// Remove all items in a supplied collection from this set.
\r
986 /// <typeparam name="U"></typeparam>
\r
987 /// <param name="items">The items to remove.</param>
\r
989 public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
\r
992 RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this);
\r
993 bool raise = raiseHandler.MustFire;
\r
995 foreach (U item in items)
\r
996 { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); }
\r
998 if (size < resizethreshhold / 2 && resizethreshhold > 16)
\r
1000 int newlength = table.Length;
\r
1002 while (newlength >= 32 && newlength * fillfactor / 2 > size)
\r
1005 resize(newlength - 1);
\r
1008 if (raise) raiseHandler.Raise();
\r
1012 /// Remove all items from the set, resetting internal table to initial size.
\r
1015 public virtual void Clear()
\r
1018 int oldsize = size;
\r
1020 if (ActiveEvents != 0 && oldsize > 0)
\r
1022 raiseCollectionCleared(true, oldsize);
\r
1023 raiseCollectionChanged();
\r
1029 /// Remove all items *not* in a supplied collection from this set.
\r
1031 /// <typeparam name="U"></typeparam>
\r
1032 /// <param name="items">The items to retain</param>
\r
1034 public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1038 HashSet<T> aux = new HashSet<T>(EqualityComparer);
\r
1040 //This only works for sets:
\r
1041 foreach (U item in items)
\r
1042 if (Contains(item))
\r
1046 aux.searchoradd(ref jtem, true, false, false);
\r
1049 if (size == aux.size)
\r
1052 CircularQueue<T> wasRemoved = null;
\r
1053 if ((ActiveEvents & EventTypeEnum.Removed) != 0)
\r
1055 wasRemoved = new CircularQueue<T>();
\r
1056 foreach (T item in this)
\r
1057 if (!aux.Contains(item))
\r
1058 wasRemoved.Enqueue(item);
\r
1061 table = aux.table;
\r
1064 defaultvalid = aux.defaultvalid;
\r
1065 defaultitem = aux.defaultitem;
\r
1067 indexmask = aux.indexmask;
\r
1068 resizethreshhold = aux.resizethreshhold;
\r
1071 if ((ActiveEvents & EventTypeEnum.Removed) != 0)
\r
1072 raiseForRemoveAll(wasRemoved);
\r
1073 else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
\r
1074 raiseCollectionChanged();
\r
1078 /// Check if all items in a supplied collection is in this set
\r
1079 /// (ignoring multiplicities).
\r
1081 /// <param name="items">The items to look for.</param>
\r
1082 /// <typeparam name="U"></typeparam>
\r
1083 /// <returns>True if all items are found.</returns>
\r
1085 public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1087 foreach (U item in items)
\r
1088 if (!Contains(item))
\r
1095 /// Create an array containing all items in this set (in enumeration order).
\r
1097 /// <returns>The array</returns>
\r
1099 public override T[] ToArray()
\r
1101 T[] res = new T[size];
\r
1106 res[index++] = defaultitem;
\r
1108 for (int i = 0; i < table.Length; i++)
\r
1110 Bucket b = table[i];
\r
1114 res[index++] = b.item;
\r
1116 if (!isnull(b.item))
\r
1117 res[index++] = b.item;
\r
1123 res[index++] = b.item;
\r
1127 if (!isnull(b.item))
\r
1129 res[index++] = b.item;
\r
1131 OverflowBucket ob = b.overflow;
\r
1133 while (ob != null)
\r
1135 res[index++] = ob.item;
\r
1143 Debug.Assert(size == index);
\r
1149 /// Count the number of times an item is in this set (either 0 or 1).
\r
1151 /// <param name="item">The item to look for.</param>
\r
1152 /// <returns>1 if item is in set, 0 else</returns>
\r
1154 public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; }
\r
1159 /// <returns></returns>
\r
1160 public virtual ICollectionValue<T> UniqueItems() { return this; }
\r
1165 /// <returns></returns>
\r
1166 public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
\r
1168 return new MultiplicityOne<T>(this);
\r
1172 /// Remove all (at most 1) copies of item from this set.
\r
1174 /// <param name="item">The item to remove</param>
\r
1176 public virtual void RemoveAllCopies(T item) { Remove(item); }
\r
1180 #region IEnumerable<T> Members
\r
1184 /// Choose some item of this collection.
\r
1186 /// <exception cref="NoSuchItemException">if collection is empty.</exception>
\r
1187 /// <returns></returns>
\r
1189 public override T Choose()
\r
1191 int len = table.Length;
\r
1193 throw new NoSuchItemException();
\r
1195 do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null);
\r
1197 if (defaultvalid) return defaultitem;
\r
1198 do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item));
\r
1200 return table[lastchosen].item;
\r
1204 /// Create an enumerator for this set.
\r
1206 /// <returns>The enumerator</returns>
\r
1208 public override SCG.IEnumerator<T> GetEnumerator()
\r
1211 int mystamp = stamp;
\r
1212 int len = table.Length;
\r
1216 while (++index < len)
\r
1218 if (mystamp != stamp) throw new CollectionModifiedException();
\r
1220 if (table[index] != null) yield return table[index].item;
\r
1224 yield return defaultitem;
\r
1226 while (++index < len)
\r
1228 if (mystamp != stamp) throw new CollectionModifiedException();
\r
1230 T item = table[index].item;
\r
1232 if (!isnull(item)) yield return item;
\r
1239 OverflowBucket ob = null;
\r
1242 yield return defaultitem;
\r
1246 if (mystamp != stamp)
\r
1247 throw new CollectionModifiedException();
\r
1250 if (b == null || b.overflow == null)
\r
1254 if (++index >= len) yield break;
\r
1255 } while (table[index] == null);
\r
1258 yield return b.item;
\r
1263 yield return b.item;
\r
1266 if (ob != null && ob.overflow != null)
\r
1269 yield return ob.item;
\r
1271 else if (index >= 0 && ob == null && (ob = table[index].overflow) != null)
\r
1273 yield return ob.item;
\r
1279 if (++index >= len) yield break;
\r
1280 } while (isnull(table[index].item));
\r
1282 yield return table[index].item;
\r
1292 #region ISink<T> Members
\r
1294 /// Report if this is a set collection.
\r
1296 /// <value>Always false</value>
\r
1298 public virtual bool AllowsDuplicates { [Tested]get { return false; } }
\r
1301 /// By convention this is true for any collection with set semantics.
\r
1303 /// <value>True if only one representative of a group of equal items
\r
1304 /// is kept in the collection together with the total count.</value>
\r
1305 public virtual bool DuplicatesByCounting { get { return true; } }
\r
1308 /// Add an item to this set.
\r
1310 /// <param name="item">The item to add.</param>
\r
1311 /// <returns>True if item was added (i.e. not found)</returns>
\r
1313 public virtual bool Add(T item)
\r
1316 return !searchoradd(ref item, true, false, true);
\r
1320 /// Add the elements from another collection with a more specialized item type
\r
1321 /// to this collection. Since this
\r
1322 /// collection has set semantics, only items not already in the collection
\r
1323 /// will be added.
\r
1325 /// <typeparam name="U">The type of items to add</typeparam>
\r
1326 /// <param name="items">The items to add</param>
\r
1328 public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1331 bool wasChanged = false;
\r
1332 bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
\r
1333 CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null;
\r
1334 foreach (T item in items)
\r
1338 if (!searchoradd(ref jtem, true, false, false))
\r
1340 wasChanged = true;
\r
1342 wasAdded.Enqueue(item);
\r
1345 //TODO: implement a RaiseForAddAll() method
\r
1346 if (raiseAdded & wasChanged)
\r
1347 foreach (T item in wasAdded)
\r
1348 raiseItemsAdded(item, 1);
\r
1349 if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged))
\r
1350 raiseCollectionChanged();
\r
1356 #region Diagnostics
\r
1359 /// Test internal structure of data (invariants)
\r
1361 /// <returns>True if pass</returns>
\r
1363 public virtual bool Check()
\r
1367 int lasthole = table.Length - 1;
\r
1370 while (lasthole >= 0 && table[lasthole] != null)
\r
1372 while (lasthole >= 0 && !isnull(table[lasthole].item))
\r
1381 Console.WriteLine("Table is completely filled!");
\r
1385 for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++)
\r
1387 Bucket b = table[cellindex];
\r
1388 int hashindex = hv2i(b.hashval);
\r
1390 if (hashindex <= lasthole || hashindex > cellindex)
\r
1392 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole);
\r
1397 int latesthole = -1;
\r
1399 for (int cellindex = 0; cellindex < lasthole; cellindex++)
\r
1401 Bucket b = table[cellindex];
\r
1406 if (!isnull(b.item))
\r
1411 int hashindex = hv2i(b.hashval);
\r
1413 if (cellindex < hashindex && hashindex <= lasthole)
\r
1415 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
\r
1421 latesthole = cellindex;
\r
1426 for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++)
\r
1428 Bucket b = table[cellindex];
\r
1433 if (!isnull(b.item))
\r
1438 int hashindex = hv2i(b.hashval);
\r
1440 if (hashindex <= latesthole || cellindex < hashindex)
\r
1442 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
\r
1448 latesthole = cellindex;
\r
1454 bool retval = true;
\r
1455 for (int i = 0, s = table.Length; i < s; i++)
\r
1458 Bucket b = table[i];
\r
1462 if (i != hv2i(b.hashval))
\r
1464 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1473 if (!isnull(b.item))
\r
1476 if (i != hv2i(b.hashval))
\r
1478 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1482 OverflowBucket ob = b.overflow;
\r
1484 while (ob != null)
\r
1488 if (i != hv2i(ob.hashval))
\r
1490 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1500 if (count != size)
\r
1502 Console.WriteLine("size({0}) != count({1})", size, count);
\r
1512 /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete.
\r
1514 /// <returns>Histogram data.</returns>
\r
1515 [Tested(via = "Manually")]
\r
1516 public ISortedDictionary<int, int> BucketCostDistribution()
\r
1518 TreeDictionary<int, int> res = new TreeDictionary<int, int>();
\r
1522 while (table[count] != null)
\r
1524 while (!isnull(table[count].item))
\r
1527 for (int i = table.Length - 1; i >= 0; i--)
\r
1530 if (table[i] != null)
\r
1532 if (!isnull(table[i].item))
\r
1537 if (res.Contains(count))
\r
1545 for (int i = 0, s = table.Length; i < s; i++)
\r
1549 Bucket b = table[i];
\r
1557 Bucket b = table[i];
\r
1559 if (!isnull(b.item))
\r
1563 OverflowBucket ob = b.overflow;
\r
1565 while (ob != null)
\r
1572 if (res.Contains(count))
\r
1584 #region ICloneable Members
\r
1587 /// Make a shallow copy of this HashSet.
\r
1589 /// <returns></returns>
\r
1590 public virtual object Clone()
\r
1592 HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer);
\r
1593 //TODO: make sure this really adds in the counting bag way!
\r
1594 clone.AddAll(this);
\r