/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using JetBrains.Omea.Algorithms; using System.Text; using JetBrains.Omea.Base; namespace JetBrains.Omea.Containers { /** * ArrayList of ints */ public class IntArrayList: ICollection, ICloneable { private int[] _items; private int _size; private int _version; private const int InitialCapacity = 2; public IntArrayList() { _items = new int [ InitialCapacity ]; } public IntArrayList( int capacity ) { _items = new int [ AdjustCapacity( capacity ) ]; } public IntArrayList( ICollection collection ) { _items = new int [ AdjustCapacity( collection.Count ) ]; AddRange( collection ); } private IntArrayList( int[] items, int size ) { _items = items; _size = size; } #region ICollection Members public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return null; } } public int Count { get { return _size; } } public void CopyTo( Array array, int arrayIndex ) { Array.Copy( _items, 0, array, arrayIndex, _size ); } private static int[] _emptyIntArray = new int[ 0 ]; public int[] ToArray() { if( _size == 0 ) { return _emptyIntArray; } int[] result = new int [_size]; Array.Copy( _items, 0, result, 0, _size ); return result; } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return new IntArrayListEnumerator( this ); } public IntArrayListEnumerator GetEnumerator() { return new IntArrayListEnumerator( this ); } #endregion #region ICloneable Members public object Clone() { IntArrayList result; if( _size == 0 ) { result = new IntArrayList(); } else { result = new IntArrayList( _size ); result._size = _size; Array.Copy( _items, 0, result._items, 0, _size ); } result._version = _version; return result; } #endregion public void Add( int value ) { if ( _size == _items.Length ) EnsureCapacity( _size + 1 ); _items [_size] = value; _size++; _version++; } public void Add( int value, int count ) { EnsureCapacity( _size + count ); while( --count >= 0 ) { _items[ _size++ ] = value; } _version++; } public void AddRange( ICollection c ) { int count = c.Count; EnsureCapacity( _size + count ); c.CopyTo( _items, _size ); _size += count; _version++; } public void Insert( int index, int value ) { if(( index < 0 ) || ( index > _size )) throw new ArgumentOutOfRangeException( "index", index, "Insertion index is out of bounds: current size = " + _size.ToString() ); if ( _size == _items.Length ) EnsureCapacity( _size+1 ); if ( index < _size ) Array.Copy( _items, index, _items, index + 1, _size - index ); _items [index] = value; _size++; _version++; } public void Remove( int value ) { int index = IndexOf( value ); if ( index >= 0 ) { RemoveAt( index ); } } public void RemoveRange( int index, int count ) { if ( index < 0 ) throw new ArgumentOutOfRangeException( "index" ); if ( count < 0 ) throw new ArgumentOutOfRangeException( "count" ); if ( _size - index < count ) throw new ArgumentException( "_size - index < count" ); if (count > 0) { _size -= count; if (index < _size) { Array.Copy(_items, index + count, _items, index, _size - index); } _version++; } } public int IndexOf( int value ) { int size = _size; if( ( size & 1 ) == 0) { for( int i = 0; i < size; ++i ) { if( _items[ i ] == value ) return i; } } else { for( int i = size - 1; i >= 0; --i ) { if( _items[ i ] == value ) return i; } } return -1; } public void Reverse() { int last = _size - 1; int count = (last + 1) / 2; for ( int i = 0; i < count; i++ ) { int temp = _items[i]; _items[i] = _items[last - i]; _items[last - i] = temp; } } public void RemoveAt( int index ) { if ( index < 0 || index >= _size ) throw new ArgumentOutOfRangeException( "index" ); _size--; if (index < _size) { Array.Copy(_items, index + 1, _items, index, _size - index); } _version++; } public int Capacity { get { return _items.Length; } set { if( value <= 0 ) throw new ArgumentOutOfRangeException( "value" ); int[] newData = new int [ AdjustCapacity( value ) ]; _size = Math.Min( _size, newData.Length ); Array.Copy( _items, 0, newData, 0, _size ); _items = newData; } } public void Clear() { _items = new int[ InitialCapacity ]; _size = 0; } public void SetSize( int size ) { _size = size; } public int this[int index] { get { if (index < 0 || index >= _size) { throw new ArgumentOutOfRangeException( "index", index, "Specified argument was out of the range of valid values: list size=" + _size ); } return _items [index]; } set { if (index < 0 || index >= _size) { throw new ArgumentOutOfRangeException( "index", index, "Specified argument was out of the range of valid values: list size=" + _size ); } _items [index] = value; _version++; } } public int Last { get { if( _size <= 0 ) throw new IndexOutOfRangeException( "Last element is not defined in the empty array" ); return( _items[ _size - 1 ] ); } } public int BinarySearch( int value ) { int lo = 0; int hi = _size - 1; while (lo <= hi) { int i = (lo + hi) >> 1; int c = _items[ i ] - value; if (c == 0) return i; if (c < 0) { lo = i + 1; } else { hi = i - 1; } } return ~lo; } public int BinarySearch( int value, IComparer comparer ) { return Array.BinarySearch( _items, 0, _size, value, comparer ); } public void Sort() { Debug.Assert( _size >= 0 && _size <= _items.Length ); Array.Sort( _items, 0, _size ); _version++; } public void RadixSort() { Sorts.RadixSort( _items, Count ); _version++; } public void Sort( IComparer comparer ) { Array.Sort( _items, 0, _size, comparer ); _version++; } private void EnsureCapacity( int requestCapacity ) { int capacity = Capacity; if( capacity < requestCapacity ) { do { capacity = AdjustCapacity( ( ( capacity + 1 ) * 13 ) >> 3 ); // phi's rational approximation } while( capacity < requestCapacity ); Capacity = capacity; } } private static int AdjustCapacity( int capacity ) { return ( capacity < InitialCapacity ) ? InitialCapacity : capacity; } public void RemoveDuplicatesSorted() { if ( _size == 0 ) return; int srcIndex = 1; int destIndex = 0; while( srcIndex < _size ) { if ( _items [srcIndex] != _items [destIndex] ) { destIndex++; _items [destIndex] = _items [srcIndex]; } srcIndex++; } _size = destIndex+1; Debug.Assert( _size >= 0 && _size <= _items.Length ); } public static IntArrayList MergeSorted( IntArrayList list1, IntArrayList list2 ) { int count1 = list1.Count; int count2 = list2.Count; if( count1 == 0 ) { return list2; } if( count2 == 0 ) { return list1; } int[] result = new int [ AdjustCapacity( count1 + count2) ]; int index1 = 0; int index2 = 0; int destIndex = 0; while ( index1 < count1 && index2 < count2 ) { int compareResult = list1._items [index1] - list2._items [index2]; if ( compareResult == 0 ) { index1++; } else if ( compareResult < 0 ) { result [destIndex++] = list1._items [index1]; index1++; } else { result [destIndex++] = list2._items [index2]; index2++; } } // only one of these for loops will have >0 steps for( int i=index1; i < count1; i++ ) { result [destIndex++] = list1._items [i]; } for( int i=index2; i < count2; i++ ) { result [destIndex++] = list2._items [i]; } return new IntArrayList( result, destIndex ); } public static IntArrayList MergeSorted( IntArrayList list1, IntArrayList list2, IComparer comparer ) { int count1 = list1.Count; int count2 = list2.Count; if( count1 == 0 ) { return list2; } if( count2 == 0 ) { return list1; } int[] result = new int [ AdjustCapacity( count1 + count2) ]; int index1 = 0; int index2 = 0; int destIndex = 0; while ( index1 < count1 && index2 < count2 ) { int compareResult = comparer.Compare( list1._items [index1], list2._items [index2] ); if ( compareResult == 0 ) { index1++; } else if ( compareResult < 0 ) { result [destIndex++] = list1._items [index1]; index1++; } else { result [destIndex++] = list2._items [index2]; index2++; } } // only one of these for loops will have >0 steps for( int i=index1; i < count1; i++ ) { result [destIndex++] = list1._items [i]; } for( int i=index2; i < count2; i++ ) { result [destIndex++] = list2._items [i]; } return new IntArrayList( result, destIndex ); } public void IntersectSorted( IntArrayList list ) { int count1 = _size; int count2 = list._size; int compareResult; int index1 = 0; int index2 = 0; int destIndex = 0; if( count1 > 0 && count2 > 0 ) { compareResult = _items[ 0 ] - list[ 0 ]; if( compareResult < 0 ) { compareResult = BinarySearch( list[0] ); if( compareResult >= 0 ) { index1 = compareResult; } else { index1 = ~compareResult; } } else if( compareResult > 0 ) { compareResult = list.BinarySearch( _items[0] ); if( compareResult >= 0 ) { index2 = compareResult; } else { index2 = ~compareResult; } } compareResult = _items[ count1 - 1 ] - list[ count2 - 1 ]; if( compareResult < 0 ) { compareResult = Array.BinarySearch( list._items, index2, count2 - index2, _items[ count1 - 1 ] ); if( compareResult >= 0 ) { count2 = compareResult + 1; } else { count2 = ~compareResult; } } else if( compareResult > 0 ) { compareResult = Array.BinarySearch( _items, index1, count1 - index1, list[ count2 - 1 ] ); if( compareResult >= 0 ) { count1 = compareResult + 1; } else { count1 = ~compareResult; } } while ( index1 < count1 && index2 < count2 ) { compareResult = _items[ index1 ] - list._items[ index2 ]; if ( compareResult == 0 ) { int value = _items[ index1++ ]; _items[ destIndex++ ] = value; // skip duplicates while( index1 < count1 && _items[ index1 ] == value ) { index1++; } do { index2++; } while( index2 < count2 && list._items[ index2 ] == value ); } else if ( compareResult < 0 ) { index1++; } else { index2++; } } } _size = destIndex; _version++; } /// /// Intersects the specified lists of integers. This method can return one of the lists passed to it /// if one of the lists is found to be empty. /// /// The first list to intersect. /// The second list to intersect. /// The intersection result. public static IntArrayList IntersectSorted( IntArrayList list1, IntArrayList list2 ) { if( list1.Count == 0 ) { return list1; } if( list2.Count == 0 ) { return list2; } return IntersectSortedNew( list1, list2 ); } /// /// Intersects the specified lists of integers. This method is guaranteed to return a new list instance /// and not to reuse any of the lists passed to it. /// /// The first list to intersect. /// The second list to intersect. /// The intersection result. public static IntArrayList IntersectSortedNew(IntArrayList list1, IntArrayList list2) { int count1 = list1.Count; int count2 = list2.Count; int index1 = 0; int index2 = 0; int[] result = new int [ AdjustCapacity( Math.Min( count1, count2 ) )] ; int destIndex = 0; if( count1 > 0 && count2 > 0 ) { int compareResult = list1[ 0 ] - list2[ 0 ]; if( compareResult < 0 ) { compareResult = list1.BinarySearch( list2[ 0 ] ); if( compareResult >= 0 ) { index1 = compareResult; } else { index1 = ~compareResult; } } else if( compareResult > 0 ) { compareResult = list2.BinarySearch( list1[ 0 ] ); if( compareResult >= 0 ) { index2 = compareResult; } else { index2 = ~compareResult; } } compareResult = list1[ count1 - 1 ] - list2[ count2 - 1 ]; if( compareResult < 0 ) { compareResult = Array.BinarySearch( list2._items, index2, count2 - index2, list1[ count1 - 1 ] ); if( compareResult >= 0 ) { count2 = compareResult + 1; } else { count2 = ~compareResult; } } else if( compareResult > 0 ) { compareResult = Array.BinarySearch( list1._items, index1, count1 - index1, list2[ count2 - 1 ] ); if( compareResult >= 0 ) { count1 = compareResult + 1; } else { count1 = ~compareResult; } } while ( index1 < count1 && index2 < count2 ) { compareResult = list1._items[ index1 ] - list2._items[ index2 ]; if ( compareResult == 0 ) { int value = list1._items [index1]; result [destIndex++] = value; index1++; // skip duplicates while( index1 < count1 && list1._items [index1 ] == value ) { index1++; } do { index2++; } while( index2 < count2 && list2._items [index2 ] == value ); } else if ( compareResult < 0 ) { index1++; } else { index2++; } } } return new IntArrayList( result, destIndex ); } public static IntArrayList IntersectSorted( IntArrayList list1, IntArrayList list2, IComparer comparer ) { int count1 = list1.Count; int count2 = list2.Count; if( count1 == 0 ) { return list1; } if( count2 == 0 ) { return list2; } IntArrayList result = new IntArrayList( Math.Min( count1, count2 ) ); int index1 = 0; int index2 = 0; while ( index1 < count1 && index2 < count2 ) { int compareResult = comparer.Compare( list1 [index1], list2 [index2] ); if ( compareResult == 0 ) { result.Add( list1 [index1] ); index1++; } else if ( compareResult < 0 ) { index1++; } else { index2++; } } return result; } /// /// Unlike MergeSorted and IntersectSorted, MinusSorted is instance method because it /// implements the "-=" operation /// /// public void MinusSorted( IntArrayList minus ) { int index0 = 0; int index1 = 0; int index2 = 0; int count1 = Count; int count2 = minus.Count; while( index1 < count1 && index2 < count2 ) { _items[ index0 ] = _items[ index1 ]; int compareResult = _items[ index1 ] - minus[ index2 ]; if ( compareResult == 0 ) { index1++; } else if ( compareResult < 0 ) { index0++; index1++; } else { index2++; } } while( index1 < count1 ) { _items[ index0++ ] = _items[ index1++ ]; } _size = index0; _version++; } [Serializable()] public class IntArrayListEnumerator : IEnumerator, ICloneable { private IntArrayList _list; private int _index; private int _version; internal IntArrayListEnumerator( IntArrayList list ) { _list = list; _index = -1; _version = list._version; } public Object Clone() { return MemberwiseClone(); } public virtual bool MoveNext() { if ( _version != _list._version ) throw new InvalidOperationException( "Collection was modified; enumeration operation may not execute." ); if ( _index != -2 && _index < (_list.Count-1) ) { _index++; return true; } else _index = -2; return false; } object IEnumerator.Current { get { if ( _index == -1 ) throw new InvalidOperationException( "Enumeration has not started. Call MoveNext." ); else if ( _index == -2 ) throw new InvalidOperationException( "Enumeration already finished." ); return _list [_index]; } } public int Current { get { if ( _index == -1 ) throw new InvalidOperationException( "Enumeration has not started. Call MoveNext." ); else if ( _index == -2 ) throw new InvalidOperationException( "Enumeration already finished." ); return _list [_index]; } } public virtual void Reset() { if (_version != _list._version) throw new InvalidOperationException( "Collection was modified; enumeration operation may not execute." ); _index = -1; } } } /** * ArrayList with per-element Equals(), GetHashCode() and ToString() implementations. */ public class ComparableArrayList: ArrayList { private const string _toStringSeparator = ","; public ComparableArrayList() : base() {} public ComparableArrayList( ICollection coll ): base(coll) {} public override bool Equals( object obj ) { if ( obj == null || !(obj is ComparableArrayList) ) return false; if ( obj == this ) return true; ComparableArrayList rhs = (ComparableArrayList) obj; if ( rhs.Count != Count ) return false; for( int i=0; i 0 ) { builder.Append( _toStringSeparator ); } builder.Append( this [i].ToString() ); } return builder.ToString(); } finally { StringBuilderPool.Dispose( builder ); } } } }