Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / C5 / Collections.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 IMPROVED_COLLECTION_HASHFUNCTION
23

    
24
using System;
25
using System.Diagnostics;
26
using SCG = System.Collections.Generic;
27
namespace C5
28
{
29
  /// <summary>
30
  /// A base class for implementing an IEnumerable&lt;T&gt;
31
  /// </summary>
32
  [Serializable]
33
  public abstract class EnumerableBase<T> : SCG.IEnumerable<T>
34
  {
35
    /// <summary>
36
    /// Create an enumerator for this collection.
37
    /// </summary>
38
    /// <returns>The enumerator</returns>
39
    public abstract SCG.IEnumerator<T> GetEnumerator();
40

    
41
    /// <summary>
42
    /// Count the number of items in an enumerable by enumeration
43
    /// </summary>
44
    /// <param name="items">The enumerable to count</param>
45
    /// <returns>The size of the enumerable</returns>
46
    [Tested]
47
    protected static int countItems(SCG.IEnumerable<T> items)
48
    {
49
      ICollectionValue<T> jtems = items as ICollectionValue<T>;
50

    
51
      if (jtems != null)
52
        return jtems.Count;
53

    
54
      int count = 0;
55

    
56
      using (SCG.IEnumerator<T> e = items.GetEnumerator())
57
        while (e.MoveNext()) count++;
58

    
59
      return count;
60
    }
61

    
62
    #region IEnumerable Members
63

    
64
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
65
    {
66
      return GetEnumerator();
67
    }
68

    
69
    #endregion
70
  }
71

    
72

    
73
  /// <summary>
74
  /// Base class for classes implementing ICollectionValue[T]
75
  /// </summary>
76
  [Serializable]
77
  public abstract class CollectionValueBase<T> : EnumerableBase<T>, ICollectionValue<T>, IShowable
78
  {
79
    #region Event handling
80
    EventBlock<T> eventBlock;
81
    /// <summary>
82
    /// 
83
    /// </summary>
84
    /// <value></value>
85
    public virtual EventTypeEnum ListenableEvents { get { return 0; } }
86

    
87
    /// <summary>
88
    /// A flag bitmap of the events currently subscribed to by this collection.
89
    /// </summary>
90
    /// <value></value>
91
    public virtual EventTypeEnum ActiveEvents { get { return eventBlock == null ? 0 : eventBlock.events; } }
92

    
93
    private void checkWillListen(EventTypeEnum eventType)
94
    {
95
      if ((ListenableEvents & eventType) == 0)
96
        throw new UnlistenableEventException();
97
    }
98

    
99
    /// <summary>
100
    /// The change event. Will be raised for every change operation on the collection.
101
    /// </summary>
102
    public virtual event CollectionChangedHandler<T> CollectionChanged
103
    {
104
      add { checkWillListen(EventTypeEnum.Changed); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionChanged += value; }
105
      remove
106
      {
107
        checkWillListen(EventTypeEnum.Changed);
108
        if (eventBlock != null)
109
        {
110
          eventBlock.CollectionChanged -= value;
111
          if (eventBlock.events == 0) eventBlock = null;
112
        }
113
      }
114
    }
115
    /// <summary>
116
    /// Fire the CollectionChanged event
117
    /// </summary>
118
    protected virtual void raiseCollectionChanged()
119
    { if (eventBlock != null) eventBlock.raiseCollectionChanged(this); }
120

    
121
    /// <summary>
122
    /// The clear event. Will be raised for every Clear operation on the collection.
123
    /// </summary>
124
    public virtual event CollectionClearedHandler<T> CollectionCleared
125
    {
126
      add { checkWillListen(EventTypeEnum.Cleared); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionCleared += value; }
127
      remove
128
      {
129
        checkWillListen(EventTypeEnum.Cleared);
130
        if (eventBlock != null)
131
        {
132
          eventBlock.CollectionCleared -= value;
133
          if (eventBlock.events == 0) eventBlock = null;
134
        }
135
      }
136
    }
137
    /// <summary>
138
    /// Fire the CollectionCleared event
139
    /// </summary>
140
    protected virtual void raiseCollectionCleared(bool full, int count)
141
    { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count); }
142

    
143
    /// <summary>
144
    /// Fire the CollectionCleared event
145
    /// </summary>
146
    protected virtual void raiseCollectionCleared(bool full, int count, int? offset)
147
    { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count, offset); }
148

    
149
    /// <summary>
150
    /// The item added  event. Will be raised for every individual addition to the collection.
151
    /// </summary>
152
    public virtual event ItemsAddedHandler<T> ItemsAdded
153
    {
154
      add { checkWillListen(EventTypeEnum.Added); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsAdded += value; }
155
      remove
156
      {
157
        checkWillListen(EventTypeEnum.Added);
158
        if (eventBlock != null)
159
        {
160
          eventBlock.ItemsAdded -= value;
161
          if (eventBlock.events == 0) eventBlock = null;
162
        }
163
      }
164
    }
165
    /// <summary>
166
    /// Fire the ItemsAdded event
167
    /// </summary>
168
    /// <param name="item">The item that was added</param>
169
    /// <param name="count"></param>
170
    protected virtual void raiseItemsAdded(T item, int count)
171
    { if (eventBlock != null) eventBlock.raiseItemsAdded(this, item, count); }
172

    
173
    /// <summary>
174
    /// The item removed event. Will be raised for every individual removal from the collection.
175
    /// </summary>
176
    public virtual event ItemsRemovedHandler<T> ItemsRemoved
177
    {
178
      add { checkWillListen(EventTypeEnum.Removed); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsRemoved += value; }
179
      remove
180
      {
181
        checkWillListen(EventTypeEnum.Removed);
182
        if (eventBlock != null)
183
        {
184
          eventBlock.ItemsRemoved -= value;
185
          if (eventBlock.events == 0) eventBlock = null;
186
        }
187
      }
188
    }
189
    /// <summary>
190
    /// Fire the ItemsRemoved event
191
    /// </summary>
192
    /// <param name="item">The item that was removed</param>
193
    /// <param name="count"></param>
194
    protected virtual void raiseItemsRemoved(T item, int count)
195
    { if (eventBlock != null) eventBlock.raiseItemsRemoved(this, item, count); }
196

    
197
    /// <summary>
