Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / C5 / hashing / HashTable.cs @ 3

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
    uint randomhashfactor = (2 * (uint)(new Random()).Next() + 1) * 1529784659;
132
#endif
133
#endif
134

    
135
    #endregion
136

    
137
    #region Events
138

    
139
    /// <summary>
140
    /// 
141
    /// </summary>
142
    /// <value></value>
143
    public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
144

    
145
    #endregion
146

    
147
    #region Bucket nested class(es)
148
#if REFBUCKET
149
    [Serializable]
150
    class Bucket
151
    {
152
      internal T item;
153

    
154
      internal int hashval; //Cache!
155

    
156
#if LINEARPROBING
157
      internal Bucket(T item, int hashval)
158
      {
159
        this.item = item;
160
        this.hashval = hashval;
161
      }
162
#else
163
      internal Bucket overflow;
164

    
165
      internal Bucket(T item, int hashval, Bucket overflow)
166
      {
167
        this.item = item;
168
        this.hashval = hashval;
169
        this.overflow = overflow;
170
      }
171
#endif
172
    }
173
#else
174
    struct Bucket
175
    {
176
      internal T item;
177

    
178
      internal int hashval; //Cache!
179

    
180
#if LINEARPROBING
181
      internal Bucket(T item, int hashval)
182
      {
183
        this.item = item;
184
        this.hashval = hashval;
185
      }
186
#else
187
			internal OverflowBucket overflow;
188

    
189

    
190
			internal Bucket(T item, int hashval)
191
			{
192
				this.item = item;
193
				this.hashval = hashval;
194
				this.overflow = default(OverflowBucket);
195
			}
196
#endif
197
    }
198

    
199

    
200
#if !LINEARPROBING
201
		class OverflowBucket
202
		{
203
			internal T item;
204

    
205
			internal int hashval; //Cache!
206

    
207
			internal OverflowBucket overflow;
208

    
209

    
210
			internal OverflowBucket(T item, int hashval, OverflowBucket overflow)
211
			{
212
				this.item = item;
213
				this.hashval = hashval;
214
				this.overflow = overflow;
215
			}
216
		}
217
#endif
218
#endif
219

    
220
    #endregion
221

    
222
    #region Basic Util
223

    
224
    bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
225

    
226
#if !REFBUCKET
227
    bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); }
228
#endif
229

    
230
    int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); }
231

    
232

    
233
    int hv2i(int hashval)
234
    {
235
#if INTERHASHING
236
			//Note: *inverse  mod 2^32 is -1503427877
237
			return (int)(((uint)hashval * 1529784659) >>bitsc); 
238
#elif RANDOMINTERHASHING
239
      return (int)(((uint)hashval * randomhashfactor) >> bitsc);
240
#else
241
			return indexmask & hashval;
242
#endif
243
    }
244

    
245

    
246
    void expand()
247
    {
248
      //Console.WriteLine(String.Format("Expand to {0} bits", bits+1));
249
      resize(bits + 1);
250
    }
251

    
252

    
253
    void shrink()
254
    {
255
      if (bits > 3)
256
      {
257
        //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1));
258
        resize(bits - 1);
259
      }
260
    }
261

    
262

    
263
    void resize(int bits)
264
    {
265
      //Console.WriteLine(String.Format("Resize to {0} bits", bits));
266
      this.bits = bits;
267
      bitsc = 32 - bits;
268
      indexmask = (1 << bits) - 1;
269

    
270
      Bucket[] newtable = new Bucket[indexmask + 1];
271

    
272
      for (int i = 0, s = table.Length; i < s; i++)
273
      {
274
        Bucket b = table[i];
275

    
276
#if LINEARPROBING
277
#if REFBUCKET
278
        if (b != null)
279
        {
280
          int j = hv2i(b.hashval);
281

    
282
          while (newtable[j] != null) { j = indexmask & (j + 1); }
283

    
284
          newtable[j] = b;
285
        }
286
#else
287
        if (!isnull(b.item))
288
        {
289
          int j = hv2i(b.hashval);
290

    
291
          while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); }
292

    
293
          newtable[j] = b;
294
        }
295
#endif
296
#else
297
#if REFBUCKET
298
        while (b != null)
299
        {
300
          int j = hv2i(b.hashval);
301

    
302
          newtable[j] = new Bucket(b.item, b.hashval, newtable[j]);
303
          b = b.overflow;
304
        }
305
#else
306
				if (!isnull(b.item))
307
				{
308
					insert(b.item, b.hashval, newtable);
309

    
310
					OverflowBucket ob = b.overflow;
311

    
312
					while (ob != null)
313
					{
314
						insert(ob.item, ob.hashval, newtable);
315
						ob = ob.overflow;
316
					}
317
				}
318
#endif
319
#endif
320
      }
321

    
322
      table = newtable;
323
      resizethreshhold = (int)(table.Length * fillfactor);
324
      //Console.WriteLine(String.Format("Resize to {0} bits done", bits));
325
    }
326

    
327
#if REFBUCKET
328
#else
329
#if LINEARPROBING
330
#else
331
		//Only for resize!!!
332
		private void insert(T item, int hashval, Bucket[] t)
