Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / C5 / arrays / ArrayList.cs @ 3

1

    
2
/*
3
 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
4
 Permission is hereby granted, free of charge, to any person obtaining a copy
5
 of this software and associated documentation files (the "Software"), to deal
6
 in the Software without restriction, including without limitation the rights
7
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
 copies of the Software, and to permit persons to whom the Software is
9
 furnished to do so, subject to the following conditions:
10
 
11
 The above copyright notice and this permission notice shall be included in
12
 all copies or substantial portions of the Software.
13
 
14
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
 SOFTWARE.
21
*/
22

    
23
#define HASHINDEXnot
24

    
25
using System;
26
using System.Diagnostics;
27
using SCG = System.Collections.Generic;
28
namespace C5
29
{
30
  /// <summary>
31
  /// A list collection based on a plain dynamic array data structure.
32
  /// Expansion of the internal array is performed by doubling on demand. 
33
  /// The internal array is only shrinked by the Clear method. 
34
  ///
35
  /// <i>When the FIFO property is set to false this class works fine as a stack of T.
36
  /// When the FIFO property is set to true the class will function as a (FIFO) queue
37
  /// but very inefficiently, use a LinkedList (<see cref="T:C5.LinkedList`1"/>) instead.</i>
38
  /// </summary>
39
  [Serializable]
40
  public class ArrayList<T> : ArrayBase<T>, IList<T>, SCG.IList<T>
41
#if HASHINDEX
42
#else
43
, IStack<T>, IQueue<T>
44
#endif
45
  {
46
    #region Fields
47

    
48
    /// <summary>
49
    /// Has this list or view not been invalidated by some operation (by someone calling Dispose())
50
    /// </summary>
51
    bool isValid = true;
52

    
53
    //TODO: wonder if we should save some memory on none-view situations by 
54
    //      putting these three fields into a single ref field?
55
    /// <summary>
56
    /// The underlying list if we are a view, null else.
57
    /// </summary>
58
    ArrayList<T> underlying;
59
    WeakViewList<ArrayList<T>> views;
60
    WeakViewList<ArrayList<T>>.Node myWeakReference;
61

    
62
    /// <summary>
63
    /// The size of the underlying list.
64
    /// </summary>
65
    int underlyingsize { get { return (underlying ?? this).size; } }
66

    
67
    /// <summary>
68
    /// The underlying field of the FIFO property
69
    /// </summary>
70
    bool fIFO = false;
71

    
72
#if HASHINDEX
73
    HashSet<KeyValuePair<T, int>> itemIndex;
74
#endif
75
    #endregion
76
    #region Events
77

    
78
    /// <summary>
79
    /// 
80
    /// </summary>
81
    /// <value></value>
82
    public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } }
83

    
84
    /*
85
        /// <summary>
86
        /// 
87
        /// </summary>
88
        /// <value></value>
89
        public override event CollectionChangedHandler<T> CollectionChanged
90
        {
91
          add
92
          {
93
            if (underlying == null)
94
              base.CollectionChanged += value;
95
            else
96
              throw new UnlistenableEventException("Can't listen to a view");
97
          }
98
          remove
99
          {
100
            if (underlying == null)
101
              base.CollectionChanged -= value;
102
            else
103
              throw new UnlistenableEventException("Can't listen to a view");
104
          }
105
        }
106

    
107
        /// <summary>
108
        /// 
109
        /// </summary>
110
        /// <value></value>
111
        public override event CollectionClearedHandler<T> CollectionCleared
112
        {
113
          add
114
          {
115
            if (underlying == null)
116
              base.CollectionCleared += value;
117
            else
118
              throw new UnlistenableEventException("Can't listen to a view");
119
          }
120
          remove
121
          {
122
            if (underlying == null)
123
              base.CollectionCleared -= value;
124
            else
125
              throw new UnlistenableEventException("Can't listen to a view");
126
          }
127
        }
128

    
129
        /// <summary>
130
        /// 
131
        /// </summary>
132
        /// <value></value>
133
        public override event ItemsAddedHandler<T> ItemsAdded
134
        {
135
          add
136
          {
137
            if (underlying == null)
138
              base.ItemsAdded += value;
139
            else
140
              throw new UnlistenableEventException("Can't listen to a view");
141
          }
142
          remove
143
          {
144
            if (underlying == null)
145
              base.ItemsAdded -= value;
146
            else
147
              throw new UnlistenableEventException("Can't listen to a view");
148
          }
149
        }
150

    
151
        /// <summary>
152
        /// 
153
        /// </summary>
154
        /// <value></value>
155
        public override event ItemInsertedHandler<T> ItemInserted
156
        {
157
          add
158
          {
159
            if (underlying == null)
160
              base.ItemInserted += value;
161
            else
162
              throw new UnlistenableEventException("Can't listen to a view");
163
          }
164
          remove
165
          {
166
            if (underlying == null)
167
              base.ItemInserted -= value;
168
            else
169
              throw new UnlistenableEventException("Can't listen to a view");
170
          }
171
        }
172

    
173
        /// <summary>
174
        /// 
175
        /// </summary>
176
        /// <value></value>
177
        public override event ItemsRemovedHandler<T> ItemsRemoved
178
        {
179
          add
180
          {
181
            if (underlying == null)
182
              base.ItemsRemoved += value;
183
            else
184
              throw new UnlistenableEventException("Can't listen to a view");
185
          }
186
          remove
187
          {
188
            if (underlying == null)
189
              base.ItemsRemoved -= value;
190
            else
191
              throw new UnlistenableEventException("Can't listen to a view");
192
          }
193
        }
194

    
195
        /// <summary>
196
        /// 
197
        /// </summary>
198
        /// <value></value>
199
        public override event ItemRemovedAtHandler<T> ItemRemovedAt
200
        {
201
          add
202
          {
203
            if (underlying == null)
204
              base.ItemRemovedAt += value;
205
            else
206
              throw new UnlistenableEventException("Can't listen to a view");
207
          }
208
          remove
209
          {
210
            if (underlying == null)
211
              base.ItemRemovedAt -= value;
212
            else
213
              throw new UnlistenableEventException("Can't listen to a view");
214
          }
215
        }
216

    
217
          */
218

    
219
    #endregion
220
    #region Util
221

    
222
    bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
223

    
224
    /// <summary>
225
    /// Increment or decrement the private size fields
226
    /// </summary>
227
    /// <param name="delta">Increment (with sign)</param>
228
    void addtosize(int delta)
229
    {
230
      size += delta;
231
      if (underlying != null)
232
        underlying.size += delta;
233
    }
234

    
235
    #region Array handling
236
    /// <summary>
237
    /// Double the size of the internal array.
238
    /// </summary>
239
    protected override void expand()
240
    { expand(2 * array.Length, underlyingsize); }
241

    
242

    
243
    /// <summary>
244
    /// Expand the internal array, resetting the index of the first unused element.
245
    /// </summary>
246
    /// <param name="newcapacity">The new capacity (will be rouded upwards to a power of 2).</param>
247
    /// <param name="newsize">The new count of </param>
248
    protected override void expand(int newcapacity, int newsize)
249
    {
250
      if (underlying != null)
251
        underlying.expand(newcapacity, newsize);
252
      else
253
      {
254
        base.expand(newcapacity, newsize);
255
        if (views != null)
256
          foreach (ArrayList<T> v in views)
257
            v.array = array;
258
      }
259
    }
260

    
261
    #endregion
262

    
263
    #region Checks
264
    /// <summary>
265
    /// Check if it is valid to perform updates and increment stamp if so.
266
    /// </summary>
267
    /// <exception cref="ViewDisposedException"> If check fails by this list being a disposed view.</exception>
268
    /// <exception cref="ReadOnlyCollectionException"> If check fails by this being a read only list.</exception>
269
    protected override void updatecheck()
270
    {
271
      validitycheck();
272
      base.updatecheck();
273
      if (underlying != null)
274
        underlying.stamp++;
275
    }
276

    
277

    
278
    /// <summary>
279
    /// Check if we are a view that the underlying list has only been updated through us.
280
    /// <para>This method should be called from enumerators etc to guard against 
281
    /// modification of the base collection.</para>
282
    /// </summary>
283
    /// <exception cref="ViewDisposedException"> if check fails.</exception>
284
    void validitycheck()
285
    {
286
      if (!isValid)
287
        throw new ViewDisposedException();
288
    }
289

    
290

    
291
    /// <summary>
292
    /// Check that the list has not been updated since a particular time.
293
    /// <para>To be used by enumerators and range </para>
294
    /// </summary>
295
    /// <exception cref="ViewDisposedException"> If check fails by this list being a disposed view.</exception>
296
    /// <exception cref="CollectionModifiedException">If the list *has* beeen updated since that  time..</exception>
297
    /// <param name="stamp">The stamp indicating the time.</param>
298
    protected override void modifycheck(int stamp)
299
    {
300
      validitycheck();
301
      if (this.stamp != stamp)
302
        throw new CollectionModifiedException();
303
    }
304

    
305
    #endregion
306

    
307
    #region Searching
308

    
309
    /// <summary>
310
    /// Internal version of IndexOf without modification checks.
311
    /// </summary>
312
    /// <param name="item">Item to look for</param>
313
    /// <returns>The index of first occurrence</returns>
314
    int indexOf(T item)
315
    {
316
#if HASHINDEX
317
      KeyValuePair<T, int> p = new KeyValuePair<T, int>(item);
318
      if (itemIndex.Find(ref p) && p.Value >= offset && p.Value < offset + size)
319
        return p.Value - offset;
320
#else
321
      for (int i = 0; i < size; i++)
322
        if (equals(item, array[offset + i]))
323
          return i;
324
#endif
325
      return ~size;
326
    }
327

    
328
    /// <summary>
329
    /// Internal version of LastIndexOf without modification checks.
330
    /// </summary>
331
    /// <param name="item">Item to look for</param>
332
    /// <returns>The index of last occurrence</returns>
333
    int lastIndexOf(T item)
334
    {
335
#if HASHINDEX
336
      return indexOf(item);
337
#else
338
      for (int i = size - 1; i >= 0; i--)
339
        if (equals(item, array[offset + i]))
340
          return i;
341
      return ~size;
342
#endif
343
    }
344
    #endregion
345

    
346
    #region Inserting
347

    
348
#if HASHINDEX
349
    /// <summary>
350
    /// Internal version of Insert with no modification checks.
351
    /// </summary>
352
    /// <exception cref="DuplicateNotAllowedException"> if item already in list.</exception>
353
    /// <param name="i">Index to insert at</param>
354
    /// <param name="item">Item to insert</param>
355
#else
356
    /// <summary>
357
    /// Internal version of Insert with no modification checks.
358
    /// </summary>
359
    /// <param name="i">Index to insert at</param>
360
    /// <param name="item">Item to insert</param>
361
#endif
362
    protected override void insert(int i, T item)
363
    {
364
#if HASHINDEX
365
      KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, offset + i);