198
    /// The item added  event. Will be raised for every individual addition to the collection.
199
    /// </summary>
200
    public virtual event ItemInsertedHandler<T> ItemInserted
201
    {
202
      add { checkWillListen(EventTypeEnum.Inserted); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemInserted += value; }
203
      remove
204
      {
205
        checkWillListen(EventTypeEnum.Inserted);
206
        if (eventBlock != null)
207
        {
208
          eventBlock.ItemInserted -= value;
209
          if (eventBlock.events == 0) eventBlock = null;
210
        }
211
      }
212
    }
213
    /// <summary>
214
    /// Fire the ItemInserted event
215
    /// </summary>
216
    /// <param name="item">The item that was added</param>
217
    /// <param name="index"></param>
218
    protected virtual void raiseItemInserted(T item, int index)
219
    { if (eventBlock != null) eventBlock.raiseItemInserted(this, item, index); }
220

    
221
    /// <summary>
222
    /// The item removed event. Will be raised for every individual removal from the collection.
223
    /// </summary>
224
    public virtual event ItemRemovedAtHandler<T> ItemRemovedAt
225
    {
226
      add { checkWillListen(EventTypeEnum.RemovedAt); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemRemovedAt += value; }
227
      remove
228
      {
229
        checkWillListen(EventTypeEnum.RemovedAt);
230
        if (eventBlock != null)
231
        {
232
          eventBlock.ItemRemovedAt -= value;
233
          if (eventBlock.events == 0) eventBlock = null;
234
        }
235
      }
236
    }
237
    /// <summary> 
238
    /// Fire the ItemRemovedAt event
239
    /// </summary>
240
    /// <param name="item">The item that was removed</param>
241
    /// <param name="index"></param>
242
    protected virtual void raiseItemRemovedAt(T item, int index)
243
    { if (eventBlock != null) eventBlock.raiseItemRemovedAt(this, item, index); }
244

    
245
    #region Event support for IList
246
    /// <summary>
247
    /// 
248
    /// </summary>
249
    /// <param name="index"></param>
250
    /// <param name="value"></param>
251
    /// <param name="item"></param>
252
    protected virtual void raiseForSetThis(int index, T value, T item)
253
    {
254
      if (ActiveEvents != 0)
255
      {
256
        raiseItemsRemoved(item, 1);
257
        raiseItemRemovedAt(item, index);
258
        raiseItemsAdded(value, 1);
259
        raiseItemInserted(value, index);
260
        raiseCollectionChanged();
261
      }
262
    }
263
    /// <summary>
264
    /// 
265
    /// </summary>
266
    /// <param name="i"></param>
267
    /// <param name="item"></param>
268
    protected virtual void raiseForInsert(int i, T item)
269
    {
270
      if (ActiveEvents != 0)
271
      {
272
        raiseItemInserted(item, i);
273
        raiseItemsAdded(item, 1);
274
        raiseCollectionChanged();
275
      }
276
    }
277

    
278
    /// <summary>
279
    /// 
280
    /// </summary>
281
    /// <param name="item"></param>
282
    protected void raiseForRemove(T item)
283
    {
284
      if (ActiveEvents != 0)
285
      {
286
        raiseItemsRemoved(item, 1);
287
        raiseCollectionChanged();
288
      }
289
    }
290

    
291
    /// <summary>
292
    /// 
293
    /// </summary>
294
    /// <param name="item"></param>
295
    /// <param name="count"></param>
296
    protected void raiseForRemove(T item, int count)
297
    {
298
      if (ActiveEvents != 0)
299
      {
300
        raiseItemsRemoved(item, count);
301
        raiseCollectionChanged();
302
      }
303
    }
304

    
305
    /// <summary>
306
    /// 
307
    /// </summary>
308
    /// <param name="index"></param>
309
    /// <param name="item"></param>
310
    protected void raiseForRemoveAt(int index, T item)
311
    {
312
      if (ActiveEvents != 0)
313
      {
314
        raiseItemRemovedAt(item, index);
315
        raiseItemsRemoved(item, 1);
316
        raiseCollectionChanged();
317
      }
318
    }
319

    
320
    #endregion
321

    
322
    #region Event  Support for ICollection
323
    /// <summary>
324
    /// 
325
    /// </summary>
326
    /// <param name="newitem"></param>
327
    /// <param name="olditem"></param>
328
    protected virtual void raiseForUpdate(T newitem, T olditem)
329
    {
330
      if (ActiveEvents != 0)
331
      {
332
        raiseItemsRemoved(olditem, 1);
333
        raiseItemsAdded(newitem, 1);
334
        raiseCollectionChanged();
335
      }
336
    }
337
    /// <summary>
338
    /// 
339
    /// </summary>
340
    /// <param name="newitem"></param>
341
    /// <param name="olditem"></param>
342
    /// <param name="count"></param>
343
    protected virtual void raiseForUpdate(T newitem, T olditem, int count)
344
    {
345
      if (ActiveEvents != 0)
346
      {
347
        raiseItemsRemoved(olditem, count);
348
        raiseItemsAdded(newitem, count);
349
        raiseCollectionChanged();
350
      }
351
    }
352
    /// <summary>
353
    /// 
354
    /// </summary>
355
    /// <param name="item"></param>
356
    protected virtual void raiseForAdd(T item)
357
    {
358
      if (ActiveEvents != 0)
359
      {
360
        raiseItemsAdded(item, 1);
361
        raiseCollectionChanged();
362
      }
363
    }
364
    /// <summary>
365
    /// 
366
    /// </summary>
367
    /// <param name="wasRemoved"></param>
368
    protected virtual void raiseForRemoveAll(ICollectionValue<T> wasRemoved)
369
    {
370
      if ((ActiveEvents & EventTypeEnum.Removed) != 0)
371
        foreach (T item in wasRemoved)
372
          raiseItemsRemoved(item, 1);
373
      if (wasRemoved != null && wasRemoved.Count > 0)
374
        raiseCollectionChanged();
375
    }
376

    
377
    /// <summary>
378
    /// 
379
    /// </summary>
380
    protected class RaiseForRemoveAllHandler