333
		{
334
			int i = hv2i(hashval);
335
			Bucket b = t[i];
336

    
337
			if (!isnull(b.item))
338
			{
339
				t[i].overflow = new OverflowBucket(item, hashval, b.overflow);
340
			}
341
			else
342
				t[i] = new Bucket(item, hashval);
343
		}
344
#endif
345
#endif
346

    
347
    /// <summary>
348
    /// Search for an item equal (according to itemequalityComparer) to the supplied item.  
349
    /// </summary>
350
    /// <param name="item"></param>
351
    /// <param name="add">If true, add item to table if not found.</param>
352
    /// <param name="update">If true, update table entry if item found.</param>
353
    /// <param name="raise">If true raise events</param>
354
    /// <returns>True if found</returns>
355
    private bool searchoradd(ref T item, bool add, bool update, bool raise)
356
    {
357

    
358
#if LINEARPROBING
359
#if REFBUCKET
360
      int hashval = gethashcode(item);
361
      int i = hv2i(hashval);
362
      Bucket b = table[i];
363

    
364
      while (b != null)
365
      {
366
        T olditem = b.item;
367
        if (equals(olditem, item))
368
        {
369
          if (update)
370
            b.item = item;
371
          else
372
            item = olditem;
373

    
374
          if (raise && update)
375
            raiseForUpdate(item, olditem);
376
          return true;
377
        }
378

    
379
        b = table[i = indexmask & (i + 1)];
380
      }
381

    
382
      if (!add) goto notfound;
383

    
384
      table[i] = new Bucket(item, hashval);
385

    
386
#else
387
      if (isnull(item))
388
      {
389
        if (defaultvalid)
390
        {
391
          T olditem = defaultitem;
392
          if (update)
393
            defaultitem = item;
394
          else
395
            item = defaultitem;
396

    
397
          if (raise && update)
398
            raiseForUpdate(item, olditem);
399
          return true;
400
        }
401

    
402
        if (!add) goto notfound;
403

    
404
        defaultvalid = true;
405
        defaultitem = item;
406
      }
407
      else
408
      {
409
        int hashval = gethashcode(item);
410
        int i = hv2i(hashval);
411
        T t = table[i].item;
412

    
413
        while (!isnull(t))
414
        {
415
          if (equals(t, item))
416
          {
417
            if (update)
418
              table[i].item = item;
419
            else
420
              item = t;
421

    
422
            if (raise && update)
423
              raiseForUpdate(item, t);
424
            return true;
425
          }
426

    
427
          t = table[i = indexmask & (i + 1)].item;
428
        }
429

    
430
        if (!add) goto notfound;
431

    
432
        table[i] = new Bucket(item, hashval);
433
      }
434
#endif
435
#else
436
#if REFBUCKET
437
      int hashval = gethashcode(item);
438
      int i = hv2i(hashval);
439
      Bucket b = table[i], bold = null;
440

    
441
      if (b != null)
442
      {
443
        while (b != null)
444
        {
445
          T olditem = b.item;
446
          if (equals(olditem, item))
447
          {
448
            if (update)
449
            {
450
              b.item = item;
451
            }
452
            if (raise && update)
453
              raiseForUpdate(item, olditem);
454
            // bug20071112:
455
            item = olditem;
456
            return true;
457
          }
458

    
459
          bold = b;
460
          b = b.overflow;
461
        }
462

    
463
        if (!add) goto notfound;
464

    
465
        bold.overflow = new Bucket(item, hashval, null);
466
      }
467
      else
468
      {
469
        if (!add) goto notfound;
470

    
471
        table[i] = new Bucket(item, hashval, null);
472
      }
473
#else
474
			if (isnull(item))
475
			{
476
        if (defaultvalid)
477
        {
478
          T olditem = defaultitem;
479
          if (update)
480
            defaultitem = item;
481
          else
482
            item = defaultitem;
483

    
484
          if (raise && update)
485
            raiseForUpdate(item, olditem);
486
          return true;
487
        }
488

    
489
				if (!add) goto notfound;
490

    
491
				defaultvalid = true;
492
				defaultitem = item;
493
			}
494
			else
495
			{
496
				int hashval = gethashcode(item);
497
				int i = hv2i(hashval);
498
				Bucket b = table[i];
499

    
500
				if (!isnull(b.item))
501
				{
502
					if (equals(b.item, item))
503
					{
504
						if (update)
505
							table[i].item = item;
506
						else
507
							item = b.item;
508

    
509
            if (raise && update)
510
              raiseForUpdate(item, b.item);
511
            return true;
512
					}
513

    
514
					OverflowBucket ob = table[i].overflow;
515

    
516
					if (ob == null)
517
					{
518
						if (!add) goto notfound;
519

    
520
						table[i].overflow = new OverflowBucket(item, hashval, null);
521
					}
522
					else
523
					{
524
            T olditem = ob.item;
525
						while (ob.overflow != null)
526
						{
527
              if (equals(item, olditem))
528
              {
529
                if (update)
530
                  ob.item = item;
531
                else
532
                  item = olditem;
533

    
534
                if (raise && update)
535
                  raiseForUpdate(item, olditem);
536
                return true;
537
              }
538

    
539
							ob = ob.overflow;
540
              olditem = ob.item;
541
						}
542

    
543
            if (equals(item, olditem))
544
            {
545
              if (update)
546
                ob.item = item;
547
              else
548
                item = olditem;
549

    
550
              if (raise && update)
551
                raiseForUpdate(item, olditem);
552
              return true;
553
            }
554

    
555
						if (!add) goto notfound;
556

    
557
						ob.overflow = new OverflowBucket(item, hashval, null);
558
					}
559
				}
560
				else
561
				{
562
					if (!add) goto notfound;
563

    
564
					table[i] = new Bucket(item, hashval);
565
				}
566
			}
567
#endif
568
#endif
569
      size++;
570
      if (size > resizethreshhold)
571
        expand();
572
    notfound:
573
      if (raise && add)
574
        raiseForAdd(item);
575
      if (update)
576
        item = default(T);
577
      return false;
578
    }