366
      if (itemIndex.FindOrAdd(ref p))
367
        throw new DuplicateNotAllowedException("Item already in indexed list: " + item);
368
#endif
369
      baseinsert(i, item);
370
#if HASHINDEX
371
      reindex(i + offset + 1);
372
#endif
373
    }
374

    
375
    private void baseinsert(int i, T item)
376
    {
377
      if (underlyingsize == array.Length)
378
        expand();
379
      i += offset;
380
      if (i < underlyingsize)
381
        Array.Copy(array, i, array, i + 1, underlyingsize - i);
382
      array[i] = item;
383
      addtosize(1);
384
      fixViewsAfterInsert(1, i);
385
    }
386
    #endregion
387

    
388
    #region Removing
389

    
390
    /// <summary>
391
    /// Internal version of RemoveAt with no modification checks.
392
    /// </summary>
393
    /// <param name="i">Index to remove at</param>
394
    /// <returns>The removed item</returns>
395
    T removeAt(int i)
396
    {
397
      i += offset;
398
      fixViewsBeforeSingleRemove(i);
399
      T retval = array[i];
400
      addtosize(-1);
401
      if (underlyingsize > i)
402
        Array.Copy(array, i + 1, array, i, underlyingsize - i);
403
      array[underlyingsize] = default(T);
404
#if HASHINDEX
405
      itemIndex.Remove(new KeyValuePair<T, int>(retval));
406
      reindex(i);
407
#endif
408
      return retval;
409
    }
410
    #endregion
411

    
412
    #region Indexing
413

    
414
#if HASHINDEX
415
    private void reindex(int start) { reindex(start, underlyingsize); }
416

    
417
    private void reindex(int start, int end)
418
    {
419
      for (int j = start; j < end; j++)
420
        itemIndex.UpdateOrAdd(new KeyValuePair<T, int>(array[j], j));
421
    }
422
#endif
423
    #endregion
424

    
425
    #region fixView utilities
426

    
427
    /// <summary>
428
    /// 
429
    /// </summary>
430
    /// <param name="added">The actual number of inserted nodes</param>
431
    /// <param name="realInsertionIndex"></param>
432
    void fixViewsAfterInsert(int added, int realInsertionIndex)
433
    {
434
      if (views != null)
435
        foreach (ArrayList<T> view in views)
436
        {
437
          if (view != this)
438
          {
439
            if (view.offset < realInsertionIndex && view.offset + view.size > realInsertionIndex)
440
              view.size += added;
441
            if (view.offset > realInsertionIndex || (view.offset == realInsertionIndex && view.size > 0))
442
              view.offset += added;
443
          }
444
        }
445
    }
446

    
447
    void fixViewsBeforeSingleRemove(int realRemovalIndex)
448
    {
449
      if (views != null)
450
        foreach (ArrayList<T> view in views)
451
        {
452
          if (view != this)
453
          {
454
            if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex)
455
              view.size--;
456
            if (view.offset > realRemovalIndex)
457
              view.offset--;
458
          }
459
        }
460
    }
461

    
462
    /// <summary>
463
    /// Fix offsets and sizes of other views before removing an interval from this 
464
    /// </summary>
465
    /// <param name="start">the start of the interval relative to the array/underlying</param>
466
    /// <param name="count"></param>
467
    void fixViewsBeforeRemove(int start, int count)
468
    {
469
      int clearend = start + count - 1;
470
      if (views != null)
471
        foreach (ArrayList<T> view in views)
472
        {
473
          if (view == this)
474
            continue;
475
          int viewoffset = view.offset, viewend = viewoffset + view.size - 1;
476
          if (start < viewoffset)
477
          {
478
            if (clearend < viewoffset)
479
              view.offset = viewoffset - count;
480
            else
481
            {
482
              view.offset = start;
483
              view.size = clearend < viewend ? viewend - clearend : 0;
484
            }
485
          }
486
          else if (start <= viewend)
487
            view.size = clearend <= viewend ? view.size - count : start - viewoffset;
488
        }
489
    }
490

    
491
    /// <summary>
492
    /// 
493
    /// </summary>
494
    /// <param name="otherOffset"></param>
495
    /// <param name="otherSize"></param>
496
    /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns>
497
    MutualViewPosition viewPosition(int otherOffset, int otherSize)
498
    {
499
      int end = offset + size, otherEnd = otherOffset + otherSize;
500
      if (otherOffset >= end || otherEnd <= offset)
501
        return MutualViewPosition.NonOverlapping;
502
      if (size == 0 || (otherOffset <= offset && end <= otherEnd))
503
        return MutualViewPosition.Contains;
504
      if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end))
505
        return MutualViewPosition.ContainedIn;
506
      return MutualViewPosition.Overlapping;
507
    }
508

    
509
    //TODO: make version that fits the new, more forgiving rules for disposing
510
    void disposeOverlappingViews(bool reverse)
511
    {
512
      if (views != null)
513
        foreach (ArrayList<T> view in views)
514
        {
515
          if (view != this)
516
          {
517
            switch (viewPosition(view.offset, view.size))
518
            {
519
              case MutualViewPosition.ContainedIn:
520
                if (reverse)
521
                  view.offset = 2 * offset + size - view.size - view.offset;
522
                else
523
                  view.Dispose();
524
                break;
525
              case MutualViewPosition.Overlapping:
526
                view.Dispose();
527
                break;
528
              case MutualViewPosition.Contains:
529
              case MutualViewPosition.NonOverlapping:
530
                break;
531
            }
532
          }
533
        }
534
    }
535
    #endregion
536

    
537
    #endregion
538

    
539
    #region Position, PositionComparer and ViewHandler nested types
540
    class PositionComparer : SCG.IComparer<Position>
541
    {
542
      public int Compare(Position a, Position b)
543
      {
544
        return a.index.CompareTo(b.index);
545
      }
546
    }
547
    /// <summary>
548
    /// During RemoveAll, we need to cache the original endpoint indices of views (??? also for ArrayList?)
549
    /// </summary>
550
    struct Position
551
    {
552
      public readonly ArrayList<T> view;
553
      public readonly int index;
554
      public Position(ArrayList<T> view, bool left)
555
      {
556
        this.view = view;
557
        index = left ? view.offset : view.offset + view.size - 1;
558
      }
559
      public Position(int index) { this.index = index; view = null; }
560
    }
561

    
562
    /// <summary>
563
    /// Handle the update of (other) views during a multi-remove operation.
564
    /// </summary>
565
    struct ViewHandler
566
    {
567
      ArrayList<Position> leftEnds;
568
      ArrayList<Position> rightEnds;
569
      int leftEndIndex, rightEndIndex;
570
      internal readonly int viewCount;
571
      internal ViewHandler(ArrayList<T> list)
572
      {
573
        leftEndIndex = rightEndIndex = viewCount = 0;
574
        leftEnds = rightEnds = null;
575
        if (list.views != null)
576
          foreach (ArrayList<T> v in list.views)
577
            if (v != list)
578
            {
579
              if (leftEnds == null)
580
              {
581
                leftEnds = new ArrayList<Position>();
582
                rightEnds = new ArrayList<Position>();
583
              }
584
              leftEnds.Add(new Position(v, true));
585
              rightEnds.Add(new Position(v, false));
586
            }
587
        if (leftEnds == null)
588
          return;
589
        viewCount = leftEnds.Count;
590
        leftEnds.Sort(new PositionComparer());
591
        rightEnds.Sort(new PositionComparer());
592
      }
593
      /// <summary>
594
      /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed
595
      /// </summary>
596
      /// <param name="removed"></param>
597
      /// <param name="realindex"></param>
598
      internal void skipEndpoints(int removed, int realindex)
599
      {
600
        if (viewCount > 0)
601
        {
602
          Position endpoint;
603
          while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).index <= realindex)
604
          {
605
            ArrayList<T> view = endpoint.view;
606
            view.offset = view.offset - removed;
607
            view.size += removed;
608
            leftEndIndex++;
609
          }
610
          while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).index < realindex)
611
          {
612
            endpoint.view.size -= removed;
613
            rightEndIndex++;
614
          }
615
        }
616
      }
617
      internal void updateViewSizesAndCounts(int removed, int realindex)
618
      {
619
        if (viewCount > 0)
620
        {
621
          Position endpoint;
622
          while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).index <= realindex)
623
          {
624
            ArrayList<T> view = endpoint.view;
625
            view.offset = view.Offset - removed;
626
            view.size += removed;
627
            leftEndIndex++;
628
          }
629
          while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).index < realindex)
630
          {
631
            endpoint.view.size -= removed;
632
            rightEndIndex++;
633
          }
634
        }
635
      }
636
    }
637
    #endregion
638

    
639
    #region Constructors
640
    /// <summary>
641
    /// Create an array list with default item equalityComparer and initial capacity 8 items.
642
    /// </summary>
643
    public ArrayList() : this(8) { }
644

    
645

    
646
    /// <summary>
647
    /// Create an array list with external item equalityComparer and initial capacity 8 items.
648
    /// </summary>
649
    /// <param name="itemequalityComparer">The external item equalityComparer</param>
650
    public ArrayList(SCG.IEqualityComparer<T> itemequalityComparer) : this(8, itemequalityComparer) { }
651

    
652

    
653
    /// <summary>
654
    /// Create an array list with default item equalityComparer and prescribed initial capacity.
655
    /// </summary>
656
    /// <param name="capacity">The prescribed capacity</param>
657
    public ArrayList(int capacity) : this(capacity, EqualityComparer<T>.Default) { }
658

    
659

    
660
    /// <summary>
661
    /// Create an array list with external item equalityComparer and prescribed initial capacity.
662
    /// </summary>
663
    /// <param name="capacity">The prescribed capacity</param>
664
    /// <param name="itemequalityComparer">The external item equalityComparer</param>