381
    {
382
      CollectionValueBase<T> collection;
383
      CircularQueue<T> wasRemoved;
384
      bool wasChanged = false;
385

    
386
      /// <summary>
387
      /// 
388
      /// </summary>
389
      /// <param name="collection"></param>
390
      public RaiseForRemoveAllHandler(CollectionValueBase<T> collection)
391
      {
392
        this.collection = collection;
393
        mustFireRemoved = (collection.ActiveEvents & EventTypeEnum.Removed) != 0;
394
        MustFire = (collection.ActiveEvents & (EventTypeEnum.Removed | EventTypeEnum.Changed)) != 0;
395
      }
396

    
397
      bool mustFireRemoved;
398
      /// <summary>
399
      /// 
400
      /// </summary>
401
      public readonly bool MustFire;
402

    
403
      /// <summary>
404
      /// 
405
      /// </summary>
406
      /// <param name="item"></param>
407
      public void Remove(T item)
408
      {
409
        if (mustFireRemoved)
410
        {
411
          if (wasRemoved == null)
412
            wasRemoved = new CircularQueue<T>();
413
          wasRemoved.Enqueue(item);
414
        }
415
        if (!wasChanged)
416
          wasChanged = true;
417
      }
418

    
419
      /// <summary>
420
      /// 
421
      /// </summary>
422
      public void Raise()
423
      {
424
        if (wasRemoved != null)
425
          foreach (T item in wasRemoved)
426
            collection.raiseItemsRemoved(item, 1);
427
        if (wasChanged)
428
          collection.raiseCollectionChanged();
429
      }
430
    }
431
    #endregion
432

    
433
    #endregion
434

    
435
    /// <summary>
436
    /// Check if collection is empty.
437
    /// </summary>
438
    /// <value>True if empty</value>
439
    public abstract bool IsEmpty { get;}
440

    
441
    /// <summary>
442
    /// The number of items in this collection.
443
    /// </summary>
444
    /// <value></value>
445
    public abstract int Count { get;}
446

    
447
    /// <summary>
448
    /// The value is symbolic indicating the type of asymptotic complexity
449
    /// in terms of the size of this collection (worst-case or amortized as
450
    /// relevant).
451
    /// </summary>
452
    /// <value>A characterization of the speed of the 
453
    /// <code>Count</code> property in this collection.</value>
454
    public abstract Speed CountSpeed { get; }
455

    
456
    /// <summary>
457
    /// Copy the items of this collection to part of an array.
458
    /// </summary>
459
    /// <exception cref="ArgumentOutOfRangeException"> if <code>index</code> 
460
    /// is not a valid index
461
    /// into the array (i.e. negative or greater than the size of the array)
462
    /// or the array does not have room for the items.</exception>
463
    /// <param name="array">The array to copy to.</param>
464
    /// <param name="index">The starting index.</param>
465
    [Tested]
466
    public virtual void CopyTo(T[] array, int index)
467
    {
468
      if (index < 0 || index + Count > array.Length)
469
        throw new ArgumentOutOfRangeException();
470

    
471
      foreach (T item in this) array[index++] = item;
472
    }
473

    
474
    /// <summary>
475
    /// Create an array with the items of this collection (in the same order as an
476
    /// enumerator would output them).
477
    /// </summary>
478
    /// <returns>The array</returns>
479
    //[Tested]
480
    public virtual T[] ToArray()
481
    {
482
      T[] res = new T[Count];
483
      int i = 0;
484

    
485
      foreach (T item in this) res[i++] = item;
486

    
487
      return res;
488
    }
489

    
490
    /// <summary>
491
    /// Apply an single argument action, <see cref="T:C5.Act`1"/> to this enumerable
492
    /// </summary>
493
    /// <param name="action">The action delegate</param>
494
    [Tested]
495
    public virtual void Apply(Act<T> action)
496
    {
497
      foreach (T item in this)
498
        action(item);
499
    }
500

    
501

    
502
    /// <summary>
503
    /// Check if there exists an item  that satisfies a
504
    /// specific predicate in this collection.
505
    /// </summary>
506
    /// <param name="predicate">A delegate 
507
    /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) 
508
    /// defining the predicate</param>
509
    /// <returns>True if such an item exists</returns>
510
    [Tested]
511
    public virtual bool Exists(Fun<T, bool> predicate)
512
    {
513
      foreach (T item in this)
514
        if (predicate(item))
515
          return true;
516

    
517
      return false;
518
    }
519

    
520
    /// <summary>
521
    /// Check if there exists an item  that satisfies a
522
    /// specific predicate in this collection and return the first one in enumeration order.
523
    /// </summary>
524
    /// <param name="predicate">A delegate 
525
    /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
526
    /// <param name="item"></param>
527
    /// <returns>True is such an item exists</returns>
528
    public virtual bool Find(Fun<T, bool> predicate, out T item)
529
    {
530
      foreach (T jtem in this)
531
        if (predicate(jtem))
532
        {
533
          item = jtem;
534
          return true;
535
        }
536
      item = default(T);
537
      return false;
538
    }
539

    
540
    /// <summary>
541
    /// Check if all items in this collection satisfies a specific predicate.
542
    /// </summary>
543
    /// <param name="predicate">A delegate 
544
    /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) 
545
    /// defining the predicate</param>
546
    /// <returns>True if all items satisfies the predicate</returns>
547
    [Tested]
548
    public virtual bool All(Fun<T, bool> predicate)
549
    {
550
      foreach (T item in this)
551
        if (!predicate(item))
552
          return false;
553

    
554
      return true;
555
    }
556

    
557
    /// <summary>
558
    /// Create an enumerable, enumerating the items of this collection that satisfies 
559
    /// a certain condition.
560
    /// </summary>
561
    /// <param name="predicate">A delegate 
562
    /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) 
563
    /// defining the predicate</param>
564
    /// <returns>The filtered enumerable</returns>
565
    public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> predicate)
566
    {
567
      foreach (T item in this)
568
        if (predicate(item))
569
          yield return item;
570
    }
571

    
572
    /// <summary>
573
    /// Choose some item of this collection. 
574
    /// </summary>
575
    /// <exception cref="NoSuchItemException">if collection is empty.</exception>
576
    /// <returns></returns>
577
    public abstract T Choose();
578

    
579

    
580
    /// <summary>
581
    /// Create an enumerator for this collection.
582
    /// </summary>
583
    /// <returns>The enumerator</returns>