579

    
580

    
581
    private bool remove(ref T item)
582
    {
583

    
584
      if (size == 0)
585
        return false;
586
#if LINEARPROBING
587
#if REFBUCKET
588
      int hashval = gethashcode(item);
589
      int index = hv2i(hashval);
590
      Bucket b = table[index];
591

    
592
      while (b != null)
593
      {
594
        if (equals(item, b.item))
595
        {
596
          //ref
597
          item = table[index].item;
598
          table[index] = null;
599

    
600
          //Algorithm R
601
          int j = (index + 1) & indexmask;
602

    
603
          b = table[j];
604
          while (b != null)
605
          {
606
            int k = hv2i(b.hashval);
607

    
608
            if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
609
            //if (index > j ? (j < k && k <= index): (k <= index || j < k) )
610
            {
611
              table[index] = b;
612
              table[j] = null;
613
              index = j;
614
            }
615

    
616
            j = (j + 1) & indexmask;
617
            b = table[j];
618
          }
619

    
620
          goto found;
621
        }
622

    
623
        b = table[index = indexmask & (index + 1)];
624
      }
625
      return false;
626
#else
627
      if (isnull(item))
628
      {
629
        if (!defaultvalid)
630
          return false;
631

    
632
        //ref
633
        item = defaultitem;
634
        defaultvalid = false;
635
        defaultitem = default(T); //No spaceleaks!
636
      }
637
      else
638
      {
639
        int hashval = gethashcode(item);
640
        int index = hv2i(hashval);
641
        T t = table[index].item;
642

    
643
        while (!isnull(t))
644
        {
645
          if (equals(item, t))
646
          {
647
            //ref
648
            item = table[index].item;
649
            table[index].item = default(T);
650

    
651
            //algorithm R
652
            int j = (index + 1) & indexmask;
653
            Bucket b = table[j];
654

    
655
            while (!isnull(b.item))
656
            {
657
              int k = hv2i(b.hashval);
658

    
659
              if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
660
              {
661
                table[index] = b;
662
                table[j].item = default(T);
663
                index = j;
664
              }
665

    
666
              j = (j + 1) & indexmask;
667
              b = table[j];
668
            }
669

    
670
            goto found;
671
          }
672

    
673
          t = table[index = indexmask & (index + 1)].item;
674
        }
675

    
676
        return false;
677
      }
678
#endif
679
    found:
680
#else
681
#if REFBUCKET
682
      int hashval = gethashcode(item);
683
      int index = hv2i(hashval);
684
      Bucket b = table[index], bold;
685

    
686
      if (b == null)
687
        return false;
688

    
689
      if (equals(item, b.item))
690
      {
691
        //ref
692
        item = b.item;
693
        table[index] = b.overflow;
694
      }
695
      else
696
      {
697
        bold = b;
698
        b = b.overflow;
699
        while (b != null && !equals(item, b.item))
700
        {
701
          bold = b;
702
          b = b.overflow;
703
        }
704

    
705
        if (b == null)
706
          return false;
707

    
708
        //ref
709
        item = b.item;
710
        bold.overflow = b.overflow;
711
      }
712

    
713
#else
714
			if (isnull(item))
715
			{
716
				if (!defaultvalid)
717
					return false;
718

    
719
				//ref
720
				item = defaultitem;
721
				defaultvalid = false;
722
				defaultitem = default(T); //No spaceleaks!
723
			}
724
			else
725
			{
726
				int hashval = gethashcode(item);
727
				int index = hv2i(hashval);
728
				Bucket b = table[index];
729
				OverflowBucket ob = b.overflow;
730

    
731
				if (equals(item, b.item))
732
				{
733
					//ref
734
					item = b.item;
735
					if (ob == null)
736
					{
737
						table[index] = new Bucket();
738
					}
739
					else
740
					{
741
						b = new Bucket(ob.item, ob.hashval);
742
						b.overflow = ob.overflow;
743
						table[index] = b;
744
					}
745
				}
746
				else
747
				{
748
					if (ob == null)
749
						return false;
750

    
751
					if (equals(item, ob.item)) 
752
					{
753
						//ref
754
						item=ob.item;
755
						table[index].overflow = ob.overflow;
756
					}
757
					else
758
					{
759
						while (ob.overflow != null)
760
							if (equals(item, ob.overflow.item))
761
							{
762
								//ref
763
								item = ob.overflow.item;
764
								break;
765
							}
766
							else
767
								ob = ob.overflow;
768

    
769
						if (ob.overflow == null)
770
							return false;
771

    
772
						ob.overflow = ob.overflow.overflow;
773
					}
774
				}
775
			}
776
#endif
777
#endif
778
      size--;
779

    
780
      return true;
781
    }