665
    public ArrayList(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
666
      : base(capacity, itemequalityComparer)
667
    {
668
#if HASHINDEX
669
      itemIndex = new HashSet<KeyValuePair<T, int>>(new KeyValuePairEqualityComparer<T, int>(itemequalityComparer));
670
#endif
671
    }
672

    
673
    #endregion
674

    
675
    #region IList<T> Members
676

    
677
    /// <summary>
678
    /// </summary>
679
    /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
680
    /// <value>The first item in this list.</value>
681
    [Tested]
682
    public virtual T First
683
    {
684
      [Tested]
685
      get
686
      {
687
        validitycheck();
688
        if (size == 0)
689
          throw new NoSuchItemException();
690

    
691
        return array[offset];
692
      }
693
    }
694

    
695

    
696
    /// <summary>
697
    /// </summary>
698
    /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
699
    /// <value>The last item in this list.</value>
700
    [Tested]
701
    public virtual T Last
702
    {
703
      [Tested]
704
      get
705
      {
706
        validitycheck();
707
        if (size == 0)
708
          throw new NoSuchItemException();
709

    
710
        return array[offset + size - 1];
711
      }
712
    }
713

    
714

    
715
    /// <summary>
716
    /// Since <code>Add(T item)</code> always add at the end of the list,
717
    /// this describes if list has FIFO or LIFO semantics.
718
    /// </summary>
719
    /// <value>True if the <code>Remove()</code> operation removes from the
720
    /// start of the list, false if it removes from the end. The default for a new array list is false.</value>
721
    [Tested]
722
    public virtual bool FIFO
723
    {
724
      [Tested]
725
      get { validitycheck(); return fIFO; }
726
      [Tested]
727
      set { updatecheck(); fIFO = value; }
728
    }
729

    
730
    /// <summary>
731
    /// 
732
    /// </summary>
733
    public virtual bool IsFixedSize
734
    {
735
      get { validitycheck(); return false; }
736
    }
737

    
738

    
739
#if HASHINDEX
740
    /// <summary>
741
    /// On this list, this indexer is read/write.
742
    /// </summary>
743
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
744
    /// &gt;= the size of the collection.</exception>
745
    /// <exception cref="DuplicateNotAllowedException"> By the get operation
746
    /// if the item already is present somewhere else in the list.</exception>
747
    /// <value>The index'th item of this list.</value>
748
    /// <param name="index">The index of the item to fetch or store.</param>
749
#else
750
    /// <summary>
751
    /// On this list, this indexer is read/write.
752
    /// </summary>
753
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
754
    /// &gt;= the size of the collection.</exception>
755
    /// <value>The index'th item of this list.</value>
756
    /// <param name="index">The index of the item to fetch or store.</param>
757
#endif
758
    [Tested]
759
    public virtual T this[int index]
760
    {
761
      [Tested]
762
      get
763
      {
764
        validitycheck();
765
        if (index < 0 || index >= size)
766
          throw new IndexOutOfRangeException();
767

    
768
        return array[offset + index];
769
      }
770
      [Tested]
771
      set
772
      {
773
        updatecheck();
774
        if (index < 0 || index >= size)
775
          throw new IndexOutOfRangeException();
776
        index += offset;
777
        T item = array[index];
778
#if HASHINDEX
779
        KeyValuePair<T, int> p = new KeyValuePair<T, int>(value, index);
780
        if (itemequalityComparer.Equals(value, item))
781
        {
782
          array[index] = value;
783
          itemIndex.Update(p);
784
        }
785
        else if (!itemIndex.FindOrAdd(ref p))
786
        {
787
          itemIndex.Remove(new KeyValuePair<T, int>(item));
788
          array[index] = value;
789
        }
790
        else
791
          throw new DuplicateNotAllowedException("Item already in indexed list");
792
#else
793
        array[index] = value;
794
#endif
795
        (underlying ?? this).raiseForSetThis(index, value, item);
796
      }
797
    }
798

    
799
    /// <summary>
800
    /// 
801
    /// </summary>
802
    /// <value></value>
803
    public virtual Speed IndexingSpeed { get { return Speed.Constant; } }
804

    
805
#if HASHINDEX
806
    /// <summary>
807
    /// Insert an item at a specific index location in this list. 
808
    ///</summary>
809
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
810
    /// &gt; the size of the collection. </exception>
811
    /// <exception cref="DuplicateNotAllowedException"> 
812
    /// If the item is already present in the list.</exception>
813
    /// <param name="index">The index at which to insert.</param>
814
    /// <param name="item">The item to insert.</param>
815
#else
816
    /// <summary>
817
    /// Insert an item at a specific index location in this list. 
818
    ///</summary>
819
    /// <exception cref="IndexOutOfRangeException"> if i is negative or
820
    /// &gt; the size of the collection. </exception>
821
    /// <param name="index">The index at which to insert.</param>
822
    /// <param name="item">The item to insert.</param>
823
#endif
824
    [Tested]
825
    public virtual void Insert(int index, T item)
826
    {
827
      updatecheck();
828
      if (index < 0 || index > size)
829
        throw new IndexOutOfRangeException();
830

    
831
      insert(index, item);
832
      (underlying ?? this).raiseForInsert(index + offset, item);
833
    }
834

    
835
    /// <summary>
836
    /// Insert an item at the end of a compatible view, used as a pointer.
837
    /// <para>The <code>pointer</code> must be a view on the same list as
838
    /// <code>this</code> and the endpoitn of <code>pointer</code> must be
839
    /// a valid insertion point of <code>this</code></para>
840
    /// </summary>
841
    /// <exception cref="IncompatibleViewException">If <code>pointer</code> 
842
    /// is not a view on or the same list as <code>this</code></exception>
843
    /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of 
844
    ///  <code>pointer</code> is not inside <code>this</code></exception>
845
    /// <exception cref="DuplicateNotAllowedException"> if the list has
846
    /// <code>AllowsDuplicates==false</code> and the item is 
847
    /// already in the list.</exception>
848
    /// <param name="pointer"></param>
849
    /// <param name="item"></param>
850
    public void Insert(IList<T> pointer, T item)
851
    {
852
      if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this)))
853
        throw new IncompatibleViewException();
854
      Insert(pointer.Offset + pointer.Count - Offset, item);
855
    }
856

    
857
#if HASHINDEX
858
    /// <summary>
859
    /// Insert into this list all items from an enumerable collection starting 
860
    /// at a particular index.
861
    /// </summary>
862
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
863
    /// &gt; the size of the collection.</exception>
864
    /// <exception cref="DuplicateNotAllowedException"> If <code>items</code> 
865
    /// contains duplicates or some item already  present in the list.</exception>
866
    /// <param name="index">Index to start inserting at</param>
867
    /// <param name="items">Items to insert</param>
868
#else
869
    /// <summary>
870
    /// Insert into this list all items from an enumerable collection starting 
871
    /// at a particular index.
872
    /// </summary>
873
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
874
    /// &gt; the size of the collection.</exception>
875
    /// <param name="index">Index to start inserting at</param>
876
    /// <param name="items">Items to insert</param>
877
    /// <typeparam name="U"></typeparam>
878
#endif
879
    [Tested]
880
    public virtual void InsertAll<U>(int index, SCG.IEnumerable<U> items) where U : T
881
    {
882
      updatecheck();
883
      if (index < 0 || index > size)
884
        throw new IndexOutOfRangeException();
885
      index += offset;
886
      int toadd = EnumerableBase<U>.countItems(items);
887
      if (toadd == 0)
888
        return;
889
      if (toadd + underlyingsize > array.Length)
890
        expand(toadd + underlyingsize, underlyingsize);
891
      if (underlyingsize > index)
892
        Array.Copy(array, index, array, index + toadd, underlyingsize - index);
893
      int i = index;
894
      try
895
      {
896

    
897
        foreach (T item in items)
898
        {
899
#if HASHINDEX
900
          KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, i);
901
          if (itemIndex.FindOrAdd(ref p))
902
            throw new DuplicateNotAllowedException("Item already in indexed list");
903
#endif
904
          array[i++] = item;
905
        }
906
      }
907
      finally
908
      {
909
        int added = i - index;
910
        if (added < toadd)
911
        {
912
          Array.Copy(array, index + toadd, array, i, underlyingsize - index);
913
          Array.Clear(array, underlyingsize + added, toadd - added);
914
        }
915
        if (added > 0)
916
        {
917
          addtosize(added);
918
#if HASHINDEX
919
          reindex(i);
920
#endif
921
          fixViewsAfterInsert(added, index);
922
          (underlying ?? this).raiseForInsertAll(index, added);
923
        }
924
      }
925
    }
926
    private void raiseForInsertAll(int index, int added)
927
    {
928
      if (ActiveEvents != 0)
929
      {
930
        if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0)
931
          for (int j = index; j < index + added; j++)
932
          {
933
            raiseItemInserted(array[j], j);
934
            raiseItemsAdded(array[j], 1);
935
          }
936
        raiseCollectionChanged();
937
      }
938
    }
939

    
940
#if HASHINDEX
941
    /// <summary>
942
    /// Insert an item at the front of this list;
943
    /// </summary>
944
    /// <exception cref="DuplicateNotAllowedException">If the item is already in the list</exception>
945
    /// <param name="item">The item to insert.</param>
946
#else
947
    /// <summary>
948
    /// Insert an item at the front of this list;
949
    /// </summary>
950
    /// <param name="item">The item to insert.</param>
951
#endif
952
    [Tested]
953
    public virtual void InsertFirst(T item)
954
    {
955
      updatecheck();
956
      insert(0, item);
957
      (underlying ?? this).raiseForInsert(offset, item);
958
    }
959

    
960

    
961
#if HASHINDEX
962
    /// <summary>
963
    /// Insert an item at the back of this list.
964
    /// </summary>
965
    /// <exception cref="DuplicateNotAllowedException">If the item is already in the list</exception>
966
    /// <param name="item">The item to insert.</param>
967
#else
968
    /// <summary>
969
    /// Insert an item at the back of this list.
970
    /// </summary>
971
    /// <param name="item">The item to insert.</param>
972
#endif
973
    [Tested]
974
    public virtual void InsertLast(T item)
975
    {
976
      updatecheck();
977
      insert(size, item);
978
      (underlying ?? this).raiseForInsert(size - 1 + offset, item);
979
    }
980

    
981

    
982
    //NOTE: if the filter throws an exception, no result will be returned.
983
    /// <summary>
984
    /// Create a new list consisting of the items of this list satisfying a 
985
    /// certain predicate.
986
    /// <para>The new list will be of type ArrayList</para>
987
    /// </summary>
988
    /// <param name="filter">The filter delegate defining the predicate.</param>
989
    /// <returns>The new list.</returns>
990
    [Tested]
991
    public virtual IList<T> FindAll(Fun<T, bool> filter)
992
    {
993
      validitycheck();
994
      int stamp = this.stamp;
995
      ArrayList<T> res = new ArrayList<T>(itemequalityComparer);
996
      int j = 0, rescap = res.array.Length;
997
      for (int i = 0; i < size; i++)
998
      {
999
        T a = array[offset + i];
1000
        bool found = filter(a);
1001
        modifycheck(stamp);
1002
        if (found)
1003
        {
1004
          if (j == rescap) res.expand(rescap = 2 * rescap, j);
1005
          res.array[j++] = a;
1006
        }
1007
      }
1008
      res.size = j;
1009
#if HASHINDEX
1010
      res.reindex(0);
1011
#endif
1012
      return res;
1013
    }
1014

    
1015

    
1016
#if HASHINDEX
1017
    /// <summary>
1018
    /// Create a new list consisting of the results of mapping all items of this
1019
    /// list. The new list will use the default item equalityComparer for the item type V.
1020
    /// <para>The new list will be of type ArrayList</para>
1021
    /// </summary>
1022
    /// <exception cref="DuplicateNotAllowedException">If <code>mapper</code>
1023
    /// creates duplicates</exception>