584
    public override abstract SCG.IEnumerator<T> GetEnumerator();
585

    
586
    #region IShowable Members
587

    
588
    /// <summary>
589
    /// 
590
    /// </summary>
591
    /// <param name="stringbuilder"></param>
592
    /// <param name="rest"></param>
593
    /// <param name="formatProvider"></param>
594
    /// <returns></returns>
595
    public virtual bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
596
    {
597
      return Showing.ShowCollectionValue<T>(this, stringbuilder, ref rest, formatProvider);
598
    }
599
    #endregion
600

    
601
    #region IFormattable Members
602

    
603
    /// <summary>
604
    /// 
605
    /// </summary>
606
    /// <param name="format"></param>
607
    /// <param name="formatProvider"></param>
608
    /// <returns></returns>
609
    public virtual string ToString(string format, IFormatProvider formatProvider)
610
    {
611
      return Showing.ShowString(this, format, formatProvider);
612
    }
613

    
614
    #endregion
615

    
616
    /// <summary>
617
    /// 
618
    /// </summary>
619
    /// <returns></returns>
620
    public override string ToString()
621
    {
622
      return ToString(null, null);
623
    }
624

    
625
  }
626

    
627
  /// <summary>
628
  /// 
629
  /// </summary>
630
  /// <typeparam name="T"></typeparam>
631
  public abstract class DirectedCollectionValueBase<T> : CollectionValueBase<T>, IDirectedCollectionValue<T>
632
  {
633
    /// <summary>
634
    /// <code>Forwards</code> if same, else <code>Backwards</code>
635
    /// </summary>
636
    /// <value>The enumeration direction relative to the original collection.</value>
637
    public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
638

    
639
    /// <summary>
640
    /// 
641
    /// </summary>
642
    /// <returns></returns>
643
    public abstract IDirectedCollectionValue<T> Backwards();
644

    
645
    IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
646

    
647
    /// <summary>
648
    /// Check if there exists an item  that satisfies a
649
    /// specific predicate in this collection and return the first one in enumeration order.
650
    /// </summary>
651
    /// <param name="predicate">A delegate 
652
    /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
653
    /// <param name="item"></param>
654
    /// <returns>True is such an item exists</returns>
655
    public virtual bool FindLast(Fun<T, bool> predicate, out T item)
656
    {
657
      foreach (T jtem in Backwards())
658
        if (predicate(jtem))
659
        {
660
          item = jtem;
661
          return true;
662
        }
663
      item = default(T);
664
      return false;
665
    }
666
  }
667

    
668
  /// <summary>
669
  /// Base class (abstract) for ICollection implementations.
670
  /// </summary>
671
  [Serializable]
672
  public abstract class CollectionBase<T> : CollectionValueBase<T>