782

    
783

    
784
    private void clear()
785
    {
786
      bits = origbits;
787
      bitsc = 32 - bits;
788
      indexmask = (1 << bits) - 1;
789
      size = 0;
790
      table = new Bucket[indexmask + 1];
791
      resizethreshhold = (int)(table.Length * fillfactor);
792
#if !REFBUCKET
793
      defaultitem = default(T);
794
      defaultvalid = false;
795
#endif
796
    }
797

    
798
    #endregion
799

    
800
    #region Constructors
801
    /// <summary>
802
    /// Create a hash set with natural item equalityComparer and default fill threshold (66%)
803
    /// and initial table size (16).
804
    /// </summary>
805
    public HashSet()
806
      : this(EqualityComparer<T>.Default) { }
807

    
808

    
809
    /// <summary>
810
    /// Create a hash set with external item equalityComparer and default fill threshold (66%)
811
    /// and initial table size (16).
812
    /// </summary>
813
    /// <param name="itemequalityComparer">The external item equalityComparer</param>
814
    public HashSet(SCG.IEqualityComparer<T> itemequalityComparer)
815
      : this(16, itemequalityComparer) { }
816

    
817

    
818
    /// <summary>
819
    /// Create a hash set with external item equalityComparer and default fill threshold (66%)
820
    /// </summary>
821
    /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
822
    /// <param name="itemequalityComparer">The external item equalityComparer</param>
823
    public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
824
      : this(capacity, 0.66, itemequalityComparer) { }
825

    
826

    
827
    /// <summary>
828
    /// Create a hash set with external item equalityComparer.
829
    /// </summary>
830
    /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
831
    /// <param name="fill">Fill threshold (in range 10% to 90%)</param>
832
    /// <param name="itemequalityComparer">The external item equalityComparer</param>
833
    public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer)
834
      : base(itemequalityComparer)
835
    {
836
      if (fill < 0.1 || fill > 0.9)
837
        throw new ArgumentException("Fill outside valid range [0.1, 0.9]");
838
      if (capacity <= 0)
839
        throw new ArgumentException("Capacity must be non-negative");
840
      //this.itemequalityComparer = itemequalityComparer;
841
      origbits = 4;
842
      while (capacity - 1 >> origbits > 0) origbits++;
843
      clear();
844
    }
845

    
846

    
847

    
848
    #endregion
849

    
850
    #region IEditableCollection<T> Members
851

    
852
    /// <summary>
853
    /// The complexity of the Contains operation
854
    /// </summary>
855
    /// <value>Always returns Speed.Constant</value>
856
    [Tested]
857
    public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } }
858

    
859
    /// <summary>
860
    /// Check if an item is in the set 
861
    /// </summary>
862
    /// <param name="item">The item to look for</param>
863
    /// <returns>True if set contains item</returns>
864
    [Tested]
865
    public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); }
866

    
867

    
868
    /// <summary>
869
    /// Check if an item (collection equal to a given one) is in the set and
870
    /// if so report the actual item object found.
871
    /// </summary>
872
    /// <param name="item">On entry, the item to look for.
873
    /// On exit the item found, if any</param>
874
    /// <returns>True if set contains item</returns>
875
    [Tested]
876
    public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); }
877

    
878

    
879
    /// <summary>
880
    /// Check if an item (collection equal to a given one) is in the set and
881
    /// if so replace the item object in the set with the supplied one.
882
    /// </summary>
883
    /// <param name="item">The item object to update with</param>
884
    /// <returns>True if item was found (and updated)</returns>
885
    [Tested]
886
    public virtual bool Update(T item)
887
    { updatecheck(); return searchoradd(ref item, false, true, true); }
888

    
889
    /// <summary>
890
    /// Check if an item (collection equal to a given one) is in the set and
891
    /// if so replace the item object in the set with the supplied one.
892
    /// </summary>
893
    /// <param name="item">The item object to update with</param>
894
    /// <param name="olditem"></param>
895
    /// <returns>True if item was found (and updated)</returns>
896
    public virtual bool Update(T item, out T olditem)
897
    { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); }
898

    
899

    
900
    /// <summary>
901
    /// Check if an item (collection equal to a given one) is in the set.
902
    /// If found, report the actual item object in the set,
903
    /// else add the supplied one.
904
    /// </summary>
905
    /// <param name="item">On entry, the item to look for or add.
906
    /// On exit the actual object found, if any.</param>
907
    /// <returns>True if item was found</returns>
908
    [Tested]
909
    public virtual bool FindOrAdd(ref T item)
910
    { updatecheck(); return searchoradd(ref item, true, false, true); }
911

    
912

    
913
    /// <summary>
914
    /// Check if an item (collection equal to a supplied one) is in the set and
915
    /// if so replace the item object in the set with the supplied one; else
916
    /// add the supplied one.
917
    /// </summary>
918
    /// <param name="item">The item to look for and update or add</param>
919
    /// <returns>True if item was updated</returns>
920
    [Tested]
921
    public virtual bool UpdateOrAdd(T item)
922
    { updatecheck(); return searchoradd(ref item, true, true, true); }
923

    
924

    
925
    /// <summary>
