public void CopyTo(T[] array, int arrayIndex, int count) { if (array == null) { throw new ArgumentNullException("array"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException("Index cannot be less than zero."); } if (count < 0) { throw new ArgumentOutOfRangeException("Count cannot be less than zero."); } if (arrayIndex > array.Length || count > (array.Length - arrayIndex)) { throw new ArgumentException("Destination array is too small"); } Contract.EndContractBlock(); int p = arrayIndex; count = Math.Min(count, m_count); foreach (var item in ColaStore.IterateOrdered(count, m_levels, m_comparer, false)) { array[p++] = item; } Contract.Assert(p == arrayIndex + count); }
/// <summary>Find the largest element in the store</summary> /// <returns>Largest element found, or default(T) if the store is empty</returns> public T Max() { switch (m_count) { case 0: return(default(T)); case 1: return(m_root[0]); case 2: return(m_levels[1][1]); default: { int level = ColaStore.LowestBit(m_count); int end = ColaStore.HighestBit(m_count); T max = m_levels[level][0]; while (level <= end) { if (!IsFree(level) && m_comparer.Compare(max, m_levels[level][0]) < 0) { max = m_levels[level][0]; } ++level; } return(max); } } }
/// <summary>Finds the location of an element in the array</summary> /// <param name="value">Value of the element to search for.</param> /// <param name="offset">Receives the offset of the element inside the level if found; otherwise, 0.</param> /// <returns>Level that contains the element if found; otherwise, -1.</returns> public int Find(T value, out int offset, out T actualValue) { if ((m_count & 1) != 0) { // If someone gets the last inserted key, there is a 50% change that it is in the root // (if not, it will the the last one of the first non-empty level) if (m_comparer.Compare(value, m_root[0]) == 0) { offset = 0; actualValue = m_root[0]; return(0); } } var levels = m_levels; for (int i = 1; i < levels.Length; i++) { if (IsFree(i)) { // this segment is not allocated continue; } int p = ColaStore.BinarySearch <T>(levels[i], 0, 1 << i, value, m_comparer); if (p >= 0) { offset = p; actualValue = levels[i][p]; return(i); } } offset = 0; actualValue = default(T); return(NOT_FOUND); }
/// <summary>Remove the value at the specified location</summary> /// <param name="arrayIndex">Absolute index in the vector-array</param> /// <returns>Value that was removed</returns> public T RemoveAt(int arrayIndex) { Contract.Requires(arrayIndex >= 0 && arrayIndex <= this.Capacity); int offset, level = ColaStore.FromIndex(arrayIndex, out offset); return(RemoveAt(level, offset)); }
/// <summary>Insert a new element in the set, and returns its index.</summary> /// <param name="value">Value to insert. Warning: if the value already exists, the store will be corrupted !</param> /// <remarks>The index is the absolute index, as if all the levels where a single, contiguous, array (0 = root, 7 = first element of level 3)</remarks> public void Insert(T value) { if (IsFree(0)) { // half the inserts (when the count is even) can be done in the root m_root[0] = value; } else if (IsFree(1)) { // a quarter of the inserts only need to move the root and the value to level 1 ColaStore.MergeSimple <T>(m_levels[1], m_root[0], value, m_comparer); m_root[0] = default(T); } else { // we need to merge one or more levels var spare = GetSpare(0); if (object.ReferenceEquals(spare, m_root)) { Debugger.Break(); } Contract.Assert(spare != null && spare.Length == 1); spare[0] = value; MergeCascade(1, m_root, spare); PutSpare(0, spare); m_root[0] = default(T); } ++m_count; CheckInvariants(); }
void System.Collections.IEnumerator.Reset() { if (m_version != m_parent.m_version) { ColaStore.ThrowStoreVersionChanged(); } m_iterator = new ColaStore.Enumerator <KeyValuePair <TKey, TValue> >(m_parent.m_items, m_iterator.Reverse); }
internal Enumerator(ColaStore <T> items, bool reverse) { m_items = items; m_reverse = reverse; m_cursors = ColaStore.CreateCursors(m_items.Count, out m_min); m_max = m_cursors.Length - 1; m_current = default(T); }
public bool MoveNext() { if (m_version != m_parent.m_version) { ColaStore.ThrowStoreVersionChanged(); } return(m_iterator.MoveNext()); }
/// <summary>Store a value at a specific location in the arrayh</summary> /// <param name="arrayIndex">Absolute index in the vector-array</param> /// <param name="value">Value to store</param> /// <returns>Previous value at that location</returns> public T SetAt(int arrayIndex, T value) { Contract.Assert(arrayIndex >= 0 && arrayIndex <= this.Capacity); int offset; int level = ColaStore.FromIndex(arrayIndex, out offset); return(SetAt(level, offset, value)); }
internal Iterator(T[][] levels, int count, IComparer <T> comparer) { Contract.Requires(levels != null && count >= 0 && comparer != null); m_levels = levels; m_count = count; m_comparer = comparer; m_cursors = ColaStore.CreateCursors(m_count, out m_min); }
public ColaOrderedSet(int capacity, IComparer <T> comparer) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", "Capacity cannot be less than zero."); } Contract.EndContractBlock(); m_items = new ColaStore <T>(capacity, comparer ?? Comparer <T> .Default); }
public ColaRangeSet(int capacity, IComparer <TKey> keyComparer) { m_comparer = keyComparer ?? Comparer <TKey> .Default; if (capacity == 0) { capacity = 15; } m_items = new ColaStore <Entry>(capacity, new BeginKeyComparer(m_comparer)); m_bounds = new Entry(default(TKey), default(TKey)); }
/// <summary>Find an element </summary> /// <param name="value"></param> /// <returns>The zero-based index of the first occurrence of <paramref name="value"/> within the entire list, if found; otherwise, –1.</returns> public int IndexOf(T value) { T _; int offset, level = m_items.Find(value, out offset, out _); if (level >= 0) { return(ColaStore.MapLocationToOffset(m_items.Count, level, offset)); } return(NOT_FOUND); }
public ColaRangeDictionary(int capacity, IComparer <TKey> keyComparer, IComparer <TValue> valueComparer) { m_keyComparer = keyComparer ?? Comparer <TKey> .Default; m_valueComparer = valueComparer ?? Comparer <TValue> .Default; if (capacity == 0) { capacity = 15; } m_items = new ColaStore <Entry>(capacity, new BeginKeyComparer(m_keyComparer)); m_bounds = new Entry(default(TKey), default(TKey), default(TValue)); }
/// <summary>Allocates a new store</summary> /// <param name="capacity">Initial capacity, or 0 for the default capacity</param> /// <param name="comparer">Comparer used to order the elements</param> public ColaStore(int capacity, IComparer <T> comparer) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", "Capacity cannot be less than zero."); } if (comparer == null) { throw new ArgumentNullException("comparer"); } Contract.EndContractBlock(); int levels; if (capacity == 0) { // use the default capacity levels = INITIAL_LEVELS; } else { // L levels will only store (2^L - 1) // note: there is no real penalty if the capacity was not correctly estimated, appart from the fact that all levels will not be contiguous in memory // 1 => 1 // 2..3 => 2 // 4..7 => 3 levels = ColaStore.HighestBit(capacity) + 1; } // allocating more than 31 levels would mean having an array of length 2^31, which is not possible if (levels >= 31) { throw new ArgumentOutOfRangeException("capacity", "Cannot allocate more than 30 levels"); } // pre-allocate the segments and spares at the same time, so that they are always at the same memory location var segments = new T[levels][]; var spares = new T[MAX_SPARE_ORDER][]; for (int i = 0; i < segments.Length; i++) { segments[i] = new T[1 << i]; if (i < spares.Length) { spares[i] = new T[1 << i]; } } m_levels = segments; m_root = segments[0]; m_spares = spares; #if ENFORCE_INVARIANTS m_spareUsed = new bool[spares.Length]; #endif m_comparer = comparer; }
/// <summary>Returns the smallest and largest element in the store</summary> /// <param name="min">Receives the value of the smallest element (or default(T) is the store is Empty)</param> /// <param name="max">Receives the value of the largest element (or default(T) is the store is Empty)</param> /// <remarks>If the store contains only one element, than min and max will be equal</remarks> public void GetBounds(out T min, out T max) { switch (m_count) { case 0: { min = default(T); max = default(T); break; } case 1: { min = m_root[0]; max = min; break; } case 2: { min = m_levels[1][0]; max = m_levels[1][1]; break; } default: { int level = ColaStore.LowestBit(m_count); int end = ColaStore.HighestBit(m_count); var segment = m_levels[level]; min = segment[0]; max = segment[segment.Length - 1]; while (level <= end) { if (IsFree(level)) { continue; } segment = m_levels[level]; if (m_comparer.Compare(min, segment[0]) > 0) { min = segment[0]; } if (m_comparer.Compare(max, segment[segment.Length - 1]) < 0) { min = segment[segment.Length - 1]; } ++level; } break; } } }
public T this[int index] { get { if (index < 0 || index >= m_items.Count) { ThrowIndexOutOfRangeException(); } int offset; int level = ColaStore.MapOffsetToLocation(m_items.Count, index, out offset); Contract.Assert(level >= 0); return(m_items.GetAt(level, offset)); } }
/// <summary>Pre-allocate memory in the store so that it can store a specified amount of items</summary> /// <param name="minimumRequired">Number of items that will be inserted in the store</param> public void EnsureCapacity(int minimumRequired) { int level = ColaStore.HighestBit(minimumRequired); if ((1 << level) < minimumRequired) { ++level; } if (level >= m_levels.Length) { Grow(level); } }
public T RemoveAt(int arrayIndex) { if (arrayIndex < 0 || arrayIndex >= m_items.Count) { throw new ArgumentOutOfRangeException("arrayIndex", "Index is outside the array"); } int offset; int level = ColaStore.MapOffsetToLocation(m_items.Count, arrayIndex, out offset); Contract.Assert(level >= 0 && offset >= 0 && offset < 1 << level); ++m_version; return(m_items.RemoveAt(level, offset)); }
/// <summary>Search for the smallest element that is larger than a reference element</summary> /// <param name="value">Reference element</param> /// <param name="orEqual">If true, return the position of the value itself if it is found. If false, return the position of the closest value that is smaller.</param> /// <param name="offset">Receive the offset within the level of the next element, or 0 if not found</param> /// <param name="result">Receive the value of the next element, or default(T) if not found</param> /// <returns>Level of the next element, or -1 if <param name="result"/> was already the largest</returns> public static int FindNext <T>(T[][] levels, int count, T value, bool orEqual, IComparer <T> comparer, out int offset, out T result) { int level = NOT_FOUND; T min = default(T); int minOffset = 0; // scan each segment for a value that would be larger, keep track of the smallest found for (int i = 0; i < levels.Length; i++) { if (ColaStore.IsFree(i, count)) { continue; } var segment = levels[i]; int pos = ColaStore.BinarySearch <T>(segment, 0, segment.Length, value, comparer); if (pos >= 0) { // we found an exact match in this segment if (orEqual) { offset = pos; result = segment[pos]; return(i); } // the next item in this segment should be larger ++pos; } else { // we found where it would be stored in this segment pos = ~pos; } if (pos < segment.Length) { if (level == NOT_FOUND || comparer.Compare(segment[pos], min) < 0) { // we found a better candidate min = segment[pos]; level = i; minOffset = pos; } } } offset = minOffset; result = min; return(level); }
private void Debug_Dump(string label = null) { Trace.WriteLine("* Cursor State: " + label); for (int i = m_min; i < m_cursors.Length; i++) { if (ColaStore.IsFree(i, m_count)) { Trace.WriteLine(" - L" + i + ": unallocated"); continue; } int p = m_cursors[i]; Trace.WriteLine(" - L" + i + ": " + p + " [" + (1 << i) + "] = " + (p < 0 ? "<BEFORE>" : (p >= (1 << i)) ? "<AFTER>" : ("" + m_levels[i][p]))); } Trace.WriteLine(" > Current at " + m_currentLevel + " : " + m_current); }
private void MergeCascade(int level, T[] left, T[] right) { Contract.Requires(level > 0, "level"); Contract.Requires(left != null && left.Length == (1 << (level - 1)), "left"); Contract.Requires(right != null && right.Length == (1 << (level - 1)), "right"); if (IsFree(level)) { // target level is empty if (level >= m_levels.Length) { Grow(level); } Contract.Assert(level < m_levels.Length); ColaStore.MergeSort(m_levels[level], left, right, m_comparer); } else if (IsFree(level + 1)) { // the next level is empty if (level + 1 >= m_levels.Length) { Grow(level + 1); } Contract.Assert(level + 1 < m_levels.Length); var spare = GetSpare(level); ColaStore.MergeSort(spare, left, right, m_comparer); var next = m_levels[level]; ColaStore.MergeSort(m_levels[level + 1], next, spare, m_comparer); Array.Clear(next, 0, next.Length); PutSpare(level, spare); } else { // both are full, need to do a cascade merge Contract.Assert(level < m_levels.Length); // merge N and N +1 var spare = GetSpare(level); ColaStore.MergeSort(spare, left, right, m_comparer); // and cascade to N + 2 ... var next = m_levels[level]; MergeCascade(level + 1, next, spare); Array.Clear(next, 0, next.Length); PutSpare(level, spare); } }
/// <summary>Search for the largest element that is smaller than a reference element</summary> /// <param name="value">Reference element</param> /// <param name="orEqual">If true, return the position of the value itself if it is found. If false, return the position of the closest value that is smaller.</param> /// <param name="offset">Receive the offset within the level of the previous element, or 0 if not found</param> /// <param name="result">Receive the value of the previous element, or default(T) if not found</param> /// <returns>Level of the previous element, or -1 if <param name="result"/> was already the smallest</returns> public static int FindPrevious <T>(T[][] levels, int count, T value, bool orEqual, IComparer <T> comparer, out int offset, out T result) { int level = NOT_FOUND; T max = default(T); int maxOffset = 0; // scan each segment for a value that would be smaller, keep track of the smallest found for (int i = 0; i < levels.Length; i++) { if (ColaStore.IsFree(i, count)) { continue; } var segment = levels[i]; int pos = ColaStore.BinarySearch <T>(segment, 0, segment.Length, value, comparer); // the previous item in this segment should be smaller if (pos < 0) { // it is not pos = ~pos; } else if (orEqual) { // we found an exact match in this segment offset = pos; result = segment[pos]; return(i); } --pos; if (pos >= 0) { if (level == NOT_FOUND || comparer.Compare(segment[pos], max) > 0) { // we found a better candidate max = segment[pos]; level = i; maxOffset = pos; } } } offset = maxOffset; result = max; return(level); }
/// <summary>Iterate over all the values in the set, without any order guarantee</summary> internal static IEnumerable <T> IterateUnordered <T>(int count, T[][] inputs) { Contract.Requires(count >= 0 && inputs != null && count < (1 << inputs.Length)); for (int i = 0; i < inputs.Length; i++) { if (ColaStore.IsFree(i, count)) { continue; } var segment = inputs[i]; Contract.Assert(segment != null && segment.Length == 1 << i); for (int j = 0; j < segment.Length; j++) { yield return(segment[j]); } } }
public bool MoveNext() { int pos; if (m_reverse) { pos = ColaStore.IterateFindPrevious(m_items.Levels, m_cursors, m_min, m_max, m_items.Comparer, out m_current); } else { pos = ColaStore.IterateFindNext(m_items.Levels, m_cursors, m_min, m_max, m_items.Comparer, out m_current); } if (pos == NOT_FOUND) { // that was the last item! return(false); } // update the bounds if necessary if (pos == m_max) { if (m_cursors[m_max] == NOT_FOUND) { --m_max; } } else if (pos == m_min) { if (m_cursors[m_min] == NOT_FOUND) { ++m_min; } } return(true); }
/// <summary>Insert two elements in the set.</summary> public void InsertItems(T first, T second) { Contract.Requires(m_comparer.Compare(first, second) != 0, "Cannot insert the same value twice"); if (IsFree(1)) { ColaStore.MergeSimple <T>(m_levels[1], first, second, m_comparer); } else { //Console.WriteLine("InsertItems([2]) Cascade"); var spare = GetSpare(1); spare[0] = first; spare[1] = second; var segment = m_levels[1]; MergeCascade(2, segment, spare); segment[0] = default(T); segment[1] = default(T); PutSpare(1, spare); } m_count += 2; CheckInvariants(); }
void System.Collections.IEnumerator.Reset() { m_cursors = ColaStore.CreateCursors(m_items.Count, out m_min); m_max = m_cursors.Length - 1; m_current = default(T); }
/// <summary>Iterate over all the values in the set, using their natural order</summary> internal static IEnumerable <T> IterateOrdered <T>(int count, T[][] inputs, IComparer <T> comparer, bool reverse) { Contract.Requires(count >= 0 && inputs != null && comparer != null && count < (1 << inputs.Length)); // NOT TESTED !!!!! // NOT TESTED !!!!! // NOT TESTED !!!!! Contract.Requires(count >= 0 && inputs != null && comparer != null); // We will use a list of N cursors, set to the start of their respective levels. // A each turn, look for the smallest key referenced by the cursors, return that one, and advance its cursor. // Once a cursor is past the end of its level, it is set to -1 and is ignored for the rest of the operation if (count > 0) { // setup the cursors, with the empty levels already marked as completed var cursors = new int[inputs.Length]; for (int i = 0; i < cursors.Length; i++) { if (ColaStore.IsFree(i, count)) { cursors[i] = NOT_FOUND; } } // pre compute the first/last active level int min = ColaStore.LowestBit(count); int max = ColaStore.HighestBit(count); while (count-- > 0) { T item; int pos; if (reverse) { pos = IterateFindPrevious(inputs, cursors, min, max, comparer, out item); } else { pos = IterateFindNext(inputs, cursors, min, max, comparer, out item); } if (pos == NOT_FOUND) { // we unexpectedly ran out of stuff before the end ? //TODO: should we fail or stop here ? throw new InvalidOperationException("Not enough data in the source arrays to fill the output array"); } yield return(item); // update the bounds if needed if (pos == max) { if (cursors[max] == NOT_FOUND) { --max; } } else if (pos == min) { if (cursors[min] == NOT_FOUND) { ++min; } } } } }
public static IEnumerable <T> FindBetween <T>(T[][] levels, int count, T begin, bool beginOrEqual, T end, bool endOrEqual, int limit, IComparer <T> comparer) { if (limit > 0) { for (int i = 0; i < levels.Length; i++) { if (ColaStore.IsFree(i, count)) { continue; } var segment = levels[i]; int to = ColaStore.BinarySearch <T>(segment, 0, segment.Length, end, comparer); if (to >= 0) { if (!endOrEqual) { to--; } } else { to = ~to; } if (to < 0 || to >= segment.Length) { continue; } int from = ColaStore.BinarySearch <T>(segment, 0, segment.Length, begin, comparer); if (from >= 0) { if (!beginOrEqual) { ++from; } } else { from = ~from; } if (from >= segment.Length) { continue; } if (from > to) { continue; } for (int j = from; j <= to && limit > 0; j++) { yield return(segment[j]); --limit; } if (limit <= 0) { break; } } } }
public ColaOrderedDictionary(int capacity, IComparer <TKey> keyComparer, IEqualityComparer <TValue> valueComparer) { m_keyComparer = keyComparer ?? Comparer <TKey> .Default; m_valueComparer = valueComparer ?? EqualityComparer <TValue> .Default; m_items = new ColaStore <KeyValuePair <TKey, TValue> >(capacity, new KeyOnlyComparer(m_keyComparer)); }