/// <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>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;
            }
            }
        }
Exemple #3
0
        /// <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;
                        }
                    }
                }
            }
        }
        /// <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();
        }