926
    /// Check if an item (collection equal to a supplied one) is in the set and
927
    /// if so replace the item object in the set with the supplied one; else
928
    /// add the supplied one.
929
    /// </summary>
930
    /// <param name="item">The item to look for and update or add</param>
931
    /// <param name="olditem"></param>
932
    /// <returns>True if item was updated</returns>
933
    public virtual bool UpdateOrAdd(T item, out T olditem)
934
    { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); }
935

    
936

    
937
    /// <summary>
938
    /// Remove an item from the set
939
    /// </summary>
940
    /// <param name="item">The item to remove</param>
941
    /// <returns>True if item was (found and) removed </returns>
942
    [Tested]
943
    public virtual bool Remove(T item)
944
    {
945
      updatecheck();
946
      if (remove(ref item))
947
      {
948
#if SHRINK
949
				if (size<resizethreshhold/2 && resizethreshhold>8)
950
					shrink();
951
#endif
952
        raiseForRemove(item);
953
        return true;
954
      }
955
      else
956
        return false;
957
    }
958

    
959

    
960
    /// <summary>
961
    /// Remove an item from the set, reporting the actual matching item object.
962
    /// </summary>
963
    /// <param name="item">The value to remove.</param>
964
    /// <param name="removeditem">The removed value.</param>
965
    /// <returns>True if item was found.</returns>
966
    [Tested]
967
    public virtual bool Remove(T item, out T removeditem)
968
    {
969
      updatecheck();
970
      removeditem = item;
971
      if (remove(ref removeditem))
972
      {
973
#if SHRINK
974
				if (size<resizethreshhold/2 && resizethreshhold>8)
975
					shrink();
976
#endif
977
        raiseForRemove(removeditem);
978
        return true;
979
      }
980
      else
981
        return false;
982
    }
983

    
984

    
985
    /// <summary>
986
    /// Remove all items in a supplied collection from this set.
987
    /// </summary>
988
    /// <typeparam name="U"></typeparam>
989
    /// <param name="items">The items to remove.</param>
990
    [Tested]
991
    public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
992
    {
993
      updatecheck();
994
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this);
995
      bool raise = raiseHandler.MustFire;
996
      T jtem;
997
      foreach (U item in items)
998
      { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); }
999
#if SHRINK
1000
			if (size < resizethreshhold / 2 && resizethreshhold > 16)
1001
			{
1002
				int newlength = table.Length;
1003

    
1004
				while (newlength >= 32 && newlength * fillfactor / 2 > size)
1005
					newlength /= 2;
1006

    
1007
				resize(newlength - 1);
1008
			}
1009
#endif
1010
      if (raise) raiseHandler.Raise();
1011
    }
1012

    
1013
    /// <summary>
1014
    /// Remove all items from the set, resetting internal table to initial size.
1015
    /// </summary>
1016
    [Tested]
1017
    public virtual void Clear()
1018
    {
1019
      updatecheck();
1020
      int oldsize = size;
1021
      clear();
1022
      if (ActiveEvents != 0 && oldsize > 0)
1023
      {
1024
        raiseCollectionCleared(true, oldsize);
1025
        raiseCollectionChanged();
1026
      }
1027
    }
1028

    
1029

    
1030
    /// <summary>
1031
    /// Remove all items *not* in a supplied collection from this set.
1032
    /// </summary>
1033
    /// <typeparam name="U"></typeparam>
1034
    /// <param name="items">The items to retain</param>
1035
    [Tested]
1036
    public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
1037
    {
1038
      updatecheck();
1039

    
1040
      HashSet<T> aux = new HashSet<T>(EqualityComparer);
1041

    
1042
      //This only works for sets:
1043
      foreach (U item in items)
1044
        if (Contains(item))
1045
        {
1046
          T jtem = item;
1047

    
1048
          aux.searchoradd(ref jtem, true, false, false);
1049
        }
1050

    
1051
      if (size == aux.size)
1052
        return;
1053

    
1054
      CircularQueue<T> wasRemoved = null;
1055
      if ((ActiveEvents & EventTypeEnum.Removed) != 0)
1056
      {
1057
        wasRemoved = new CircularQueue<T>();
1058
        foreach (T item in this)
1059
          if (!aux.Contains(item))
1060
            wasRemoved.Enqueue(item);
1061
      }
1062

    
1063
      table = aux.table;
1064
      size = aux.size;
1065
#if !REFBUCKET
1066
      defaultvalid = aux.defaultvalid;
1067
      defaultitem = aux.defaultitem;
1068
#endif
1069
      indexmask = aux.indexmask;
1070
      resizethreshhold = aux.resizethreshhold;
1071
      bits = aux.bits;
1072
      bitsc = aux.bitsc;
1073
#if DEBUG
1074
#else
1075
      randomhashfactor = aux.randomhashfactor;
1076
#endif
1077

    
1078
      if ((ActiveEvents & EventTypeEnum.Removed) != 0)
1079
        raiseForRemoveAll(wasRemoved);
1080
      else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
1081
        raiseCollectionChanged();
1082
    }
1083

    
1084
    /// <summary>
1085
    /// Check if all items in a supplied collection is in this set
1086
    /// (ignoring multiplicities). 
1087
    /// </summary>
1088
    /// <param name="items">The items to look for.</param>
