| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- //
- // HashSet.cs
- //
- // Authors:
- // Jb Evain <[email protected]>
- //
- // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.Serialization;
- using System.Runtime.InteropServices;
- using System.Security;
- using System.Security.Permissions;
- // HashSet is basically implemented as a reduction of Dictionary<K, V>
- namespace System.Collections.Generic {
- [Serializable, HostProtection (SecurityAction.LinkDemand, MayLeakOnAbort = true)]
- public class HashSet<T> : ICollection<T>, ISerializable, IDeserializationCallback {
- const int INITIAL_SIZE = 10;
- const float DEFAULT_LOAD_FACTOR = (90f / 100);
- const int NO_SLOT = -1;
- struct Link {
- public int HashCode;
- public int Next;
- }
- // The hash table contains indices into the "links" array
- int [] table;
- Link [] links;
- T [] slots;
- // The number of slots in "links" and "slots" that
- // are in use (i.e. filled with data) or have been used and marked as
- // "empty" later on.
- int touched;
- // The index of the first slot in the "empty slots chain".
- // "Remove ()" prepends the cleared slots to the empty chain.
- // "Add ()" fills the first slot in the empty slots chain with the
- // added item (or increases "touched" if the chain itself is empty).
- int empty_slot;
- // The number of items in this set.
- int count;
- // The number of items the set can hold without
- // resizing the hash table and the slots arrays.
- int threshold;
- IEqualityComparer<T> comparer;
- SerializationInfo si;
- // The number of changes made to this set. Used by enumerators
- // to detect changes and invalidate themselves.
- int generation;
- public int Count {
- get { return count; }
- }
- public HashSet ()
- {
- Init (INITIAL_SIZE, null);
- }
- public HashSet (IEqualityComparer<T> comparer)
- {
- Init (INITIAL_SIZE, comparer);
- }
- public HashSet (IEnumerable<T> collection) : this (collection, null)
- {
- }
- public HashSet (IEnumerable<T> collection, IEqualityComparer<T> comparer)
- {
- if (collection == null)
- throw new ArgumentNullException ("collection");
- int capacity = 0;
- var col = collection as ICollection<T>;
- if (col != null)
- capacity = col.Count;
- Init (capacity, comparer);
- foreach (var item in collection)
- Add (item);
- }
- protected HashSet (SerializationInfo info, StreamingContext context)
- {
- si = info;
- }
- void Init (int capacity, IEqualityComparer<T> comparer)
- {
- if (capacity < 0)
- throw new ArgumentOutOfRangeException ("capacity");
- this.comparer = comparer ?? EqualityComparer<T>.Default;
- if (capacity == 0)
- capacity = INITIAL_SIZE;
- /* Modify capacity so 'capacity' elements can be added without resizing */
- capacity = (int) (capacity / DEFAULT_LOAD_FACTOR) + 1;
- InitArrays (capacity);
- generation = 0;
- }
- void InitArrays (int size) {
- table = new int [size];
- links = new Link [size];
- empty_slot = NO_SLOT;
- slots = new T [size];
- touched = 0;
- threshold = (int) (table.Length * DEFAULT_LOAD_FACTOR);
- if (threshold == 0 && table.Length > 0)
- threshold = 1;
- }
- bool SlotsContainsAt (int index, int hash, T item)
- {
- int current = table [index] - 1;
- while (current != NO_SLOT) {
- Link link = links [current];
- if (link.HashCode == hash && comparer.Equals (item, slots [current]))
- return true;
- current = link.Next;
- }
- return false;
- }
- public void CopyTo (T [] array)
- {
- CopyTo (array, 0, count);
- }
- public void CopyTo (T [] array, int index)
- {
- CopyTo (array, index, count);
- }
- public void CopyTo (T [] array, int index, int count)
- {
- if (array == null)
- throw new ArgumentNullException ("array");
- if (index < 0)
- throw new ArgumentOutOfRangeException ("index");
- if (index > array.Length)
- throw new ArgumentException ("index larger than largest valid index of array");
- if (array.Length - index < count)
- throw new ArgumentException ("Destination array cannot hold the requested elements!");
- for (int i = 0, items = 0; i < table.Length && items < count; i++) {
- int current = table [i] - 1;
- while (current != NO_SLOT) {
- array [index++] = slots [current];
- current = links [current].Next;
- items++;
- }
- }
- }
- void Resize ()
- {
- int newSize = PrimeHelper.ToPrime ((table.Length << 1) | 1);
- // allocate new hash table and link slots array
- var newTable = new int [newSize];
- var newLinks = new Link [newSize];
- for (int i = 0; i < table.Length; i++) {
- int current = table [i] - 1;
- while (current != NO_SLOT) {
- int hashCode = newLinks [current].HashCode = comparer.GetHashCode (slots [current]);
- int index = (hashCode & int.MaxValue) % newSize;
- newLinks [current].Next = newTable [index] - 1;
- newTable [index] = current + 1;
- current = links [current].Next;
- }
- }
- table = newTable;
- links = newLinks;
- // allocate new data slots, copy data
- var newSlots = new T [newSize];
- Array.Copy (slots, 0, newSlots, 0, touched);
- slots = newSlots;
- threshold = (int) (newSize * DEFAULT_LOAD_FACTOR);
- }
- public bool Add (T item)
- {
- int hashCode = comparer.GetHashCode (item);
- int index = (hashCode & int.MaxValue) % table.Length;
- if (SlotsContainsAt (index, hashCode, item))
- return false;
- if (++count > threshold) {
- Resize ();
- index = (hashCode & int.MaxValue) % table.Length;
- }
- // find an empty slot
- int current = empty_slot;
- if (current == NO_SLOT)
- current = touched++;
- else
- empty_slot = links [current].Next;
- // store the hash code of the added item,
- // prepend the added item to its linked list,
- // update the hash table
- links [current].HashCode = hashCode;
- links [current].Next = table [index] - 1;
- table [index] = current + 1;
- // store item
- slots [current] = item;
- generation++;
- return true;
- }
- public IEqualityComparer<T> Comparer {
- get { return comparer; }
- }
- public void Clear ()
- {
- count = 0;
- // clear the hash table and the slots
- Array.Clear (table, 0, table.Length);
- Array.Clear (slots, 0, slots.Length);
- // empty the "empty slots chain"
- empty_slot = NO_SLOT;
- touched = 0;
- generation++;
- }
- public bool Contains (T item)
- {
- int hashCode = comparer.GetHashCode (item);
- int index = (hashCode & int.MaxValue) % table.Length;
- return SlotsContainsAt (index, hashCode, item);
- }
- public bool Remove (T item)
- {
- // get first item of linked list corresponding to given key
- int hashCode = comparer.GetHashCode (item);
- int index = (hashCode & int.MaxValue) % table.Length;
- int current = table [index] - 1;
- // if there is no linked list, return false
- if (current == NO_SLOT)
- return false;
- // walk linked list until right slot (and its predecessor) is
- // found or end is reached
- int prev = NO_SLOT;
- do {
- Link link = links [current];
- if (link.HashCode == hashCode && comparer.Equals (slots [current], item))
- break;
- prev = current;
- current = link.Next;
- } while (current != NO_SLOT);
- // if we reached the end of the chain, return false
- if (current == NO_SLOT)
- return false;
- count--;
- // remove slot from linked list
- // is slot at beginning of linked list?
- if (prev == NO_SLOT)
- table [index] = links [current].Next + 1;
- else
- links [prev].Next = links [current].Next;
- // mark slot as empty and prepend it to "empty slots chain"
- links [current].Next = empty_slot;
- empty_slot = current;
- // clear slot
- slots [current] = default (T);
- generation++;
- return true;
- }
- public int RemoveWhere (Predicate<T> predicate)
- {
- if (predicate == null)
- throw new ArgumentNullException ("predicate");
- int counter = 0;
- var copy = new T [count];
- CopyTo (copy, 0);
- foreach (var item in copy) {
- if (predicate (item)) {
- Remove (item);
- counter++;
- }
- }
- return counter;
- }
- public void TrimExcess ()
- {
- Resize ();
- }
- // set operations
- public void IntersectWith (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- var copy = new T [count];
- CopyTo (copy, 0);
- foreach (var item in copy)
- if (!other.Contains (item))
- Remove (item);
- foreach (var item in other)
- if (!Contains (item))
- Remove (item);
- }
- public void ExceptWith (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in other)
- Remove (item);
- }
- public bool Overlaps (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in other)
- if (Contains (item))
- return true;
- return false;
- }
- public bool SetEquals (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- if (count != other.Count ())
- return false;
- foreach (var item in this)
- if (!other.Contains (item))
- return false;
- return true;
- }
- public void SymmetricExceptWith (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in other) {
- if (Contains (item))
- Remove (item);
- else
- Add (item);
- }
- }
- public void UnionWith (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in other)
- Add (item);
- }
- bool CheckIsSubsetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in this)
- if (!other.Contains (item))
- return false;
- return true;
- }
- public bool IsSubsetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- if (count == 0)
- return true;
- if (count > other.Count ())
- return false;
- return CheckIsSubsetOf (other);
- }
- public bool IsProperSubsetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- if (count == 0)
- return true;
- if (count >= other.Count ())
- return false;
- return CheckIsSubsetOf (other);
- }
- bool CheckIsSupersetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- foreach (var item in other)
- if (!Contains (item))
- return false;
- return true;
- }
- public bool IsSupersetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- if (count < other.Count ())
- return false;
- return CheckIsSupersetOf (other);
- }
- public bool IsProperSupersetOf (IEnumerable<T> other)
- {
- if (other == null)
- throw new ArgumentNullException ("other");
- if (count <= other.Count ())
- return false;
- return CheckIsSupersetOf (other);
- }
- [MonoTODO]
- public static IEqualityComparer<HashSet<T>> CreateSetComparer ()
- {
- throw new NotImplementedException ();
- }
- [MonoTODO]
- [SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
- public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
- {
- throw new NotImplementedException ();
- }
- [MonoTODO]
- public virtual void OnDeserialization (object sender)
- {
- if (si == null)
- return;
- throw new NotImplementedException ();
- }
- IEnumerator<T> IEnumerable<T>.GetEnumerator ()
- {
- return new Enumerator (this);
- }
- bool ICollection<T>.IsReadOnly {
- get { return false; }
- }
- void ICollection<T>.CopyTo (T [] array, int index)
- {
- CopyTo (array, index);
- }
- void ICollection<T>.Add (T item)
- {
- if (!Add (item))
- throw new ArgumentException ();
- }
- IEnumerator IEnumerable.GetEnumerator ()
- {
- return new Enumerator (this);
- }
- public Enumerator GetEnumerator ()
- {
- return new Enumerator (this);
- }
- [Serializable]
- public struct Enumerator : IEnumerator<T>, IDisposable {
- HashSet<T> hashset;
- int index, current;
- int stamp;
- internal Enumerator (HashSet<T> hashset)
- {
- this.hashset = hashset;
- this.stamp = hashset.generation;
- index = -1;
- current = NO_SLOT;
- }
- public bool MoveNext ()
- {
- CheckState ();
- do {
- if (current != NO_SLOT) {
- current = hashset.links [current].Next;
- continue;
- }
- if (index + 1 >= hashset.table.Length)
- return false;
- current = hashset.table [++index] - 1;;
- } while (current == NO_SLOT);
- return true;
- }
- public T Current {
- get {
- CheckCurrent ();
- return hashset.slots [current];
- }
- }
- object IEnumerator.Current {
- get { return this.Current; }
- }
- void IEnumerator.Reset ()
- {
- index = -1;
- current = NO_SLOT;
- }
- public void Dispose ()
- {
- hashset = null;
- }
- void CheckState ()
- {
- if (hashset == null)
- throw new ObjectDisposedException (null);
- if (hashset.generation != stamp)
- throw new InvalidOperationException ("HashSet have been modified while it was iterated over");
- }
- void CheckCurrent ()
- {
- CheckState ();
- if (current == NO_SLOT)
- throw new InvalidOperationException ("Current is not valid");
- }
- }
- // borrowed from System.Collections.HashTable
- static class PrimeHelper {
- static readonly int [] primes_table = {
- 11,
- 19,
- 37,
- 73,
- 109,
- 163,
- 251,
- 367,
- 557,
- 823,
- 1237,
- 1861,
- 2777,
- 4177,
- 6247,
- 9371,
- 14057,
- 21089,
- 31627,
- 47431,
- 71143,
- 106721,
- 160073,
- 240101,
- 360163,
- 540217,
- 810343,
- 1215497,
- 1823231,
- 2734867,
- 4102283,
- 6153409,
- 9230113,
- 13845163
- };
- static bool TestPrime (int x)
- {
- if ((x & 1) != 0) {
- int top = (int) Math.Sqrt (x);
- for (int n = 3; n < top; n += 2) {
- if ((x % n) == 0)
- return false;
- }
- return true;
- }
- // There is only one even prime - 2.
- return x == 2;
- }
- static int CalcPrime (int x)
- {
- for (int i = (x & (~1)) - 1; i < Int32.MaxValue; i += 2)
- if (TestPrime (i))
- return i;
- return x;
- }
- public static int ToPrime (int x)
- {
- for (int i = 0; i < primes_table.Length; i++)
- if (x <= primes_table [i])
- return primes_table [i];
- return CalcPrime (x);
- }
- }
- }
- }
|