1024
    /// <typeparam name="V">The type of items of the new list</typeparam>
1025
    /// <param name="mapper">The delegate defining the map.</param>
1026
    /// <returns>The new list.</returns>
1027
#else
1028
    /// <summary>
1029
    /// Create a new list consisting of the results of mapping all items of this
1030
    /// list. The new list will use the default item equalityComparer for the item type V.
1031
    /// <para>The new list will be of type ArrayList</para>
1032
    /// </summary>
1033
    /// <typeparam name="V">The type of items of the new list</typeparam>
1034
    /// <param name="mapper">The delegate defining the map.</param>
1035
    /// <returns>The new list.</returns>
1036
#endif
1037
    [Tested]
1038
    public virtual IList<V> Map<V>(Fun<T, V> mapper)
1039
    {
1040
      validitycheck();
1041

    
1042
      ArrayList<V> res = new ArrayList<V>(size);
1043

    
1044
      return map<V>(mapper, res);
1045
    }
1046

    
1047
#if HASHINDEX
1048
    /// <summary>
1049
    /// Create a new list consisting of the results of mapping all items of this
1050
    /// list. The new list will use a specified item equalityComparer for the item type.
1051
    /// <para>The new list will be of type ArrayList</para>
1052
    /// </summary>
1053
    /// <exception cref="DuplicateNotAllowedException">If <code>mapper</code>
1054
    /// creates duplicates</exception>
1055
    /// <typeparam name="V">The type of items of the new list</typeparam>
1056
    /// <param name="mapper">The delegate defining the map.</param>
1057
    /// <param name="itemequalityComparer">The item equalityComparer to use for the new list</param>
1058
    /// <returns>The new list.</returns>
1059
#else
1060
    /// <summary>
1061
    /// Create a new list consisting of the results of mapping all items of this
1062
    /// list. The new list will use a specified item equalityComparer for the item type.
1063
    /// <para>The new list will be of type ArrayList</para>
1064
    /// </summary>
1065
    /// <typeparam name="V">The type of items of the new list</typeparam>
1066
    /// <param name="mapper">The delegate defining the map.</param>
1067
    /// <param name="itemequalityComparer">The item equalityComparer to use for the new list</param>
1068
    /// <returns>The new list.</returns>
1069
#endif
1070
    public virtual IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> itemequalityComparer)
1071
    {
1072
      validitycheck();
1073

    
1074
      ArrayList<V> res = new ArrayList<V>(size, itemequalityComparer);
1075

    
1076
      return map<V>(mapper, res);
1077
    }
1078

    
1079
    private IList<V> map<V>(Fun<T, V> mapper, ArrayList<V> res)
1080
    {
1081
      int stamp = this.stamp;
1082
      if (size > 0)
1083
        for (int i = 0; i < size; i++)
1084
        {
1085
          V mappeditem = mapper(array[offset + i]);
1086
          modifycheck(stamp);
1087
#if HASHINDEX
1088
          KeyValuePair<V, int> p = new KeyValuePair<V, int>(mappeditem, i);
1089
          if (res.itemIndex.FindOrAdd(ref p))
1090
            throw new ArgumentException("Mapped item already in indexed list");
1091
#endif
1092
          res.array[i] = mappeditem;
1093
        }
1094
      res.size = size;
1095
      return res;
1096
    }
1097

    
1098
    /// <summary>
1099
    /// Remove one item from the list: from the front if <code>FIFO</code>
1100
    /// is true, else from the back.
1101
    /// </summary>
1102
    /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
1103
    /// <returns>The removed item.</returns>
1104
    [Tested]
1105
    public virtual T Remove()
1106
    {
1107
      updatecheck();
1108
      if (size == 0)
1109
        throw new NoSuchItemException("List is empty");
1110

    
1111
      T item = removeAt(fIFO ? 0 : size - 1);
1112
      (underlying ?? this).raiseForRemove(item);
1113
      return item;
1114
    }
1115

    
1116
    /// <summary>
1117
    /// Remove one item from the fromnt of the list.
1118
    /// </summary>
1119
    /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
1120
    /// <returns>The removed item.</returns>
1121
    [Tested]
1122
    public virtual T RemoveFirst()
1123
    {
1124
      updatecheck();
1125
      if (size == 0)
1126
        throw new NoSuchItemException("List is empty");
1127

    
1128
      T item = removeAt(0);
1129
      (underlying ?? this).raiseForRemoveAt(offset, item);
1130
      return item;
1131
    }
1132

    
1133

    
1134
    /// <summary>
1135
    /// Remove one item from the back of the list.
1136
    /// </summary>
1137
    /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
1138
    /// <returns>The removed item.</returns>
1139
    [Tested]
1140
    public virtual T RemoveLast()
1141
    {
1142
      updatecheck();
1143
      if (size == 0)
1144
        throw new NoSuchItemException("List is empty");
1145

    
1146
      T item = removeAt(size - 1);
1147
      (underlying ?? this).raiseForRemoveAt(size + offset, item);
1148
      return item;
1149
    }
1150

    
1151
    /// <summary>
1152
    /// Create a list view on this list. 
1153
    /// </summary>
1154
    /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative
1155
    /// or the range does not fit within list.</exception>
1156
    /// <param name="start">The index in this list of the start of the view.</param>
1157
    /// <param name="count">The size of the view.</param>
1158
    /// <returns>The new list view.</returns>
1159
    [Tested]
1160
    public virtual IList<T> View(int start, int count)
1161
    {
1162
      validitycheck();
1163
      checkRange(start, count);
1164
      if (views == null)
1165
        views = new WeakViewList<ArrayList<T>>();
1166
      ArrayList<T> retval = (ArrayList<T>)MemberwiseClone();
1167

    
1168

    
1169
      retval.underlying = underlying != null ? underlying : this;
1170
      retval.offset = start + offset;
1171
      retval.size = count;
1172
      retval.myWeakReference = views.Add(retval);
1173
      return retval;
1174
    }
1175

    
1176
    /// <summary>
1177
    /// Create a list view on this list containing the (first) occurrence of a particular item.
1178
    /// <para>Returns <code>null</code> if the item is not in this list.</para>
1179
    /// </summary>
1180
    /// <param name="item">The item to find.</param>
1181
    /// <returns>The new list view.</returns>
1182
    [Tested]
1183
    public virtual IList<T> ViewOf(T item)
1184
    {
1185
      int index = indexOf(item);
1186
      if (index < 0)
1187
        return null;
1188
      return View(index, 1);
1189
    }
1190

    
1191

    
1192
    /// <summary>
1193
    /// Create a list view on this list containing the last occurrence of a particular item. 
1194
    /// <para>Returns <code>null</code> if the item is not in this list.</para>
1195
    /// </summary>
1196
    /// <param name="item">The item to find.</param>
1197
    /// <returns>The new list view.</returns>
1198
    [Tested]
1199
    public virtual IList<T> LastViewOf(T item)
1200
    {
1201
      int index = lastIndexOf(item);
1202
      if (index < 0)
1203
        return null;
1204
      return View(index, 1);
1205
    }
1206

    
1207
    /// <summary>
1208
    /// Null if this list is not a view.
1209
    /// </summary>
1210
    /// <value>Underlying list for view.</value>
1211
    [Tested]
1212
    public virtual IList<T> Underlying { [Tested]get { return underlying; } }
1213

    
1214

    
1215
    /// <summary>
1216
    /// </summary>
1217
    /// <value>Offset for this list view or 0 for an underlying list.</value>
1218
    [Tested]
1219
    public virtual int Offset { [Tested]get { return offset; } }
1220

    
1221
    /// <summary>
1222
    /// 
1223
    /// </summary>
1224
    /// <value></value>
1225
    public virtual bool IsValid { get { return isValid; } }
1226

    
1227
    /// <summary>
1228
    /// Slide this list view along the underlying list.
1229
    /// </summary>
1230
    /// <exception cref="NotAViewException"> if this list is not a view.</exception>
1231
    /// <exception cref="ArgumentOutOfRangeException"> if the operation
1232
    /// would bring either end of the view outside the underlying list.</exception>
1233
    /// <param name="offset">The signed amount to slide: positive to slide
1234
    /// towards the end.</param>
1235
    [Tested]
1236
    public virtual IList<T> Slide(int offset)
1237
    {
1238
      if (!TrySlide(offset, size))
1239
        throw new ArgumentOutOfRangeException();
1240
      return this;
1241
    }
1242

    
1243

    
1244
    /// <summary>
1245
    /// Slide this list view along the underlying list, changing its size.
1246
    /// </summary>
1247
    /// <exception cref="NotAViewException"> if this list is not a view.</exception>
1248
    /// <exception cref="ArgumentOutOfRangeException"> if the operation
1249
    /// would bring either end of the view outside the underlying list.</exception>
1250
    /// <param name="offset">The signed amount to slide: positive to slide
1251
    /// towards the end.</param>
1252
    /// <param name="size">The new size of the view.</param>
1253
    [Tested]
1254
    public virtual IList<T> Slide(int offset, int size)
1255
    {
1256
      if (!TrySlide(offset, size))
1257
        throw new ArgumentOutOfRangeException();
1258
      return this;
1259
    }
1260

    
1261
    /// <summary>
1262
    /// 
1263
    /// </summary>
1264
    /// <exception cref="NotAViewException"> if this list is not a view.</exception>
1265
    /// <param name="offset"></param>
1266
    /// <returns></returns>
1267
    [Tested]
1268
    public virtual bool TrySlide(int offset)
1269
    {
1270
      return TrySlide(offset, size);
1271
    }
1272

    
1273
    /// <summary>
1274
    /// 
1275
    /// </summary>
1276
    /// <exception cref="NotAViewException"> if this list is not a view.</exception>
1277
    /// <param name="offset"></param>
1278
    /// <param name="size"></param>
1279
    /// <returns></returns>
1280
    [Tested]
1281
    public virtual bool TrySlide(int offset, int size)
1282
    {
1283
      updatecheck();
1284
      if (underlying == null)
1285
        throw new NotAViewException("Not a view");
1286

    
1287
      int newoffset = this.offset + offset;
1288
      int newsize = size;
1289

    
1290
      if (newoffset < 0 || newsize < 0 || newoffset + newsize > underlyingsize)
1291
        return false;
1292

    
1293
      this.offset = newoffset;
1294
      this.size = newsize;
1295
      return true;
1296
    }
1297

    
1298
    /// <summary>
1299
    /// 
1300
    /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para>
1301
    /// </summary>
1302
    /// <param name="otherView"></param>
1303
    /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception>
1304
    /// <returns></returns>
1305
    public virtual IList<T> Span(IList<T> otherView)
1306
    {
1307
      if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this)))
1308
        throw new IncompatibleViewException();
1309
      if (otherView.Offset + otherView.Count - Offset < 0)
1310
        return null;
1311
      return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset);
1312
    }
1313

    
1314
    /// <summary>