1089
    /// <typeparam name="U"></typeparam>
1090
    /// <returns>True if all items are found.</returns>
1091
    [Tested]
1092
    public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
1093
    {
1094
      foreach (U item in items)
1095
        if (!Contains(item))
1096
          return false;
1097
      return true;
1098
    }
1099

    
1100

    
1101
    /// <summary>
1102
    /// Create an array containing all items in this set (in enumeration order).
1103
    /// </summary>
1104
    /// <returns>The array</returns>
1105
    [Tested]
1106
    public override T[] ToArray()
1107
    {
1108
      T[] res = new T[size];
1109
      int index = 0;
1110

    
1111
#if !REFBUCKET
1112
      if (defaultvalid)
1113
        res[index++] = defaultitem;
1114
#endif
1115
      for (int i = 0; i < table.Length; i++)
1116
      {
1117
        Bucket b = table[i];
1118
#if LINEARPROBING
1119
#if REFBUCKET
1120
        if (b != null)
1121
          res[index++] = b.item;
1122
#else
1123
        if (!isnull(b.item))
1124
          res[index++] = b.item;
1125
#endif
1126
#else
1127
#if REFBUCKET
1128
        while (b != null)
1129
        {
1130
          res[index++] = b.item;
1131
          b = b.overflow;
1132
        }
1133
#else
1134
				if (!isnull(b.item))
1135
				{
1136
					res[index++] = b.item;
1137

    
1138
					OverflowBucket ob = b.overflow;
1139

    
1140
					while (ob != null)
1141
					{
1142
						res[index++] = ob.item;
1143
						ob = ob.overflow;
1144
					}
1145
				}
1146
#endif
1147
#endif
1148
      }
1149

    
1150
      Debug.Assert(size == index);
1151
      return res;
1152
    }
1153

    
1154

    
1155
    /// <summary>
1156
    /// Count the number of times an item is in this set (either 0 or 1).
1157
    /// </summary>
1158
    /// <param name="item">The item to look for.</param>
1159
    /// <returns>1 if item is in set, 0 else</returns>
1160
    [Tested]
1161
    public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; }
1162

    
1163
    /// <summary>
1164
    /// 
1165
    /// </summary>
1166
    /// <returns></returns>
1167
    public virtual ICollectionValue<T> UniqueItems() { return this; }
1168

    
1169
    /// <summary>
1170
    /// 
1171
    /// </summary>
1172
    /// <returns></returns>
1173
    public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
1174
    {
1175
      return new MultiplicityOne<T>(this);
1176
    }
1177

    
1178
    /// <summary>
1179
    /// Remove all (at most 1) copies of item from this set.
1180
    /// </summary>
1181
    /// <param name="item">The item to remove</param>
1182
    [Tested]
1183
    public virtual void RemoveAllCopies(T item) { Remove(item); }
1184

    
1185
    #endregion
1186

    
1187
    #region IEnumerable<T> Members
1188

    
1189

    
1190
    /// <summary>
1191
    /// Choose some item of this collection. 
1192
    /// </summary>
1193
    /// <exception cref="NoSuchItemException">if collection is empty.</exception>
1194
    /// <returns></returns>
1195
    [Tested]
1196
    public override T Choose()
1197
    {
1198
      int len = table.Length;
1199
      if (size == 0)
1200
        throw new NoSuchItemException();
1201
#if REFBUCKET
1202
      do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null);
1203
#else
1204
      if (defaultvalid) return defaultitem;
1205
      do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item));
1206
#endif
1207
      return table[lastchosen].item;
1208
    }
1209

    
1210
    /// <summary>
1211
    /// Create an enumerator for this set.
1212
    /// </summary>
1213
    /// <returns>The enumerator</returns>
1214
    [Tested]
1215
    public override SCG.IEnumerator<T> GetEnumerator()
1216
    {
1217
      int index = -1;
1218
      int mystamp = stamp;
1219
      int len = table.Length;
1220

    
1221
#if LINEARPROBING
1222
#if REFBUCKET
1223
      while (++index < len)
1224
      {
1225
        if (mystamp != stamp) throw new CollectionModifiedException();
1226

    
1227
        if (table[index] != null) yield return table[index].item;
1228
      }
1229
#else
1230
      if (defaultvalid)
1231
        yield return defaultitem;
1232

    
1233
      while (++index < len)
1234
      {
1235
        if (mystamp != stamp) throw new CollectionModifiedException();
1236

    
1237
        T item = table[index].item;
1238

    
1239
        if (!isnull(item)) yield return item;
1240
      }
1241
#endif
1242
#else
1243
#if REFBUCKET
1244
      Bucket b = null;
1245
#else
1246
			OverflowBucket ob = null;
1247

    
1248
			if (defaultvalid)
1249
				yield return defaultitem;
1250
#endif
1251
      while (true)
1252
      {
1253
        if (mystamp != stamp)
1254
          throw new CollectionModifiedException();
1255

    
1256
#if REFBUCKET
1257
        if (b == null || b.overflow == null)
1258
        {
1259
          do
1260
          {
1261
            if (++index >= len) yield break;
1262
          } while (table[index] == null);
1263

    
1264
          b = table[index];
1265
          yield return b.item;
1266
        }
1267
        else
1268
        {
1269
          b = b.overflow;
1270
          yield return b.item;
1271
        }
1272
#else
1273
				if (ob != null && ob.overflow != null)
1274
				{
1275
					ob = ob.overflow;
1276
					yield return ob.item;
1277
				}
1278
				else if (index >= 0 && ob == null && (ob = table[index].overflow) != null)
1279
				{
1280
					yield return  ob.item;
1281
				}
1282
				else
1283
				{
1284
					do
1285
					{
1286
						if (++index >= len) yield break;
1287
					} while (isnull(table[index].item));
1288

    
1289
          yield return table[index].item;
1290
          ob = null;
1291
				}
1292
#endif
1293
      }
1294
#endif
1295
    }