673
  {
674
    #region Fields
675

    
676
    /// <summary>
677
    /// The underlying field of the ReadOnly property
678
    /// </summary>
679
    protected bool isReadOnlyBase = false;
680

    
681
    /// <summary>
682
    /// The current stamp value
683
    /// </summary>
684
    protected int stamp;
685

    
686
    /// <summary>
687
    /// The number of items in the collection
688
    /// </summary>
689
    protected int size;
690

    
691
    /// <summary>
692
    /// The item equalityComparer of the collection
693
    /// </summary>
694
    protected readonly SCG.IEqualityComparer<T> itemequalityComparer;
695

    
696
    int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1;
697

    
698
    #endregion
699

    
700
    /// <summary>
701
    /// 
702
    /// </summary>
703
    /// <param name="itemequalityComparer"></param>
704
    protected CollectionBase(SCG.IEqualityComparer<T> itemequalityComparer)
705
    {
706
      if (itemequalityComparer == null)
707
        throw new NullReferenceException("Item EqualityComparer cannot be null.");
708
      this.itemequalityComparer = itemequalityComparer;
709
    }
710

    
711
    #region Util
712

    
713
    /// <summary>
714
    /// Utility method for range checking.
715
    /// </summary>
716
    /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative or
717
    ///  if the range does not fit within collection size.</exception>
718
    /// <param name="start">start of range</param>
719
    /// <param name="count">size of range</param>
720
    [Tested]
721
    protected void checkRange(int start, int count)
722
    {
723
      if (start < 0 || count < 0 || start + count > size)
724
        throw new ArgumentOutOfRangeException();
725
    }
726

    
727

    
728
    /// <summary>
729
    /// Compute the unsequenced hash code of a collection
730
    /// </summary>
731
    /// <param name="items">The collection to compute hash code for</param>
732
    /// <param name="itemequalityComparer">The item equalityComparer</param>
733
    /// <returns>The hash code</returns>
734
    [Tested]
735
    public static int ComputeHashCode(ICollectionValue<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
736
    {
737
      int h = 0;
738

    
739
#if IMPROVED_COLLECTION_HASHFUNCTION
740
      //But still heuristic: 
741
      //Note: the three odd factors should really be random, 
742
      //but there will be a problem with serialization/deserialization!
743
      //Two products is too few
744
      foreach (T item in items)
745
      {
746
        uint h1 = (uint)itemequalityComparer.GetHashCode(item);
747

    
748
        h += (int)((h1 * 1529784657 + 1) ^ (h1 * 2912831877) ^ (h1 * 1118771817 + 2));
749
      }
750

    
751
      return h;
752
      /*
753
            The pairs (-1657792980, -1570288808) and (1862883298, -272461342) gives the same
754
            unsequenced hashcode with this hashfunction. The pair was found with code like
755

    
756
            HashDictionary<int, int[]> set = new HashDictionary<int, int[]>();
757
            Random rnd = new C5Random(12345);
758
            while (true)
759
            {
760
                int[] a = new int[2];
761
                a[0] = rnd.Next(); a[1] = rnd.Next();
762
                int h = unsequencedhashcode(a);
763
                int[] b = a;
764
                if (set.FindOrAdd(h, ref b))
765
                {
766
                    Console.WriteLine("Code {5}, Pair ({1},{2}) number {0} matched other pair ({3},{4})", set.Count, a[0], a[1], b[0], b[1], h);
767
                }
768
            }
769
            */
770
#else
771
            foreach (T item in items)
772
				h ^= itemequalityComparer.GetHashCode(item);
773

    
774
			return (items.Count << 16) + h;
775
#endif
776
    }
777

    
778
    static Type isortedtype = typeof(ISorted<T>);
779

    
780
    /// <summary>
781
    /// Examine if collection1 and collection2 are equal as unsequenced collections
782
    /// using the specified item equalityComparer (assumed compatible with the two collections).
783
    /// </summary>
784
    /// <param name="collection1">The first collection</param>
785
    /// <param name="collection2">The second collection</param>
786
    /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
787
    /// <returns>True if equal</returns>
788
    [Tested]
789
    public static bool StaticEquals(ICollection<T> collection1, ICollection<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
790
    {
791
      if (object.ReferenceEquals(collection1, collection2))
792
        return true;
793

    
794
      // bug20070227:
795
      if (collection1 == null || collection2 == null)
796
        return false;
797

    
798
      if (collection1.Count != collection2.Count)
799
        return false;
800

    
801
      //This way we might run through both enumerations twice, but
802
      //probably not (if the hash codes are good)
803
      //TODO: check equal equalityComparers, at least here!
804
      if (collection1.GetUnsequencedHashCode() != collection2.GetUnsequencedHashCode())
805
        return false;
806

    
807
      //TODO: move this to the sorted implementation classes? 
808
      //Really depends on speed of InstanceOfType: we could save a cast
809
      {
810
        ISorted<T> stit, stat;
811
        if ((stit = collection1 as ISorted<T>) != null && (stat = collection2 as ISorted<T>) != null && stit.Comparer == stat.Comparer)
812
        {
813
          using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
814
          {
815
            while (dit.MoveNext())
816
            {
817
              dat.MoveNext();
818
              if (!itemequalityComparer.Equals(dit.Current, dat.Current))
819
                return false;
820
            }
821
            return true;
822
          }
823
        }
824
      }
825

    
826
      if (!collection1.AllowsDuplicates && (collection2.AllowsDuplicates || collection2.ContainsSpeed >= collection1.ContainsSpeed))
827
      {
828
        foreach (T x in collection1) if (!collection2.Contains(x)) return false;
829
      }
830
      else if (!collection2.AllowsDuplicates)
831
      {
832
        foreach (T x in collection2) if (!collection1.Contains(x)) return false;
833
      }
834
      // Now tit.AllowsDuplicates && tat.AllowsDuplicates
835
      else if (collection1.DuplicatesByCounting && collection2.DuplicatesByCounting)
836
      {
837
        foreach (T item in collection2) if (collection1.ContainsCount(item) != collection2.ContainsCount(item)) return false;
838
      }
839
      else
840
      {
841
        //To avoid an O(n^2) algorithm, we make an aux hashtable to hold the count of items
842
        HashDictionary<T, int> dict = new HashDictionary<T, int>();
843
        foreach (T item in collection2)
844
        {
845
          int count = 1;
846
          if (dict.FindOrAdd(item, ref count))
847
            dict[item] = count + 1;
848
        }
849
        foreach (T item in collection1)
850
        {
851
          int count;
852
          if (dict.Find(item, out count) && count > 0)
853
            dict[item] = count - 1;
854
          else
855
            return false;
856
        }
857
        return true;
858
      }
859

    
860
      return true;
861
    }
862

    
863

    
864
    /// <summary>
865
    /// Get the unsequenced collection hash code of this collection: from the cached 
866
    /// value if present and up to date, else (re)compute.
867
    /// </summary>
868
    /// <returns>The hash code</returns>
869
    public virtual int GetUnsequencedHashCode()
870
    {
871
      if (iUnSequencedHashCodeStamp == stamp)
872
        return iUnSequencedHashCode;
873

    
874
      iUnSequencedHashCode = ComputeHashCode(this, itemequalityComparer);
875
      iUnSequencedHashCodeStamp = stamp;
876
      return iUnSequencedHashCode;
877
    }
878

    
879

    
880
    /// <summary>
881
    /// Check if the contents of otherCollection is equal to the contents of this
882
    /// in the unsequenced sense.  Uses the item equality comparer of this collection
883
    /// </summary>
884
    /// <param name="otherCollection">The collection to compare to.</param>
885
    /// <returns>True if  equal</returns>
886
    public virtual bool UnsequencedEquals(ICollection<T> otherCollection)
887
    {
888
      return otherCollection != null && StaticEquals((ICollection<T>)this, otherCollection, itemequalityComparer);
889
    }
890

    
891

    
892
    /// <summary>
893
    /// Check if the collection has been modified since a specified time, expressed as a stamp value.
894
    /// </summary>
895
    /// <exception cref="CollectionModifiedException"> if this collection has been updated 
896
    /// since a target time</exception>
897
    /// <param name="thestamp">The stamp identifying the target time</param>
898
    protected virtual void modifycheck(int thestamp)
899
    {
900
      if (this.stamp != thestamp)
901
        throw new CollectionModifiedException();
902
    }
903

    
904

    
905
    /// <summary>
906
    /// Check if it is valid to perform update operations, and if so increment stamp.
907
    /// </summary>
908
    /// <exception cref="ReadOnlyCollectionException">If colection is read-only</exception>
909
    protected virtual void updatecheck()
910
    {
911
      if (isReadOnlyBase)
912
        throw new ReadOnlyCollectionException();
913

    
914
      stamp++;
915
    }
916

    
917
    #endregion
918

    
919
    #region ICollection<T> members
920

    
921
    /// <summary>
922
    /// 
923
    /// </summary>
924
    /// <value>True if this collection is read only</value>
925
    [Tested]
926
    public virtual bool IsReadOnly { [Tested]get { return isReadOnlyBase; } }
927

    
928
    #endregion
929

    
930
    #region ICollectionValue<T> members
931
    /// <summary>
932
    /// 
933
    /// </summary>
934
    /// <value>The size of this collection</value>
935
    [Tested]
936
    public override int Count { [Tested]get { return size; } }
937

    
938
    /// <summary>
939
    /// The value is symbolic indicating the type of asymptotic complexity
940
    /// in terms of the size of this collection (worst-case or amortized as
941
    /// relevant).
942
    /// </summary>
943
    /// <value>A characterization of the speed of the 
944
    /// <code>Count</code> property in this collection.</value>
945
    public override Speed CountSpeed { get { return Speed.Constant; } }
946

    
947

    
948
    #endregion
949

    
950
    #region IExtensible<T> members
951

    
952
    /// <summary>
953
    /// 
954
    /// </summary>
955
    /// <value></value>
956
    public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return itemequalityComparer; } }
957

    
958
    /// <summary>
959
    /// 
960
    /// </summary>
961
    /// <value>True if this collection is empty</value>
962
    [Tested]
963
    public override bool IsEmpty { [Tested]get { return size == 0; } }
964

    
965
    #endregion
966

    
967
    #region IEnumerable<T> Members
968
    /// <summary>
969
    /// Create an enumerator for this collection.
970
    /// </summary>
971
    /// <returns>The enumerator</returns>
972
    public override abstract SCG.IEnumerator<T> GetEnumerator();
973
    #endregion
974
  }