1315
    /// Reverst the list so the items are in the opposite sequence order.
1316
    /// </summary>
1317
    [Tested]
1318
    public virtual void Reverse()
1319
    {
1320
      updatecheck();
1321
      if (size == 0)
1322
        return;
1323
      for (int i = 0, length = size / 2, end = offset + size - 1; i < length; i++)
1324
      {
1325
        T swap = array[offset + i];
1326

    
1327
        array[offset + i] = array[end - i];
1328
        array[end - i] = swap;
1329
      }
1330
#if HASHINDEX
1331
      reindex(offset, offset + size);
1332
#endif
1333
      //TODO: be more forgiving wrt. disposing
1334
      disposeOverlappingViews(true);
1335
      (underlying ?? this).raiseCollectionChanged();
1336
    }
1337

    
1338
    /// <summary>
1339
    /// Check if this list is sorted according to the default sorting order
1340
    /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class 
1341
    /// </summary>
1342
    /// <exception cref="NotComparableException">if T is not comparable</exception>
1343
    /// <returns>True if the list is sorted, else false.</returns>
1344
    [Tested]
1345
    public bool IsSorted() { return IsSorted(Comparer<T>.Default); }
1346

    
1347
    /// <summary>
1348
    /// Check if this list is sorted according to a specific sorting order.
1349
    /// </summary>
1350
    /// <param name="c">The comparer defining the sorting order.</param>
1351
    /// <returns>True if the list is sorted, else false.</returns>
1352
    [Tested]
1353
    public virtual bool IsSorted(SCG.IComparer<T> c)
1354
    {
1355
      validitycheck();
1356
      for (int i = offset + 1, end = offset + size; i < end; i++)
1357
        if (c.Compare(array[i - 1], array[i]) > 0)
1358
          return false;
1359

    
1360
      return true;
1361
    }
1362

    
1363
    /// <summary>
1364
    /// Sort the items of the list according to the default sorting order
1365
    /// for the item type T, as defined by the Comparer[T] class 
1366
    /// (<see cref="T:C5.Comparer`1"/>).
1367
    /// </summary>
1368
    /// <exception cref="InvalidOperationException">if T is not comparable</exception>
1369
    public virtual void Sort()
1370
    {
1371
      Sort(Comparer<T>.Default);
1372
    }
1373

    
1374

    
1375
    /// <summary>
1376
    /// Sort the items of the list according to a specific sorting order.
1377
    /// </summary>
1378
    /// <param name="comparer">The comparer defining the sorting order.</param>
1379
    [Tested]
1380
    public virtual void Sort(SCG.IComparer<T> comparer)
1381
    {
1382
      updatecheck();
1383
      if (size == 0)
1384
        return;
1385
      Sorting.IntroSort<T>(array, offset, size, comparer);
1386
      disposeOverlappingViews(false);
1387
#if HASHINDEX
1388
      reindex(offset, offset + size);
1389
#endif
1390
      (underlying ?? this).raiseCollectionChanged();
1391
    }
1392

    
1393

    
1394
    /// <summary>
1395
    /// Randomly shuffle the items of this list. 
1396
    /// </summary>
1397
    public virtual void Shuffle() { Shuffle(new C5Random()); }
1398

    
1399

    
1400
    /// <summary>
1401
    /// Shuffle the items of this list according to a specific random source.
1402
    /// </summary>
1403
    /// <param name="rnd">The random source.</param>
1404
    public virtual void Shuffle(Random rnd)
1405
    {
1406
      updatecheck();
1407
      if (size == 0)
1408
        return;
1409
      for (int i = offset, top = offset + size, end = top - 1; i < end; i++)
1410
      {
1411
        int j = rnd.Next(i, top);
1412
        if (j != i)
1413
        {
1414
          T tmp = array[i];
1415
          array[i] = array[j];
1416
          array[j] = tmp;
1417
        }
1418
      }
1419
      disposeOverlappingViews(false);
1420
#if HASHINDEX
1421
      reindex(offset, offset + size);
1422
#endif
1423
      (underlying ?? this).raiseCollectionChanged();
1424
    }
1425
    #endregion
1426

    
1427
    #region IIndexed<T> Members
1428

    
1429
    /// <summary>
1430
    /// Search for an item in the list going forwrds from the start.
1431
    /// </summary>
1432
    /// <param name="item">Item to search for.</param>
1433
    /// <returns>Index of item from start.</returns>
1434
    [Tested]
1435
    public virtual int IndexOf(T item) { validitycheck(); return indexOf(item); }
1436

    
1437

    
1438
    /// <summary>
1439
    /// Search for an item in the list going backwords from the end.
1440
    /// </summary>
1441
    /// <param name="item">Item to search for.</param>
1442
    /// <returns>Index of item from the end.</returns>
1443
    [Tested]
1444
    public virtual int LastIndexOf(T item) { validitycheck(); return lastIndexOf(item); }
1445

    
1446

    
1447
    /// <summary>
1448
    /// Remove the item at a specific position of the list.
1449
    /// </summary>
1450
    /// <exception cref="IndexOutOfRangeException"> if index is negative or
1451
    /// &gt;= the size of the collection.</exception>
1452
    /// <param name="index">The index of the item to remove.</param>
1453
    /// <returns>The removed item.</returns>
1454
    [Tested]
1455
    public virtual T RemoveAt(int index)
1456
    {
1457
      updatecheck();
1458
      if (index < 0 || index >= size)
1459
        throw new IndexOutOfRangeException("Index out of range for sequenced collection");
1460

    
1461
      T item = removeAt(index);
1462
      (underlying ?? this).raiseForRemoveAt(offset + index, item);
1463
      return item;
1464
    }
1465

    
1466

    
1467
    /// <summary>
1468
    /// Remove all items in an index interval.
1469
    /// </summary>
1470
    /// <exception cref="ArgumentOutOfRangeException">If <code>start</code>
1471
    /// and <code>count</code> does not describe a valid interval in the list</exception> 
1472
    /// <param name="start">The index of the first item to remove.</param>
1473
    /// <param name="count">The number of items to remove.</param>
1474
    [Tested]
1475
    public virtual void RemoveInterval(int start, int count)
1476
    {
1477
      updatecheck();
1478
      if (count == 0)
1479
        return;
1480
      checkRange(start, count);
1481
      start += offset;
1482
      fixViewsBeforeRemove(start, count);
1483
#if HASHINDEX
1484
      KeyValuePair<T, int> p = new KeyValuePair<T, int>();
1485
      for (int i = start, end = start + count; i < end; i++)
1486
      {
1487
        p.Key = array[i];
1488
        itemIndex.Remove(p);
1489
      }
1490
#endif
1491
      Array.Copy(array, start + count, array, start, underlyingsize - start - count);
1492
      addtosize(-count);
1493
      Array.Clear(array, underlyingsize, count);
1494
#if HASHINDEX
1495
      reindex(start);
1496
#endif
1497
      (underlying ?? this).raiseForRemoveInterval(start, count);
1498
    }
1499
    void raiseForRemoveInterval(int start, int count)
1500
    {
1501
      if (ActiveEvents != 0)
1502
      {
1503
        raiseCollectionCleared(size == 0, count, start);
1504
        raiseCollectionChanged();
1505
      }
1506
    }
1507
    #endregion
1508

    
1509
    #region ICollection<T> Members
1510

    
1511
    /// <summary>
1512
    /// The value is symbolic indicating the type of asymptotic complexity
1513
    /// in terms of the size of this collection (worst-case or amortized as
1514
    /// relevant).
1515
    /// </summary>
1516
    /// <value>Speed.Linear</value>
1517
    [Tested]
1518
    public virtual Speed ContainsSpeed
1519
    {
1520
      [Tested]
1521
      get
1522
      {
1523
#if HASHINDEX
1524
        return Speed.Constant;
1525
#else
1526
        return Speed.Linear;
1527
#endif
1528
      }
1529
    }
1530

    
1531
    /// <summary>
1532
    /// 
1533
    /// </summary>
1534
    /// <returns></returns>
1535
    [Tested]
1536
    public override int GetUnsequencedHashCode()
1537
    { validitycheck(); return base.GetUnsequencedHashCode(); }
1538

    
1539
    /// <summary>
1540
    /// 
1541
    /// </summary>
1542
    /// <param name="that"></param>
1543
    /// <returns></returns>
1544
    [Tested]
1545
    public override bool UnsequencedEquals(ICollection<T> that)
1546
    { validitycheck(); return base.UnsequencedEquals(that); }
1547

    
1548
    /// <summary>
1549
    /// Check if this collection contains (an item equivalent to according to the
1550
    /// itemequalityComparer) a particular value.
1551
    /// </summary>
1552
    /// <param name="item">The value to check for.</param>
1553
    /// <returns>True if the items is in this collection.</returns>
1554
    [Tested]
1555
    public virtual bool Contains(T item)
1556
    { validitycheck(); return indexOf(item) >= 0; }
1557

    
1558

    
1559
    /// <summary>
1560
    /// Check if this collection contains an item equivalent according to the
1561
    /// itemequalityComparer to a particular value. If so, return in the ref argument (a
1562
    /// binary copy of) the actual value found.
1563
    /// </summary>
1564
    /// <param name="item">The value to look for.</param>
1565
    /// <returns>True if the items is in this collection.</returns>
1566
    [Tested]
1567
    public virtual bool Find(ref T item)
1568
    {
1569
      validitycheck();
1570

    
1571
      int i;
1572

    
1573
      if ((i = indexOf(item)) >= 0)
1574
      {
1575
        item = array[offset + i];
1576
        return true;
1577
      }
1578

    
1579
      return false;
1580
    }
1581

    
1582

    
1583
    /// <summary>
1584
    /// Check if this collection contains an item equivalent according to the
1585
    /// itemequalityComparer to a particular value. If so, update the item in the collection 
1586
    /// to with a binary copy of the supplied value. This will only update the first 
1587
    /// mathching item.
1588
    /// </summary>
1589
    /// <param name="item">Value to update.</param>
1590
    /// <returns>True if the item was found and hence updated.</returns>
1591
    [Tested]
1592
    public virtual bool Update(T item)
1593
    {
1594
      T olditem;
1595
      return Update(item, out olditem);
1596
    }
1597

    
1598
    /// <summary>
1599
    /// 
1600
    /// </summary>
1601
    /// <param name="item"></param>
1602
    /// <param name="olditem"></param>
1603
    /// <returns></returns>
1604
    public virtual bool Update(T item, out T olditem)
1605
    {
1606
      updatecheck();
1607
      int i;
1608

    
1609
      if ((i = indexOf(item)) >= 0)
1610
      {
1611
        olditem = array[offset + i];
1612
        array[offset + i] = item;
1613
#if HASHINDEX
1614
        itemIndex.Update(new KeyValuePair<T, int>(item, offset + i));
1615
#endif
1616
        (underlying ?? this).raiseForUpdate(item, olditem);
1617
        return true;
1618
      }
1619

    
1620
      olditem = default(T);
1621
      return false;
1622
    }