1296

    
1297
    #endregion
1298

    
1299
    #region ISink<T> Members
1300
    /// <summary>
1301
    /// Report if this is a set collection.
1302
    /// </summary>
1303
    /// <value>Always false</value>
1304
    [Tested]
1305
    public virtual bool AllowsDuplicates { [Tested]get { return false; } }
1306

    
1307
    /// <summary>
1308
    /// By convention this is true for any collection with set semantics.
1309
    /// </summary>
1310
    /// <value>True if only one representative of a group of equal items 
1311
    /// is kept in the collection together with the total count.</value>
1312
    public virtual bool DuplicatesByCounting { get { return true; } }
1313

    
1314
    /// <summary>
1315
    /// Add an item to this set.
1316
    /// </summary>
1317
    /// <param name="item">The item to add.</param>
1318
    /// <returns>True if item was added (i.e. not found)</returns>
1319
    [Tested]
1320
    public virtual bool Add(T item)
1321
    {
1322
      updatecheck();
1323
      return !searchoradd(ref item, true, false, true);
1324
    }
1325

    
1326
    /// <summary>
1327
    /// Add an item to this set.
1328
    /// </summary>
1329
    /// <param name="item">The item to add.</param>
1330
    [Tested]
1331
    void SCG.ICollection<T>.Add(T item)
1332
    {
1333
        Add(item);
1334
    }
1335

    
1336
    /// <summary>
1337
    /// Add the elements from another collection with a more specialized item type 
1338
    /// to this collection. Since this
1339
    /// collection has set semantics, only items not already in the collection
1340
    /// will be added.
1341
    /// </summary>
1342
    /// <typeparam name="U">The type of items to add</typeparam>
1343
    /// <param name="items">The items to add</param>
1344
    [Tested]
1345
    public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
1346
    {
1347
      updatecheck();
1348
      bool wasChanged = false;
1349
      bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
1350
      CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null;
1351
      foreach (T item in items)
1352
      {
1353
        T jtem = item;
1354

    
1355
        if (!searchoradd(ref jtem, true, false, false))
1356
        {
1357
          wasChanged = true;
1358
          if (raiseAdded)
1359
            wasAdded.Enqueue(item);
1360
        }
1361
      }
1362
      //TODO: implement a RaiseForAddAll() method
1363
      if (raiseAdded & wasChanged)
1364
        foreach (T item in wasAdded)
1365
          raiseItemsAdded(item, 1);
1366
      if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged))
1367
        raiseCollectionChanged();
1368
    }
1369

    
1370

    
1371
    #endregion
1372

    
1373
    #region Diagnostics
1374

    
1375
    /// <summary>
1376
    /// Test internal structure of data (invariants)
1377
    /// </summary>
1378
    /// <returns>True if pass</returns>
1379
    [Tested]
1380
    public virtual bool Check()
1381
    {
1382
      int count = 0;
1383
#if LINEARPROBING
1384
      int lasthole = table.Length - 1;
1385

    
1386
#if REFBUCKET
1387
      while (lasthole >= 0 && table[lasthole] != null)
1388
#else
1389
      while (lasthole >= 0 && !isnull(table[lasthole].item))
1390
#endif
1391
      {
1392
        lasthole--;
1393
        count++;
1394
      }
1395

    
1396
      if (lasthole < 0)
1397
      {
1398
        Console.WriteLine("Table is completely filled!");
1399
        return false;
1400
      }
1401

    
1402
      for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++)
1403
      {
1404
        Bucket b = table[cellindex];
1405
        int hashindex = hv2i(b.hashval);
1406

    
1407
        if (hashindex <= lasthole || hashindex > cellindex)
1408
        {
1409
          Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole);
1410
          return false;
1411
        }
1412
      }
1413

    
1414
      int latesthole = -1;
1415

    
1416
      for (int cellindex = 0; cellindex < lasthole; cellindex++)
1417
      {
1418
        Bucket b = table[cellindex];
1419

    
1420
#if REFBUCKET
1421
        if (b != null)
1422
#else
1423
        if (!isnull(b.item))
1424
#endif
1425
        {
1426
          count++;
1427

    
1428
          int hashindex = hv2i(b.hashval);
1429

    
1430
          if (cellindex < hashindex && hashindex <= lasthole)
1431
          {
1432
            Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
1433
            return false;
1434
          }
1435
        }
1436
        else
1437
        {
1438
          latesthole = cellindex;
1439
          break;
1440
        }
1441
      }
1442

    
1443
      for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++)
