/// <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(); }
/// <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(); }
/// <summary>Remove the value at the specified location</summary> /// <param name="level">Index of the level (0-based)</param> /// <param name="offset">Offset in the level (0-based)</param> /// <returns>Value that was removed</returns> public T RemoveAt(int level, int offset) { Contract.Assert(level >= 0 && offset >= 0 && offset < 1 << level); //TODO: check if level is allocated ? var segment = m_levels[level]; Contract.Assert(segment != null && segment.Length == 1 << level); T removed = segment[offset]; if (level == 0) { // removing the last inserted value segment[0] = default(T); } else if (level == 1) { // split the first level in two if (IsFree(0)) { // move up to root // ex: remove 'b' at (1,1) and move the 'a' back to the root // 0 [_] => [a] // 1 [a,b] => [_,_] m_root[0] = segment[1 - offset]; segment[0] = default(T); segment[1] = default(T); } else { // merge the root in missing spot // ex: remove 'b' at (1,1) and move the 'c' down a level // N = 3 N = 2 // 0 [c] => 0 [_] // 1 [a,b] => 1 [a,c] ColaStore.MergeSimple <T>(segment, m_root[0], segment[1 - offset], m_comparer); m_root[0] = default(T); } } else if ((m_count & 1) == 1) { // Remove an item from an odd-numbered set // Since the new count will be even, we only need to merge the root in place with the level that is missing a spot // ex: replace the 'b' at (2,1) with the 'e' in the root // N = 5 N = 4 // 0 [e] => 0 [_] // 1 [_,_] 1 [_,_] // 2 [a,b,c,d] => 2 [a,c,d,e] ColaStore.MergeInPlace <T>(segment, offset, m_root[0], m_comparer); m_root[0] = default(T); } else { // we are missing a spot in out modified segment, that need to fill // > we will take the first non empty segment, and break it in pieces // > its last item will be used to fill the empty spot // > the rest of its items will be spread to all the previous empty segments // find the first non empty segment that can be broken int firstNonEmptyLevel = ColaStore.LowestBit(m_count); if (firstNonEmptyLevel == level) { // we are the first level, this is easy ! // move the empty spot at the start if (offset > 0) { Array.Copy(segment, 0, segment, 1, offset); } // and spread the rest to all the previous levels ColaStore.SpreadLevel(level, m_levels); //TODO: modify SpreadLevel(..) to take the offset of the value to skip ? } else { // break that level, and merge its last item with the level that is missing one spot // break down this level T tmp = ColaStore.SpreadLevel(firstNonEmptyLevel, m_levels); // merge its last item with the empty spot in the modified level ColaStore.MergeInPlace(m_levels[level], offset, tmp, m_comparer); } } --m_count; if (m_levels.Length > MAX_SPARE_ORDER) { // maybe release the last level if it is empty ShrinkIfRequired(); } CheckInvariants(); return(removed); }
/// <summary>Insert one or more new elements in the set.</summary> /// <param name="values">Array of elements to insert. Warning: if a value already exist, the store will be corrupted !</param> /// <param name="ordered">If true, the entries in <paramref name="values"/> are guaranteed to already be sorted (using the store default comparer).</param> /// <remarks>The best performances are achieved when inserting a number of items that is a power of 2. The worst performances are when doubling the size of a store that is full. /// Warning: if <paramref name="ordered"/> is true but <paramref name="values"/> is not sorted, or is sorted using a different comparer, then the store will become corrupted ! /// </remarks> public void InsertItems(List <T> values, bool ordered = false) { if (values == null) { throw new ArgumentNullException("values"); } int count = values.Count; T[] segment, spare; if (count < 2) { if (count == 1) { Insert(values[0]); } return; } if (count == 2) { if (IsFree(1)) { segment = m_levels[1]; if (ordered) { segment[0] = values[0]; segment[1] = values[1]; } else { ColaStore.MergeSimple <T>(segment, values[0], values[1], m_comparer); } } else { spare = GetSpare(1); spare[0] = values[0]; spare[1] = values[1]; segment = m_levels[1]; MergeCascade(2, segment, spare); segment[0] = default(T); segment[1] = default(T); PutSpare(1, spare); } } else { // Inserting a size that is a power of 2 is very simple: // * either the corresponding level is empty, in that case we just copy the items and do a quicksort // * or it is full, then we just need to do a cascade merge // For non-power of 2s, we can split decompose them into a suite of power of 2s and insert them one by one int min = ColaStore.LowestBit(count); int max = ColaStore.HighestBit(count); if (max >= m_levels.Length) { // we need to allocate new levels Grow(max); } int p = 0; for (int i = min; i <= max; i++) { if (ColaStore.IsFree(i, count)) { continue; } segment = m_levels[i]; if (IsFree(i)) { // the target level is free, we can copy and sort in place values.CopyTo(p, segment, 0, segment.Length); if (!ordered) { Array.Sort(segment, 0, segment.Length, m_comparer); } p += segment.Length; m_count += segment.Length; } else { // the target level is used, we will have to do a cascade merge, using a spare spare = GetSpare(i); values.CopyTo(p, spare, 0, spare.Length); if (!ordered) { Array.Sort(spare, 0, spare.Length, m_comparer); } p += segment.Length; MergeCascade(i + 1, segment, spare); Array.Clear(segment, 0, segment.Length); PutSpare(i, spare); m_count += segment.Length; } } Contract.Assert(p == count); } CheckInvariants(); }