975

    
976
  /// <summary>
977
  /// 
978
  /// </summary>
979
  /// <typeparam name="T"></typeparam>
980
  [Serializable]
981
  public abstract class DirectedCollectionBase<T> : CollectionBase<T>, IDirectedCollectionValue<T>
982
  {
983
    /// <summary>
984
    /// 
985
    /// </summary>
986
    /// <param name="itemequalityComparer"></param>
987
    protected DirectedCollectionBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
988
    /// <summary>
989
    /// <code>Forwards</code> if same, else <code>Backwards</code>
990
    /// </summary>
991
    /// <value>The enumeration direction relative to the original collection.</value>
992
    public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
993

    
994
    /// <summary>
995
    /// 
996
    /// </summary>
997
    /// <returns></returns>
998
    public abstract IDirectedCollectionValue<T> Backwards();
999

    
1000
    IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
1001

    
1002
    /// <summary>
1003
    /// Check if there exists an item  that satisfies a
1004
    /// specific predicate in this collection and return the first one in enumeration order.
1005
    /// </summary>
1006
    /// <param name="predicate">A delegate 
1007
    /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1008
    /// <param name="item"></param>
1009
    /// <returns>True is such an item exists</returns>
1010
    public virtual bool FindLast(Fun<T, bool> predicate, out T item)
1011
    {
1012
      foreach (T jtem in Backwards())
1013
        if (predicate(jtem))
1014
        {
1015
          item = jtem;
1016
          return true;
1017
        }
1018
      item = default(T);
1019
      return false;
1020
    }
1021
  }
1022

    
1023
  /// <summary>
1024
  /// Base class (abstract) for sequenced collection implementations.
1025
  /// </summary>
1026
  [Serializable]
1027
  public abstract class SequencedBase<T> : DirectedCollectionBase<T>, IDirectedCollectionValue<T>