1444
      {
1445
        Bucket b = table[cellindex];
1446

    
1447
#if REFBUCKET
1448
        if (b != null)
1449
#else
1450
        if (!isnull(b.item))
1451
#endif
1452
        {
1453
          count++;
1454

    
1455
          int hashindex = hv2i(b.hashval);
1456

    
1457
          if (hashindex <= latesthole || cellindex < hashindex)
1458
          {
1459
            Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
1460
            return false;
1461
          }
1462
        }
1463
        else
1464
        {
1465
          latesthole = cellindex;
1466
        }
1467
      }
1468

    
1469
      return true;
1470
#else
1471
      bool retval = true;
1472

    
1473
      if (bitsc != 32 - bits)
1474
      {
1475
        Console.WriteLine("bitsc != 32 - bits ({0}, {1})", bitsc, bits);
1476
        retval = false;
1477
      }
1478
      if (indexmask != (1 << bits) - 1)
1479
      {
1480
        Console.WriteLine("indexmask != (1 << bits) - 1 ({0}, {1})", indexmask, bits);
1481
        retval = false;
1482
      }
1483
      if (table.Length != indexmask + 1)
1484
      {
1485
        Console.WriteLine("table.Length != indexmask + 1 ({0}, {1})", table.Length, indexmask);
1486
        retval = false;
1487
      }
1488
      if (bitsc != 32 - bits)
1489
      {
1490
        Console.WriteLine("resizethreshhold != (int)(table.Length * fillfactor) ({0}, {1}, {2})", resizethreshhold, table.Length, fillfactor);
1491
        retval = false;
1492
      }
1493

    
1494
      for (int i = 0, s = table.Length; i < s; i++)
1495
      {
1496
        int level = 0;
1497
        Bucket b = table[i];
1498
#if REFBUCKET
1499
        while (b != null)
1500
        {
1501
          if (i != hv2i(b.hashval))
1502
          {
1503
            Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
1504
            retval = false;
1505
          }
1506

    
1507
          count++;
1508
          level++;
1509
          b = b.overflow;
1510
        }
1511
#else
1512
				if (!isnull(b.item))
1513
				{
1514
					count++;
1515
					if (i != hv2i(b.hashval))
1516
					{
1517
						Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
1518
						retval = false;
1519
					}
1520

    
1521
					OverflowBucket ob = b.overflow;
1522

    
1523
					while (ob != null)
1524
					{
1525
						level++;
1526
						count++;
1527
						if (i != hv2i(ob.hashval))
1528
						{
1529
							Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
1530
							retval = false;
1531
						}
1532

    
1533
						ob = ob.overflow;
1534
					}
1535
				}
1536
#endif
1537
      }
1538

    
1539
      if (count != size)
1540
      {
1541
        Console.WriteLine("size({0}) != count({1})", size, count);
1542
        retval = false;
1543
      }
1544

    
1545
      return retval;
1546
#endif
1547
    }
1548

    
1549

    
1550
    /// <summary>
1551
    /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete.
1552
    /// </summary>
1553
    /// <returns>Histogram data.</returns>
1554
    [Tested(via = "Manually")]
1555
    public ISortedDictionary<int, int> BucketCostDistribution()
1556
    {
1557
      TreeDictionary<int, int> res = new TreeDictionary<int, int>();
1558
#if LINEARPROBING
1559
      int count = 0;
1560
#if REFBUCKET
1561
      while (table[count] != null)
1562
#else
1563
      while (!isnull(table[count].item))
1564
#endif
1565
        count++;
1566
      for (int i = table.Length - 1; i >= 0; i--)
1567
      {
1568
#if REFBUCKET
1569
        if (table[i] != null)
1570
#else
1571
        if (!isnull(table[i].item))
1572
#endif
1573
          count++;
1574
        else
1575
          count = 0;
1576
        if (res.Contains(count))
1577
          res[count]++;
1578
        else
1579
          res[count] = 1;
1580
      }
1581

    
1582
      return res;
1583
#else
1584
      for (int i = 0, s = table.Length; i < s; i++)
1585
      {
1586
        int count = 0;
1587
#if REFBUCKET
1588
        Bucket b = table[i];
1589

    
1590
        while (b != null)
1591
        {
1592
          count++;
1593
          b = b.overflow;
1594
        }
1595
#else
1596
				Bucket b = table[i];
1597

    
1598
				if (!isnull(b.item))
1599
				{
1600
					count = 1;
1601

    
1602
					OverflowBucket ob = b.overflow;
1603

    
1604
					while (ob != null)
1605
					{
1606
						count++;
1607
						ob = ob.overflow;
1608
					}
1609
				}
1610
#endif
1611
        if (res.Contains(count))
1612
          res[count]++;
1613
        else
1614
          res[count] = 1;
1615
      }
1616

    
1617
      return res;
1618
#endif
1619
    }
1620

    
1621
    #endregion
1622

    
1623
    #region ICloneable Members
1624

    
1625
    /// <summary>
1626
    /// Make a shallow copy of this HashSet.
1627
    /// </summary>
1628
    /// <returns></returns>
1629
    public virtual object Clone()
1630
    {
1631
      HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer);
1632
      //TODO: make sure this really adds in the counting bag way!
1633
      clone.AddAll(this);
1634
      return clone;
1635
    }
1636

    
1637
    #endregion
1638

    
1639
  }
1640
}