1623

    
1624
    /// <summary>
1625
    /// Check if this collection contains an item equivalent according to the
1626
    /// itemequalityComparer to a particular value. If so, return in the ref argument (a
1627
    /// binary copy of) the actual value found. Else, add the item to the collection.
1628
    /// </summary>
1629
    /// <param name="item">The value to look for.</param>
1630
    /// <returns>True if the item was found (hence not added).</returns>
1631
    [Tested]
1632
    public virtual bool FindOrAdd(ref T item)
1633
    {
1634
      updatecheck();
1635
      if (Find(ref item))
1636
        return true;
1637

    
1638
      Add(item);
1639
      return false;
1640
    }
1641

    
1642

    
1643
    /// <summary>
1644
    /// Check if this collection contains an item equivalent according to the
1645
    /// itemequalityComparer to a particular value. If so, update the item in the collection 
1646
    /// to with a binary copy of the supplied value. This will only update the first 
1647
    /// mathching item.
1648
    /// </summary>
1649
    /// <param name="item">Value to update.</param>
1650
    /// <returns>True if the item was found and hence updated.</returns>
1651
    [Tested]
1652
    public virtual bool UpdateOrAdd(T item)
1653
    {
1654
      updatecheck();
1655
      if (Update(item))
1656
        return true;
1657

    
1658
      Add(item);
1659
      return false;
1660
    }
1661

    
1662
    /// <summary>
1663
    /// 
1664
    /// </summary>
1665
    /// <param name="item"></param>
1666
    /// <param name="olditem"></param>
1667
    /// <returns></returns>
1668
    public virtual bool UpdateOrAdd(T item, out T olditem)
1669
    {
1670
      updatecheck();
1671
      if (Update(item, out olditem))
1672
        return true;
1673

    
1674
      Add(item);
1675
      olditem = default(T);
1676
      return false;
1677
    }
1678

    
1679
    /// <summary>
1680
    /// Remove a particular item from this list. The item will be searched 
1681
    /// for from the end of the list if <code>FIFO == false</code> (the default), 
1682
    /// else from the start.
1683
    /// </summary>
1684
    /// <param name="item">The value to remove.</param>
1685
    /// <returns>True if the item was found (and removed).</returns>
1686
    [Tested]
1687
    public virtual bool Remove(T item)
1688
    {
1689
      updatecheck();
1690

    
1691
      int i = fIFO ? indexOf(item) : lastIndexOf(item);
1692

    
1693
      if (i < 0)
1694
        return false;
1695

    
1696
      T removeditem = removeAt(i);
1697
      (underlying ?? this).raiseForRemove(removeditem);
1698
      return true;
1699
    }
1700

    
1701

    
1702
    /// <summary>
1703
    /// Remove the first copy of a particular item from this collection if found.
1704
    /// If an item was removed, report a binary copy of the actual item removed in 
1705
    /// the argument. The item will be searched 
1706
    /// for from the end of the list if <code>FIFO == false</code> (the default), 
1707
    /// else from the start.
1708
    /// </summary>
1709
    /// <param name="item">The value to remove.</param>
1710
    /// <param name="removeditem">The removed value.</param>
1711
    /// <returns>True if the item was found (and removed).</returns>
1712
    [Tested]
1713
    public virtual bool Remove(T item, out T removeditem)
1714
    {
1715
      updatecheck();
1716

    
1717
      int i = fIFO ? indexOf(item) : lastIndexOf(item);
1718

    
1719
      if (i < 0)
1720
      {
1721
        removeditem = default(T);
1722
        return false;
1723
      }
1724

    
1725
      removeditem = removeAt(i);
1726
      (underlying ?? this).raiseForRemove(removeditem);
1727
      return true;
1728
    }
1729

    
1730

    
1731
    //TODO: remove from end or according to FIFO?
1732
    /// <summary>
1733
    /// Remove all items in another collection from this one, taking multiplicities into account.
1734
    /// Matching items will be removed from the front. Current implementation is not optimal.
1735
    /// </summary>
1736
    /// <typeparam name="U"></typeparam>
1737
    /// <param name="items">The items to remove.</param>
1738
    [Tested]
1739
    public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
1740
    {
1741
      updatecheck();
1742
      if (size == 0)
1743
        return;
1744
      //TODO: reactivate the old code for small sizes
1745
      HashBag<T> toremove = new HashBag<T>(itemequalityComparer);
1746
      toremove.AddAll(items);
1747
      if (toremove.Count == 0)
1748
        return;
1749
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
1750
      bool mustFire = raiseHandler.MustFire;
1751
      ViewHandler viewHandler = new ViewHandler(this);
1752
      int j = offset;
1753
      int removed = 0;
1754
      int i = offset, end = offset + size;
1755
#if HASHINDEX
1756
      KeyValuePair<T, int> p = new KeyValuePair<T, int>();
1757
#endif
1758
      while (i < end)
1759
      {
1760
        T item;
1761
        //pass by a stretch of nodes
1762
        while (i < end && !toremove.Contains(item = array[i]))
1763
        {
1764
#if HASHINDEX
1765
          if (j < i)
1766
          {
1767
            p.Key = item;
1768
            p.Value = j;
1769
            itemIndex.Update(p);
1770
          }
1771
#endif
1772
          //if (j<i)
1773
          array[j] = item;
1774
          i++; j++;
1775
        }
1776
        viewHandler.skipEndpoints(removed, i);
1777
        //Remove a stretch of nodes
1778
        while (i < end && toremove.Remove(item = array[i]))
1779
        {
1780
#if HASHINDEX
1781
          p.Key = item;
1782
          itemIndex.Remove(p);
1783
#endif
1784
          if (mustFire)
1785
            raiseHandler.Remove(item);
1786
          removed++;
1787
          i++;
1788
          viewHandler.updateViewSizesAndCounts(removed, i);
1789
        }
1790
      }
1791
      if (removed == 0)
1792
        return;
1793
      viewHandler.updateViewSizesAndCounts(removed, underlyingsize);
1794
      Array.Copy(array, offset + size, array, j, underlyingsize - offset - size);
1795
      addtosize(-removed);
1796
      Array.Clear(array, underlyingsize, removed);
1797
#if HASHINDEX
1798
      reindex(j);
1799
#endif
1800
      if (mustFire)
1801
        raiseHandler.Raise();
1802
    }
1803

    
1804
    /// <summary>
1805
    /// 
1806
    /// </summary>
1807
    /// <param name="predicate"></param>
1808
    void RemoveAll(Fun<T, bool> predicate)
1809
    {
1810
      updatecheck();
1811
      if (size == 0)
1812
        return;
1813
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
1814
      bool mustFire = raiseHandler.MustFire;
1815
      ViewHandler viewHandler = new ViewHandler(this);
1816
      int j = offset;
1817
      int removed = 0;
1818
      int i = offset, end = offset + size;
1819
#if HASHINDEX
1820
      KeyValuePair<T, int> p = new KeyValuePair<T, int>();
1821
#endif
1822
      while (i < end)
1823
      {
1824
        T item;
1825
        //pass by a stretch of nodes
1826
        while (i < end && !predicate(item = array[i]))
1827
        {
1828
          updatecheck();
1829
#if HASHINDEX
1830
          if (j < i)
1831
          {
1832
            p.Key = item;
1833
            p.Value = j;
1834
            itemIndex.Update(p);
1835
          }
1836
#endif
1837
          //if (j<i)
1838
          array[j] = item;
1839
          i++; j++;
1840
        }
1841
        updatecheck();
1842
        viewHandler.skipEndpoints(removed, i);
1843
        //Remove a stretch of nodes
1844
        while (i < end && predicate(item = array[i]))
1845
        {
1846
          updatecheck();
1847
#if HASHINDEX
1848
          p.Key = item;
1849
          itemIndex.Remove(p);
1850
#endif
1851
          if (mustFire)
1852
            raiseHandler.Remove(item);
1853
          removed++;
1854
          i++;
1855
          viewHandler.updateViewSizesAndCounts(removed, i);
1856
        }
1857
        updatecheck();
1858
      }
1859
      if (removed == 0)
1860
        return;
1861
      viewHandler.updateViewSizesAndCounts(removed, underlyingsize);
1862
      Array.Copy(array, offset + size, array, j, underlyingsize - offset - size);
1863
      addtosize(-removed);
1864
      Array.Clear(array, underlyingsize, removed);
1865
#if HASHINDEX
1866
      reindex(j);
1867
#endif
1868
      if (mustFire)
1869
        raiseHandler.Raise();
1870
    }
1871

    
1872
    /// <summary>
1873
    /// Remove all items from this collection, resetting internal array size.
1874
    /// </summary>
1875
    [Tested]
1876
    public override void Clear()
1877
    {
1878
      if (underlying == null)
1879
      {
1880
        updatecheck();
1881
        if (size == 0)
1882
          return;
1883
        int oldsize = size;
1884
        fixViewsBeforeRemove(0, size);
1885
#if HASHINDEX
1886
        itemIndex.Clear();
1887
#endif
1888
        array = new T[8];
1889
        size = 0;
1890
        (underlying ?? this).raiseForRemoveInterval(offset, oldsize);
1891
      }
1892
      else
1893
        RemoveInterval(0, size);
1894
    }
1895

    
1896
    /// <summary>
1897
    /// Remove all items not in some other collection from this one, taking multiplicities into account.
1898
    /// Items are retained front first.  
1899
    /// </summary>
1900
    /// <typeparam name="U"></typeparam>
1901
    /// <param name="items">The items to retain.</param>
1902
    [Tested]
1903
    public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
1904
    {
1905
      updatecheck();
1906
      if (size == 0)
1907
        return;
1908
      HashBag<T> toretain = new HashBag<T>(itemequalityComparer);
1909
      toretain.AddAll(items);
1910
      if (toretain.Count == 0)
1911
      {
1912
        Clear();
1913
        return;
1914
      }
1915
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
1916
      bool mustFire = raiseHandler.MustFire;
1917
      ViewHandler viewHandler = new ViewHandler(this);
1918
      int j = offset;
1919
      int removed = 0;
1920
      int i = offset, end = offset + size;
1921
#if HASHINDEX
1922
      KeyValuePair<T, int> p = new KeyValuePair<T, int>();
1923
#endif
1924
      while (i < end)
1925
      {
1926
        T item;
1927
        //pass by a stretch of nodes
1928
        while (i < end && toretain.Remove(item = array[i]))
1929
        {
1930
#if HASHINDEX
1931
          if (j < i)
1932
          {
1933
            p.Key = item;
1934
            p.Value = j;
1935
            itemIndex.Update(p);
1936
          }
1937
#endif
1938
          //if (j<i)
1939
          array[j] = item;
1940
          i++; j++;
1941
        }
1942
        viewHandler.skipEndpoints(removed, i);
1943
        //Remove a stretch of nodes
1944
        while (i < end && !toretain.Contains(item = array[i]))
1945
        {
1946
#if HASHINDEX
1947
          p.Key = item;
1948
          itemIndex.Remove(p);
1949
#endif
1950
          if (mustFire)
1951
            raiseHandler.Remove(item);
1952
          removed++;
1953
          i++;
1954
          viewHandler.updateViewSizesAndCounts(removed, i);
1955
        }
1956
      }
1957
      if (removed == 0)
1958
        return;
1959
      viewHandler.updateViewSizesAndCounts(removed, underlyingsize);
1960
      Array.Copy(array, offset + size, array, j, underlyingsize - offset - size);
1961
      addtosize(-removed);
1962
      Array.Clear(array, underlyingsize, removed);
1963
#if HASHINDEX
1964
      reindex(j);
1965
#endif
1966
      raiseHandler.Raise();
1967
    }