1028
  {
1029
    #region Fields
1030

    
1031
    int iSequencedHashCode, iSequencedHashCodeStamp = -1;
1032

    
1033
    #endregion
1034

    
1035
    /// <summary>
1036
    /// 
1037
    /// </summary>
1038
    /// <param name="itemequalityComparer"></param>
1039
    protected SequencedBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
1040

    
1041
    #region Util
1042

    
1043
    //TODO: make random for release
1044
    const int HASHFACTOR = 31;
1045

    
1046
    /// <summary>
1047
    /// Compute the unsequenced hash code of a collection
1048
    /// </summary>
1049
    /// <param name="items">The collection to compute hash code for</param>
1050
    /// <param name="itemequalityComparer">The item equalityComparer</param>
1051
    /// <returns>The hash code</returns>
1052
    [Tested]
1053
    public static int ComputeHashCode(ISequenced<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
1054
    {
1055
      //NOTE: It must be possible to devise a much stronger combined hashcode, 
1056
      //but unfortunately, it has to be universal. OR we could use a (strong)
1057
      //family and initialise its parameter randomly at load time of this class!
1058
      //(We would not want to have yet a flag to check for invalidation?!)
1059
      //NBNBNB: the current hashcode has the very bad property that items with hashcode 0
1060
      // is ignored.
1061
      int iIndexedHashCode = 0;
1062

    
1063
      foreach (T item in items)
1064
        iIndexedHashCode = iIndexedHashCode * HASHFACTOR + itemequalityComparer.GetHashCode(item);
1065

    
1066
      return iIndexedHashCode;
1067
    }
1068

    
1069

    
1070
    /// <summary>
1071
    /// Examine if tit and tat are equal as sequenced collections
1072
    /// using the specified item equalityComparer (assumed compatible with the two collections).
1073
    /// </summary>
1074
    /// <param name="collection1">The first collection</param>
1075
    /// <param name="collection2">The second collection</param>
1076
    /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
1077
    /// <returns>True if equal</returns>
1078
    [Tested]
1079
    public static bool StaticEquals(ISequenced<T> collection1, ISequenced<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
1080
    {
1081
      if (object.ReferenceEquals(collection1, collection2))
1082
        return true;
1083

    
1084
      if (collection1.Count != collection2.Count)
1085
        return false;
1086

    
1087
      //This way we might run through both enumerations twice, but
1088
      //probably not (if the hash codes are good)
1089
      if (collection1.GetSequencedHashCode() != collection2.GetSequencedHashCode())
1090
        return false;
1091

    
1092
      using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
1093
      {
1094
        while (dit.MoveNext())
1095
        {
1096
          dat.MoveNext();
1097
          if (!itemequalityComparer.Equals(dit.Current, dat.Current))
1098
            return false;
1099
        }
1100
      }
1101

    
1102
      return true;
1103
    }
1104

    
1105

    
1106
    /// <summary>
1107
    /// Get the sequenced collection hash code of this collection: from the cached 
1108
    /// value if present and up to date, else (re)compute.
1109
    /// </summary>
1110
    /// <returns>The hash code</returns>
1111
    public virtual int GetSequencedHashCode()
1112
    {
1113
      if (iSequencedHashCodeStamp == stamp)
1114
        return iSequencedHashCode;
1115

    
1116
      iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemequalityComparer);
1117
      iSequencedHashCodeStamp = stamp;
1118
      return iSequencedHashCode;
1119
    }
1120

    
1121

    
1122
    /// <summary>
1123
    /// Check if the contents of that is equal to the contents of this
1124
    /// in the sequenced sense. Using the item equalityComparer of this collection.
1125
    /// </summary>
1126
    /// <param name="otherCollection">The collection to compare to.</param>
1127
    /// <returns>True if  equal</returns>
1128
    public virtual bool SequencedEquals(ISequenced<T> otherCollection)
1129
    {
1130
      return StaticEquals((ISequenced<T>)this, otherCollection, itemequalityComparer);
1131
    }
1132

    
1133

    
1134
    #endregion
1135

    
1136
    /// <summary>
1137
    /// Create an enumerator for this collection.
1138
    /// </summary>
1139
    /// <returns>The enumerator</returns>
1140
    public override abstract SCG.IEnumerator<T> GetEnumerator();
1141

    
1142
    /// <summary>
1143
    /// <code>Forwards</code> if same, else <code>Backwards</code>
1144
    /// </summary>
1145
    /// <value>The enumeration direction relative to the original collection.</value>
1146
    [Tested]
1147
    public override EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
1148

    
1149
    /// <summary>
1150
    /// Check if there exists an item  that satisfies a
1151
    /// specific predicate in this collection and return the index of the first one.
1152
    /// </summary>
1153
    /// <param name="predicate">A delegate 
1154
    /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1155
    /// <returns>the index, if found, a negative value else</returns>
1156
    public int FindIndex(Fun<T, bool> predicate)
1157
    {
1158
      int index = 0;
1159
      foreach (T item in this)
1160
      {
1161
        if (predicate(item))
1162
          return index;
1163
        index++;
1164
      }
1165
      return -1;
1166
    }
1167

    
1168
    /// <summary>
1169
    /// Check if there exists an item  that satisfies a
1170
    /// specific predicate in this collection and return the index of the last one.
1171
    /// </summary>
1172
    /// <param name="predicate">A delegate 
1173
    /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1174
    /// <returns>the index, if found, a negative value else</returns>
1175
    public int FindLastIndex(Fun<T, bool> predicate)
1176
    {
1177
      int index = Count - 1;
1178
      foreach (T item in Backwards())
1179
      {
1180
        if (predicate(item))
1181
          return index;
1182
        index--;
1183
      }
1184
      return -1;
1185
    }
1186

    
1187
  }
1188

    
1189

    
1190
  /// <summary>
1191
  /// Base class for collection classes of dynamic array type implementations.
1192
  /// </summary>
1193
  [Serializable]
1194
  public abstract class ArrayBase<T> : SequencedBase<T>
1195
  {
1196
    #region Fields
1197
    /// <summary>
1198
    /// The actual internal array container. Will be extended on demand.
1199
    /// </summary>
1200
    protected T[] array;
1201

    
1202
    /// <summary>
1203
    /// The offset into the internal array container of the first item. The offset is 0 for a 
1204
    /// base dynamic array and may be positive for an updatable view into a base dynamic array.
1205
    /// </summary>
1206
    protected int offset;
1207
    #endregion
1208

    
1209
    #region Util
1210
    /// <summary>
1211
    /// Double the size of the internal array.
1212
    /// </summary>
1213
    protected virtual void expand()
1214
    {
1215
      expand(2 * array.Length, size);
1216
    }
1217

    
1218

    
1219
    /// <summary>
1220
    /// Expand the internal array container.
1221
    /// </summary>
1222
    /// <param name="newcapacity">The new size of the internal array - 
1223
    /// will be rounded upwards to a power of 2.</param>
1224
    /// <param name="newsize">The (new) size of the (base) collection.</param>
1225
    protected virtual void expand(int newcapacity, int newsize)
1226
    {
1227
      Debug.Assert(newcapacity >= newsize);
1228

    
1229
      int newlength = array.Length;
1230

    
1231
      while (newlength < newcapacity) newlength *= 2;
1232

    
1233
      T[] newarray = new T[newlength];
1234

    
1235
      Array.Copy(array, newarray, newsize);
1236
      array = newarray;
1237
    }
1238

    
1239

    
1240
    /// <summary>
1241
    /// Insert an item at a specific index, moving items to the right
1242
    /// upwards and expanding the array if necessary.
1243
    /// </summary>
1244
    /// <param name="i">The index at which to insert.</param>
1245
    /// <param name="item">The item to insert.</param>
1246
    protected virtual void insert(int i, T item)
1247
    {
1248
      if (size == array.Length)
1249
        expand();
1250

    
1251
      if (i < size)
1252
        Array.Copy(array, i, array, i + 1, size - i);
1253

    
1254
      array[i] = item;
1255
      size++;
1256
    }
1257

    
1258
    #endregion
1259

    
1260
    #region Constructors
1261

    
1262
    /// <summary>
1263
    /// Create an empty ArrayBase object.
1264
    /// </summary>
1265
    /// <param name="capacity">The initial capacity of the internal array container.
1266
    /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param>
1267
    /// <param name="itemequalityComparer">The item equalityComparer to use, primarily for item equality</param>
1268
    protected ArrayBase(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
1269
      : base(itemequalityComparer)
1270
    {
1271
      int newlength = 8;
1272
      while (newlength < capacity) newlength *= 2;
1273
      array = new T[newlength];
1274
    }
1275

    
1276
    #endregion
1277

    
1278
    #region IIndexed members
1279

    
1280
    /// <summary>
1281
    /// </summary>
1282
    /// <exception cref="ArgumentOutOfRangeException">If the arguments does not describe a 
1283
    /// valid range in the indexed collection, cf. <see cref="M:C5.CollectionBase`1.checkRange(System.Int32,System.Int32)"/>.</exception>
1284
    /// <value>The directed collection of items in a specific index interval.</value>
1285
    /// <param name="start">The low index of the interval (inclusive).</param>
1286
    /// <param name="count">The size of the range.</param>
1287
    [Tested]
1288
    public virtual IDirectedCollectionValue<T> this[int start, int count]
1289
    {
1290
      [Tested]
1291
      get
1292
      {
1293
        checkRange(start, count);
1294
        return new Range(this, start, count, true);
1295
      }
1296
    }
1297

    
1298
    #endregion
1299

    
1300
    #region IEditableCollection members
1301
    /// <summary>
1302
    /// Remove all items and reset size of internal array container.
1303
    /// </summary>
1304
    [Tested]
1305
    public virtual void Clear()
1306
    {
1307
      updatecheck();
1308
      array = new T[8];
1309
      size = 0;
1310
    }
1311

    
1312

    
1313
    /// <summary>
1314
    /// Create an array containing (copies) of the items of this collection in enumeration order.
1315
    /// </summary>
1316
    /// <returns>The new array</returns>
1317
    [Tested]
1318
    public override T[] ToArray()
1319
    {
1320
      T[] res = new T[size];
1321

    
1322
      Array.Copy(array, offset, res, 0, size);
1323
      return res;
1324
    }
1325

    
1326

    
1327
    /// <summary>
1328
    /// Perform an internal consistency (invariant) test on the array base.
1329
    /// </summary>
1330
    /// <returns>True if test succeeds.</returns>
1331
    [Tested]
1332
    public virtual bool Check()
1333
    {
1334
      bool retval = true;
1335

    
1336
      if (size > array.Length)
1337
      {
1338
        Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length);
1339
        return false;
1340
      }
1341

    
1342
      for (int i = 0; i < size; i++)
1343
      {
1344
        if ((object)(array[i]) == null)
1345
        {
1346
          Console.WriteLine("Bad element: null at index {0}", i);
1347
          return false;
1348
        }
1349
      }
1350

    
1351
      return retval;
1352
    }
1353

    
1354
    #endregion
1355

    
1356
    #region IDirectedCollection<T> Members
1357

    
1358
    /// <summary>
1359
    /// Create a directed collection with the same contents as this one, but 
1360
    /// opposite enumeration sequence.
1361
    /// </summary>
1362
    /// <returns>The mirrored collection.</returns>
1363
    [Tested]
1364
    public override IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); }