1968

    
1969
    /// <summary>
1970
    /// 
1971
    /// </summary>
1972
    /// <param name="predicate"></param>
1973
    void RetainAll(Fun<T, bool> predicate)
1974
    {
1975
      updatecheck();
1976
      if (size == 0)
1977
        return;
1978
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
1979
      bool mustFire = raiseHandler.MustFire;
1980
      ViewHandler viewHandler = new ViewHandler(this);
1981
      int j = offset;
1982
      int removed = 0;
1983
      int i = offset, end = offset + size;
1984
#if HASHINDEX
1985
      KeyValuePair<T, int> p = new KeyValuePair<T, int>();
1986
#endif
1987
      while (i < end)
1988
      {
1989
        T item;
1990
        //pass by a stretch of nodes
1991
        while (i < end && predicate(item = array[i]))
1992
        {
1993
          updatecheck();
1994
#if HASHINDEX
1995
          if (j < i)
1996
          {
1997
            p.Key = item;
1998
            p.Value = j;
1999
            itemIndex.Update(p);
2000
          }
2001
#endif
2002
          //if (j<i)
2003
          array[j] = item;
2004
          i++; j++;
2005
        }
2006
        updatecheck();
2007
        viewHandler.skipEndpoints(removed, i);
2008
        //Remove a stretch of nodes
2009
        while (i < end && !predicate(item = array[i]))
2010
        {
2011
          updatecheck();
2012
#if HASHINDEX
2013
          p.Key = item;
2014
          itemIndex.Remove(p);
2015
#endif
2016
          if (mustFire)
2017
            raiseHandler.Remove(item);
2018
          removed++;
2019
          i++;
2020
          viewHandler.updateViewSizesAndCounts(removed, i);
2021
        }
2022
        updatecheck();
2023
      }
2024
      if (removed == 0)
2025
        return;
2026
      viewHandler.updateViewSizesAndCounts(removed, underlyingsize);
2027
      Array.Copy(array, offset + size, array, j, underlyingsize - offset - size);
2028
      addtosize(-removed);
2029
      Array.Clear(array, underlyingsize, removed);
2030
#if HASHINDEX
2031
      reindex(j);
2032
#endif
2033
      raiseHandler.Raise();
2034
    }
2035

    
2036

    
2037
    /// <summary>
2038
    /// Check if this collection contains all the values in another collection,
2039
    /// taking multiplicities into account.
2040
    /// Current implementation is not optimal.
2041
    /// </summary>
2042
    /// <param name="items">The </param>
2043
    /// <typeparam name="U"></typeparam>
2044
    /// <returns>True if all values in <code>items</code>is in this collection.</returns>
2045
    [Tested]
2046
    public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
2047
    {
2048
      validitycheck();
2049
#if HASHINDEX
2050
      foreach (T item in items)
2051
        if (indexOf(item) < 0)
2052
          return false;
2053

    
2054
      return true;
2055
#else
2056
      //TODO: use aux hash bag to obtain linear time procedure
2057
      HashBag<T> tomatch = new HashBag<T>(itemequalityComparer);
2058
      tomatch.AddAll(items);
2059
      if (tomatch.Count == 0)
2060
        return true;
2061
      for (int i = offset, end = offset + size; i < end; i++)
2062
      {
2063
        tomatch.Remove(array[i]);
2064
        if (tomatch.Count == 0)
2065
          return true;
2066
      }
2067
      return false;
2068
#endif
2069
    }
2070

    
2071

    
2072
    /// <summary>
2073
    /// Count the number of items of the collection equal to a particular value.
2074
    /// Returns 0 if and only if the value is not in the collection.
2075
    /// </summary>
2076
    /// <param name="item">The value to count.</param>
2077
    /// <returns>The number of copies found.</returns>
2078
    [Tested]
2079
    public virtual int ContainsCount(T item)
2080
    {
2081
      validitycheck();
2082
#if HASHINDEX
2083
      return indexOf(item) >= 0 ? 1 : 0;
2084
#else
2085
      int count = 0;
2086
      for (int i = 0; i < size; i++)
2087
        if (equals(item, array[offset + i]))
2088
          count++;
2089
      return count;
2090
#endif
2091
    }
2092

    
2093
    /// <summary>
2094
    /// 
2095
    /// </summary>
2096
    /// <returns></returns>
2097
    public virtual ICollectionValue<T> UniqueItems()
2098
    {
2099
#if HASHINDEX
2100
      return this;
2101
#else
2102
      HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
2103
      hashbag.AddAll(this);
2104
      return hashbag.UniqueItems();
2105
#endif
2106
    }
2107

    
2108
    /// <summary>
2109
    /// 
2110
    /// </summary>
2111
    /// <returns></returns>
2112
    public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
2113
    {
2114
#if HASHINDEX
2115
      return new MultiplicityOne<T>(this);
2116
#else
2117
      HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
2118
      hashbag.AddAll(this);
2119
      return hashbag.ItemMultiplicities();
2120
#endif
2121
    }
2122

    
2123

    
2124

    
2125

    
2126

    
2127
    /// <summary>
2128
    /// Remove all items equal to a given one.
2129
    /// </summary>
2130
    /// <param name="item">The value to remove.</param>
2131
    [Tested]
2132
    public virtual void RemoveAllCopies(T item)
2133
    {
2134
#if HASHINDEX
2135
      Remove(item);
2136
#else
2137
      updatecheck();
2138
      if (size == 0)
2139
        return;
2140
      RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
2141
      bool mustFire = raiseHandler.MustFire;
2142
      ViewHandler viewHandler = new ViewHandler(this);
2143
      int j = offset;
2144
      int removed = 0;
2145
      int i = offset, end = offset + size;
2146
      while (i < end)
2147
      {
2148
        //pass by a stretch of nodes
2149
        while (i < end && !equals(item, array[i]))
2150
          array[j++] = array[i++];
2151
        viewHandler.skipEndpoints(removed, i);
2152
        //Remove a stretch of nodes
2153
        while (i < end && equals(item, array[i]))
2154
        {
2155
          if (mustFire)
2156
            raiseHandler.Remove(array[i]);
2157
          removed++;
2158
          i++;
2159
          viewHandler.updateViewSizesAndCounts(removed, i);
2160
        }
2161
      }
2162
      if (removed == 0)
2163
        return;
2164
      viewHandler.updateViewSizesAndCounts(removed, underlyingsize);
2165
      Array.Copy(array, offset + size, array, j, underlyingsize - offset - size);
2166
      addtosize(-removed);
2167
      Array.Clear(array, underlyingsize, removed);
2168
      raiseHandler.Raise();
2169
#endif
2170
    }
2171

    
2172

    
2173
    //TODO: check views
2174
    /// <summary>
2175
    /// Check the integrity of the internal data structures of this array list.
2176
    /// </summary>
2177
    /// <returns>True if check does not fail.</returns>
2178
    [Tested]
2179
    public override bool Check()
2180
    {
2181
      bool retval = true;
2182

    
2183
      if (underlyingsize > array.Length)
2184
      {
2185
        Console.WriteLine("underlyingsize ({0}) > array.Length ({1})", size, array.Length);
2186
        return false;
2187
      }
2188

    
2189
      if (offset + size > underlyingsize)
2190
      {
2191
        Console.WriteLine("offset({0})+size({1}) > underlyingsize ({2})", offset, size, underlyingsize);
2192
        return false;
2193
      }
2194

    
2195
      if (offset < 0)
2196
      {
2197
        Console.WriteLine("offset({0}) < 0", offset);
2198
        return false;
2199
      }
2200

    
2201
      for (int i = 0; i < underlyingsize; i++)
2202
      {
2203
        if ((object)(array[i]) == null)
2204
        {
2205
          Console.WriteLine("Bad element: null at (base)index {0}", i);
2206
          retval = false;
2207
        }
2208
      }
2209

    
2210
      for (int i = underlyingsize, length = array.Length; i < length; i++)
2211
      {
2212
        if (!equals(array[i], default(T)))
2213
        {
2214
          Console.WriteLine("Bad element: != default(T) at (base)index {0}", i);
2215
          retval = false;
2216
        }
2217
      }
2218

    
2219
      {
2220
        ArrayList<T> u = underlying ?? this;
2221
        if (u.views != null)
2222
          foreach (ArrayList<T> v in u.views)
2223
          {
2224
            if (u.array != v.array)
2225
            {
2226
              Console.WriteLine("View from {0} of length has different base array than the underlying list", v.offset, v.size);
2227
              retval = false;
2228
            }
2229
          }
2230
      }
2231

    
2232

    
2233
#if HASHINDEX
2234
      if (underlyingsize != itemIndex.Count)
2235
      {
2236
        Console.WriteLine("size ({0})!= index.Count ({1})", size, itemIndex.Count);
2237
        retval = false;
2238
      }
2239

    
2240
      for (int i = 0; i < underlyingsize; i++)
2241
      {
2242
        KeyValuePair<T, int> p = new KeyValuePair<T, int>(array[i]);
2243

    
2244
        if (!itemIndex.Find(ref p))
2245
        {
2246
          Console.WriteLine("Item {1} at {0} not in hashindex", i, array[i]);
2247
          retval = false;
2248
        }
2249

    
2250
        if (p.Value != i)
2251
        {
2252
          Console.WriteLine("Item {1} at {0} has hashindex {2}", i, array[i], p.Value);
2253
          retval = false;
2254
        }
2255
      }
2256
#endif
2257
      return retval;
2258
    }
2259

    
2260
    #endregion
2261

    
2262
    #region IExtensible<T> Members
2263

    
2264
    /// <summary>
2265
    /// 
2266
    /// </summary>
2267
    /// <value>True, indicating array list has bag semantics.</value>
2268
    [Tested]
2269
    public virtual bool AllowsDuplicates
2270
    {
2271
      [Tested]
2272
      get
2273
      {
2274
#if HASHINDEX
2275
        return false;
2276
#else
2277
        return true;
2278
#endif
2279
      }
2280
    }
2281

    
2282
    /// <summary>
2283
    /// By convention this is true for any collection with set semantics.