1365

    
1366
    #endregion
1367

    
1368
    /// <summary>
1369
    /// Choose some item of this collection. The result is the last item in the internal array,
1370
    /// making it efficient to remove.
1371
    /// </summary>
1372
    /// <exception cref="NoSuchItemException">if collection is empty.</exception>
1373
    /// <returns></returns>
1374
    public override T Choose() { if (size > 0) return array[size - 1]; throw new NoSuchItemException(); }
1375

    
1376
    #region IEnumerable<T> Members
1377
    /// <summary>
1378
    /// Create an enumerator for this array based collection.
1379
    /// </summary>
1380
    /// <returns>The enumerator</returns>
1381
    [Tested]
1382
    public override SCG.IEnumerator<T> GetEnumerator()
1383
    {
1384
      int thestamp = stamp, theend = size + offset, thestart = offset;
1385

    
1386
      for (int i = thestart; i < theend; i++)
1387
      {
1388
        modifycheck(thestamp);
1389
        yield return array[i];
1390
      }
1391
    }
1392
    #endregion
1393

    
1394
    #region Range nested class
1395
    /// <summary>
1396
    /// A helper class for defining results of interval queries on array based collections.
1397
    /// </summary>
1398
    protected class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
1399
    {
1400
      int start, count, delta, stamp;
1401

    
1402
      ArrayBase<T> thebase;
1403

    
1404

    
1405
      internal Range(ArrayBase<T> thebase, int start, int count, bool forwards)
1406
      {
1407
        this.thebase = thebase; stamp = thebase.stamp;
1408
        delta = forwards ? 1 : -1;
1409
        this.start = start + thebase.offset; this.count = count;
1410
      }
1411

    
1412
      /// <summary>
1413
      /// 
1414
      /// </summary>
1415
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1416
      /// <value>True if this collection is empty.</value>
1417
      public override bool IsEmpty { get { thebase.modifycheck(stamp); return count == 0; } }
1418

    
1419

    
1420
      /// <summary>
1421
      /// 
1422
      /// </summary>
1423
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1424
      /// <value>The number of items in the range</value>
1425
      [Tested]
1426
      public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } }
1427

    
1428
      /// <summary>
1429
      /// The value is symbolic indicating the type of asymptotic complexity
1430
      /// in terms of the size of this collection (worst-case or amortized as
1431
      /// relevant).
1432
      /// </summary>
1433
      /// <value>A characterization of the speed of the 
1434
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1435
      /// <code>Count</code> property in this collection.</value>
1436
      public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } }
1437

    
1438
      /// <summary>
1439
      /// Choose some item of this collection. 
1440
      /// </summary>
1441
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1442
      /// <exception cref="NoSuchItemException">if range is empty.</exception>
1443
      /// <returns></returns>
1444
      public override T Choose()
1445
      {
1446
        thebase.modifycheck(stamp);
1447
        if (count == 0)
1448
          throw new NoSuchItemException();
1449
        return thebase.array[start];
1450
      }
1451

    
1452

    
1453
      /// <summary>
1454
      /// Create an enumerator for this range of an array based collection.
1455
      /// </summary>
1456
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1457
      /// <returns>The enumerator</returns>
1458
      [Tested]
1459
      public override SCG.IEnumerator<T> GetEnumerator()
1460
      {
1461
        for (int i = 0; i < count; i++)
1462
        {
1463
          thebase.modifycheck(stamp);
1464
          yield return thebase.array[start + delta * i];
1465
        }
1466
      }
1467

    
1468

    
1469
      /// <summary>
1470
      /// Create a araay collection range with the same contents as this one, but 
1471
      /// opposite enumeration sequence.
1472
      /// </summary>
1473
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1474
      /// <returns>The mirrored collection.</returns>
1475
      [Tested]
1476
      public override IDirectedCollectionValue<T> Backwards()
1477
      {
1478
        thebase.modifycheck(stamp);
1479

    
1480
        Range res = (Range)MemberwiseClone();
1481

    
1482
        res.delta = -delta;
1483
        res.start = start + (count - 1) * delta;
1484
        return res;
1485
      }
1486

    
1487

    
1488
      IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
1489
      {
1490
        return Backwards();
1491
      }
1492

    
1493

    
1494
      /// <summary>
1495
      /// <code>Forwards</code> if same, else <code>Backwards</code>
1496
      /// </summary>
1497
      /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1498
      /// <value>The enumeration direction relative to the original collection.</value>
1499
      [Tested]
1500
      public override EnumerationDirection Direction
1501
      {
1502
        [Tested]
1503
        get
1504
        {
1505
          thebase.modifycheck(stamp);
1506
          return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
1507
        }
1508
      }
1509
    }
1510
    #endregion
1511
  }
1512
}