2284
    /// </summary>
2285
    /// <value>True if only one representative of a group of equal items 
2286
    /// is kept in the collection together with the total count.</value>
2287
    public virtual bool DuplicatesByCounting
2288
    {
2289
      get
2290
      {
2291
#if HASHINDEX
2292
        return true;
2293
#else
2294
        return false;
2295
#endif
2296
      }
2297
    }
2298

    
2299
    /// <summary>
2300
    /// Add an item to end of this list.
2301
    /// </summary>
2302
    /// <param name="item">The item to add.</param>
2303
    /// <returns>True</returns>
2304
    [Tested]
2305
    public virtual bool Add(T item)
2306
    {
2307
      updatecheck();
2308
#if HASHINDEX
2309
      KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, size + offset);
2310
      if (itemIndex.FindOrAdd(ref p))
2311
        return false;
2312
#endif
2313
      baseinsert(size, item);
2314
#if HASHINDEX
2315
      reindex(size + offset);
2316
#endif
2317
      (underlying ?? this).raiseForAdd(item);
2318
      return true;
2319
    }
2320

    
2321

    
2322
    /// <summary>
2323
    /// Add the elements from another collection to this collection.
2324
    /// </summary>
2325
    /// <typeparam name="U"></typeparam>
2326
    /// <param name="items"></param>
2327
    [Tested]
2328
    public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
2329
    {
2330
      updatecheck();
2331
      int toadd = EnumerableBase<U>.countItems(items);
2332
      if (toadd == 0)
2333
        return;
2334

    
2335
      if (toadd + underlyingsize > array.Length)
2336
        expand(toadd + underlyingsize, underlyingsize);
2337

    
2338
      int i = size + offset;
2339
      if (underlyingsize > i)
2340
        Array.Copy(array, i, array, i + toadd, underlyingsize - i);
2341
      try
2342
      {
2343
        foreach (T item in items)
2344
        {
2345
#if HASHINDEX
2346
          KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, i);
2347
          if (itemIndex.FindOrAdd(ref p))
2348
            continue;
2349
#endif
2350
          array[i++] = item;
2351
        }
2352
      }
2353
      finally
2354
      {
2355
        int added = i - size - offset;
2356
        if (added < toadd)
2357
        {
2358
          Array.Copy(array, size + offset + toadd, array, i, underlyingsize - size - offset);
2359
          Array.Clear(array, underlyingsize + added, toadd - added);
2360
        }
2361
        if (added > 0)
2362
        {
2363
          addtosize(added);
2364
#if HASHINDEX
2365
          reindex(i);
2366
#endif
2367
          fixViewsAfterInsert(added, i - added);
2368
          (underlying ?? this).raiseForAddAll(i - added, added);
2369
        }
2370
      }
2371
    }
2372
    private void raiseForAddAll(int start, int added)
2373
    {
2374
      if ((ActiveEvents & EventTypeEnum.Added) != 0)
2375
        for (int i = start, end = start + added; i < end; i++)
2376
          raiseItemsAdded(array[i], 1);
2377
      raiseCollectionChanged();
2378
    }
2379

    
2380
    #endregion
2381

    
2382
    #region IDirectedEnumerable<T> Members
2383

    
2384
    /// <summary>
2385
    /// Create a collection containing the same items as this collection, but
2386
    /// whose enumerator will enumerate the items backwards. The new collection
2387
    /// will become invalid if the original is modified. Method typicaly used as in
2388
    /// <code>foreach (T x in coll.Backwards()) {...}</code>
2389
    /// </summary>
2390
    /// <returns>The backwards collection.</returns>
2391
    [Tested]
2392
    IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
2393

    
2394
    #endregion
2395

    
2396
    #region ICollectionValue<T> Members
2397
    /// <summary>
2398
    /// 
2399
    /// </summary>
2400
    /// <value>The number of items in this collection</value>
2401
    [Tested]
2402
    public override int Count { [Tested]get { validitycheck(); return size; } }
2403
    #endregion
2404

    
2405
    #region IEnumerable<T> Members
2406
    //TODO: make tests of all calls on a disposed view throws the right exception! (Which should be C5.InvalidListViewException)
2407
    /// <summary>
2408
    /// Create an enumerator for the collection
2409
    /// </summary>
2410
    /// <returns>The enumerator</returns>
2411
    [Tested]
2412
    public override SCG.IEnumerator<T> GetEnumerator()
2413
    {
2414
      validitycheck();
2415
      return base.GetEnumerator();
2416
    }
2417
    #endregion
2418

    
2419
#if HASHINDEX
2420
#else
2421
    #region IStack<T> Members
2422

    
2423
    /// <summary>
2424
    /// Push an item to the top of the stack.
2425
    /// </summary>
2426
    /// <param name="item">The item</param>
2427
    [Tested]
2428
    public virtual void Push(T item)
2429
    {
2430
      InsertLast(item);
2431
    }
2432

    
2433
    /// <summary>
2434
    /// Pop the item at the top of the stack from the stack.
2435
    /// </summary>
2436
    /// <returns>The popped item.</returns>
2437
    [Tested]
2438
    public virtual T Pop()
2439
    {
2440
      return RemoveLast();
2441
    }
2442

    
2443
    #endregion
2444

    
2445
    #region IQueue<T> Members
2446

    
2447
    /// <summary>
2448
    /// Enqueue an item at the back of the queue. 
2449
    /// </summary>
2450
    /// <param name="item">The item</param>
2451
    [Tested]
2452
    public virtual void Enqueue(T item)
2453
    {
2454
      InsertLast(item);
2455
    }
2456

    
2457
    /// <summary>
2458
    /// Dequeue an item from the front of the queue.
2459
    /// </summary>
2460
    /// <returns>The item</returns>
2461
    [Tested]
2462
    public virtual T Dequeue()
2463
    {
2464
      return RemoveFirst();
2465
    }
2466

    
2467
    #endregion
2468
#endif
2469
    #region IDisposable Members
2470

    
2471
    /// <summary>
2472
    /// Invalidate this list. If a view, just invalidate the view. 
2473
    /// If not a view, invalidate the list and all views on it.
2474
    /// </summary>
2475
    public virtual void Dispose()
2476
    {
2477
      Dispose(false);
2478
    }
2479

    
2480
    void Dispose(bool disposingUnderlying)
2481
    {
2482
      if (isValid)
2483
      {
2484
        if (underlying != null)
2485
        {
2486
          isValid = false;
2487
          if (!disposingUnderlying && views != null)
2488
            views.Remove(myWeakReference);
2489
          underlying = null;
2490
          views = null;
2491
          myWeakReference = null;
2492
        }
2493
        else
2494
        {
2495
          //isValid = false;
2496
          if (views != null)
2497
            foreach (ArrayList<T> view in views)
2498
              view.Dispose(true);
2499
          Clear();
2500
        }
2501
      }
2502
    }
2503

    
2504
    #endregion
2505

    
2506
    #region ICloneable Members
2507

    
2508
    /// <summary>
2509
    /// Make a shallow copy of this ArrayList.
2510
    /// </summary>
2511
    /// <returns></returns>
2512
    public virtual object Clone()
2513
    {
2514
      ArrayList<T> clone = new ArrayList<T>(size, itemequalityComparer);
2515
      clone.AddAll(this);
2516
      return clone;
2517
    }
2518

    
2519
    #endregion
2520

    
2521
    #region ISerializable Members
2522
    /*
2523
    /// <summary>
2524
    /// 
2525
    /// </summary>
2526
    /// <param name="info"></param>
2527
    /// <param name="context"></param>
2528
    public ArrayList(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) :
2529
      this(info.GetInt32("sz"),(SCG.IEqualityComparer<T>)(info.GetValue("eq",typeof(SCG.IEqualityComparer<T>))))
2530
    {
2531
      size = info.GetInt32("sz");
2532
      for (int i = 0; i < size; i++)
2533
      {
2534
        array[i] = (T)(info.GetValue("elem" + i,typeof(T)));
2535
      }
2536
#if HASHINDEX
2537
      reindex(0);
2538
#endif      
2539
    }
2540

    
2541
    /// <summary>
2542
    /// 
2543
    /// </summary>
2544
    /// <param name="info"></param>
2545
    /// <param name="context"></param>
2546
    public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
2547
    {
2548
      info.AddValue("sz", size);
2549
      info.AddValue("eq", EqualityComparer);
2550
      for (int i = 0; i < size; i++)
2551
			{
2552
        info.AddValue("elem" + i, array[i + offset]);
2553
      }
2554
    }
2555
*/
2556
    #endregion
2557

    
2558
    #region System.Collections.Generic.IList<T> Members
2559

    
2560
    void System.Collections.Generic.IList<T>.RemoveAt(int index)
2561
    {
2562
      RemoveAt(index);
2563
    }
2564

    
2565
    void System.Collections.Generic.ICollection<T>.Add(T item)
2566
    {
2567
      Add(item);
2568
    }
2569

    
2570
    #endregion
2571

    
2572
    #region System.Collections.ICollection Members
2573

    
2574
    bool System.Collections.ICollection.IsSynchronized
2575
    {
2576
      get { return false; }
2577
    }
2578

    
2579
    [Obsolete]
2580
    Object System.Collections.ICollection.SyncRoot
2581
    {
2582
      get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : array; }
2583
    }
2584

    
2585
    void System.Collections.ICollection.CopyTo(Array arr, int index)
2586
    {
2587
      if (index < 0 || index + Count > arr.Length)
2588
        throw new ArgumentOutOfRangeException();
2589

    
2590
      foreach (T item in this)
2591
        arr.SetValue(item, index++);
2592
    }
2593

    
2594
    #endregion
2595

    
2596
    #region System.Collections.IList Members
2597

    
2598
    Object System.Collections.IList.this[int index]
2599
    {
2600
      get { return this[index]; }
2601
      set { this[index] = (T)value; }
2602
    }
2603

    
2604
    int System.Collections.IList.Add(Object o)
2605
    {
2606
      bool added = Add((T)o);
2607
      // What position to report if item not added? SC.IList.Add doesn't say
2608
      return added ? Count - 1 : -1;
2609
    }
2610

    
2611
    bool System.Collections.IList.Contains(Object o)
2612
    {
2613
      return Contains((T)o);
2614
    }
2615

    
2616
    int System.Collections.IList.IndexOf(Object o)
2617
    {
2618
      return Math.Max(-1, IndexOf((T)o));
2619
    }
2620

    
2621
    void System.Collections.IList.Insert(int index, Object o)
2622
    {
2623
      Insert(index, (T)o);
2624
    }
2625

    
2626
    void System.Collections.IList.Remove(Object o)
2627
    {
2628
      Remove((T)o);
2629
    }
2630

    
2631
    void System.Collections.IList.RemoveAt(int index)
2632
    {
2633
      RemoveAt(index);
2634
    }
2635

    
2636
    #endregion
2637
  }
2638
}