/// <summary>
        /// Removes everything between begin and end then translates everything
        /// </summary>
        /// <param name="begin">begin key</param>
        /// <param name="end">end key</param>
        /// <param name="offset">offset to apply</param>
        /// <param name="applyOffset">func to apply offset to a key</param>
        public void Remove(TKey begin, TKey end, TKey offset, Func <TKey, TKey, TKey> applyOffset)
        {
            if (m_keyComparer.Compare(begin, end) >= 0)
            {
                throw new InvalidOperationException("End key must be greater than the Begin key.");
            }

            try
            {
                var entry    = new Entry(begin, end, default(TValue));
                var iterator = m_items.GetIterator();
                var comparer = m_keyComparer;
                if (!iterator.Seek(entry, true))
                {
                    //on ne trouve pas l'item exacte, on prends le premier.
                    iterator.SeekFirst();
                }
                var          cursor   = iterator.Current;
                var          c1       = comparer.Compare(begin, cursor.Begin);
                var          c2       = comparer.Compare(end, cursor.End);
                List <Entry> toRemove = null;
                //begin < cursor.Begin
                if (c1 < 0)
                {
                    var c3 = comparer.Compare(end, cursor.Begin);
                    //end <= cursor.Begin
                    //         [++++++++++
                    // ------[
                    //ou
                    //       [++++++
                    // ------[
                    if (c3 <= 0)
                    {
                        TranslateAfter(null, offset, applyOffset);
                        return;
                    }
                    //end < cursor.End
                    //     [+++++++++++[
                    //-----------[
                    if (c2 < 0)
                    {
                        cursor.Begin = applyOffset(end, offset);
                        cursor.End   = applyOffset(cursor.End, offset);
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                    //end == cursor.End
                    //     [+++++++++[
                    //---------------[
                    if (c2 == 0)
                    {
                        m_items.RemoveItem(cursor);
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                    //end > cursor.End
                    //      [+++++++++[
                    //-------------------...
                    if (c2 > 0)
                    {
                        toRemove = new List <Entry>();
                        toRemove.Add(cursor);
                        while (iterator.Next())
                        {
                            cursor = iterator.Current;
                            c2     = comparer.Compare(end, cursor.End);
                            c3     = comparer.Compare(end, cursor.Begin);
                            //end <= cursor.Begin
                            //       [+++++
                            // ----[
                            //ou
                            //       [+++++
                            // ------[
                            if (c3 <= 0)
                            {
                                //on set cursor pour que la translation soit faite correctement
                                cursor = entry;
                                break;
                            }
                            //end > cursor.Begin
                            if (c3 > 0)
                            {
                                //end < cursor.End
                                //     [+++++++++++
                                // ----------[
                                if (c2 < 0)
                                {
                                    cursor.Begin = begin;
                                    cursor.End   = applyOffset(cursor.End, offset);
                                    break;
                                }
                                // end >= cursor.End
                                //      [+++++++++[
                                // ---------------[
                                //ou
                                //      [+++++++[
                                // ----------------...
                                if (c2 >= 0)
                                {
                                    toRemove.Add(cursor);
                                    if (c2 == 0)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                        m_items.RemoveItems(toRemove);
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                }
                //begin == cursor.Begin
                else if (c1 == 0)
                {
                    //end < cursor.End
                    // [+++++++++[
                    // [-----[
                    if (c2 < 0)
                    {
                        cursor.Begin = begin;
                        cursor.End   = applyOffset(cursor.End, offset);
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                    //end == cursor.End
                    // [++++++++[
                    // [--------[
                    else if (c2 == 0)
                    {
                        toRemove = new List <Entry>();
                        toRemove.Add(cursor);
                    }
                    // end > cursor.End
                    // [+++++++[
                    // [-----------....
                    else
                    {
                        toRemove = new List <Entry>();
                        toRemove.Add(cursor);
                        while (iterator.Next())
                        {
                            cursor = iterator.Current;
                            var c3 = comparer.Compare(end, cursor.Begin);
                            c2 = comparer.Compare(end, cursor.End);
                            //end < cursor.Begin
                            //                [++++++++[
                            //---------[
                            //ou
                            //         [+++++++[
                            //---------[
                            if (c3 <= 0)
                            {
                                break;
                            }
                            else
                            {
                                //end < cursor.End
                                // [++++++++++++[
                                //-----[
                                if (c2 < 0)
                                {
                                    cursor.Begin = begin;
                                    cursor.End   = applyOffset(cursor.End, offset);
                                    break;
                                }
                                //end >= cursor.End
                                // [+++++++++[
                                //---------------...
                                //ou
                                // [+++++++++[
                                //-----------[
                                if (c2 >= 0)
                                {
                                    toRemove.Add(cursor);
                                    if (c2 == 0)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    m_items.RemoveItems(toRemove);
                    TranslateAfter(cursor, offset, applyOffset);
                    return;
                }
                //begin > cursor.Begin
                else
                {
                    //end < cursor.End
                    //   [++++++++++++[
                    //      [----[
                    // = [++[[++++[
                    if (c2 < 0)
                    {
                        var oldEnd = cursor.End;
                        cursor.End = begin;
                        TranslateAfter(cursor, offset, applyOffset);
                        m_items.Insert(new Entry(begin, applyOffset(oldEnd, offset), cursor.Value));
                        return;
                    }
                    //end == cursor.End
                    // [+++++++++++++[
                    //       [-------[
                    if (c2 == 0)
                    {
                        cursor.End = begin;
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                    //end > cursor.End
                    // [+++++++++++++[
                    //       [-------------
                    else
                    {
                        cursor.End = begin;
                        while (iterator.Next())
                        {
                            cursor = iterator.Current;
                            var c3 = comparer.Compare(end, cursor.Begin);
                            c2 = comparer.Compare(end, cursor.End);
                            //end <= cursor.Begin
                            //      [++++++++++++[
                            // --[
                            //ou
                            //      [++++++++++++[
                            // -----[
                            if (c3 <= 0)
                            {
                                break;
                            }
                            else
                            {
                                //end < cursor.End
                                //     [+++++++++++++[
                                // ------------[
                                if (c2 < 0)
                                {
                                    cursor.Begin = begin;
                                    cursor.End   = applyOffset(cursor.End, offset);
                                    break;
                                }
                                //end >= cursor.End
                                //   [+++++++++++[
                                //--------------------...
                                //ou
                                //   [+++++++++++[
                                //---------------[
                                else
                                {
                                    if (toRemove == null)
                                    {
                                        toRemove = new List <Entry>();
                                    }
                                    toRemove.Add(cursor);
                                    if (c2 == 0)
                                    {
                                        break;
                                    }
                                }
                            }
                        }

                        if (toRemove != null)
                        {
                            m_items.RemoveItems(toRemove);
                        }
                        TranslateAfter(cursor, offset, applyOffset);
                        return;
                    }
                }
            }
            finally
            {
                CheckInvariants();
            }
        }
Esempio n. 2
0
        public void Mark(TKey begin, TKey end, TValue value)
        {
            if (m_keyComparer.Compare(begin, end) >= 0)
            {
                throw new InvalidOperationException("End key must be greater than the Begin key.");
            }

            // adds a new interval to the dictionary by overwriting or splitting any previous interval
            // * if there are no interval, or the interval is disjoint from all other intervals, it is inserted as-is
            // * if the new interval completly overwrites one or more intervals, they will be replaced by the new interval
            // * if the new interval partially overlaps with one or more intervals, they will be split into chunks, and the new interval will be inserted between them

            // Examples:
            // { } + [0..1,A] => { [0..1,A] }
            // { [0..1,A] } + [2..3,B] => { [0..1,A], [2..3,B] }
            // { [4..5,A] } + [0..10,B] => { [0..10,B] }
            // { [0..10,A] } + [4..5,B] => { [0..4,A], [4..5,B], [5..10,A] }
            // { [2..4,A], [6..8,B] } + [3..7,C] => { [2..3,A], [3..7,C], [7..8,B] }
            // { [1..2,A], [2..3,B], ..., [9..10,Y] } + [0..10,Z] => { [0..10,Z] }

            var   entry = new Entry(begin, end, value);
            Entry cursor;
            var   cmp = m_keyComparer;
            int   c1, c2;

            try
            {
                switch (m_items.Count)
                {
                case 0:
                {                         // the list empty
                    // no checks required
                    m_items.Insert(entry);
                    m_bounds.Begin = entry.Begin;
                    m_bounds.End   = entry.End;
                    break;
                }

                case 1:
                {                         // there is only one value
                    cursor = m_items[0];

                    c1 = cmp.Compare(begin, cursor.End);
                    if (c1 >= 0)
                    {
                        // [--------)  [========)
                        // or [--------|========)
                        if (c1 == 0 && m_valueComparer.Compare(cursor.Value, value) == 0)
                        {
                            cursor.End = end;
                        }
                        else
                        {
                            m_items.Insert(entry);
                        }
                        m_bounds.End = end;
                        return;
                    }
                    c1 = cmp.Compare(end, cursor.Begin);
                    if (c1 <= 0)
                    {
                        // [========)  [--------)
                        // or [========|--------)
                        if (c1 == 0 && m_valueComparer.Compare(cursor.Value, value) == 0)
                        {
                            cursor.Begin = begin;
                        }
                        else
                        {
                            m_items.Insert(entry);
                        }
                        m_bounds.Begin = begin;
                        return;
                    }

                    c1 = cmp.Compare(begin, cursor.Begin);
                    c2 = cmp.Compare(end, cursor.End);
                    if (c1 == 0)
                    {                             // same start
                        if (c2 == 0)
                        {                         // same end
                            //   [--------)
                            // + [========)
                            // = [========)
                            cursor.Value = value;
                        }
                        else if (c2 < 0)
                        {
                            //   [----------)
                            // + [======)
                            // = [======|---)
                            if (m_valueComparer.Compare(cursor.Value, value) != 0)
                            {
                                cursor.Begin = end;
                                m_items.Insert(entry);
                            }
                        }
                        else
                        {
                            //   [------)
                            // + [==========)
                            // = [==========)
                            cursor.Set(entry);
                            m_bounds.End = end;
                        }
                    }
                    else if (c1 > 0)
                    {                             // entry is to the right
                        if (c2 >= 0)
                        {
                            //   [------)
                            // +     [=======)
                            // = [---|=======)

                            cursor.End = begin;
                            m_items.Insert(entry);
                            if (c2 > 0)
                            {
                                m_bounds.End = end;
                            }
                        }
                        else
                        {
                            //   [-----------)
                            // +     [====)
                            // = [---|====|--)
                            var tmp = new Entry(end, cursor.End, cursor.Value);
                            cursor.End = begin;
                            m_items.InsertItems(entry, tmp);
                        }
                    }
                    else
                    {                             // entry is to the left
                        if (c2 >= 0)
                        {
                            cursor.Set(entry);
                            m_bounds.End = end;
                        }
                        else
                        {
                            cursor.Begin = end;
                            m_items.Insert(entry);
                        }
                        m_bounds.Begin = begin;
                    }
                    break;
                }

                default:
                {
                    // check with the bounds first

                    if (cmp.Compare(begin, m_bounds.End) > 0)
                    {                             // completely to the right
                        m_items.Insert(entry);
                        m_bounds.End = end;
                        break;
                    }
                    if (cmp.Compare(end, m_bounds.Begin) < 0)
                    {                             // completely to the left
                        m_items.Insert(entry);
                        m_bounds.Begin = begin;
                        break;
                    }
                    if (cmp.Compare(begin, m_bounds.Begin) <= 0 && cmp.Compare(end, m_bounds.End) >= 0)
                    {                             // overlaps with all the ranges
                        // note :if we are here, there was at least 2 items, so just clear everything
                        m_items.Clear();
                        m_items.Insert(entry);
                        m_bounds.Begin = entry.Begin;
                        m_bounds.End   = entry.End;
                        break;
                    }

                    // note: we have already bound checked, so we know that there is at least one overlap !

                    bool inserted = false;

                    // => we will try to find the first range and last range in the dictionary that would be impacted, mutate them and delete all ranges in between

                    var iterator = m_items.GetIterator();
                    // seek to the range that starts before (or at) the new range's begin point
                    if (!iterator.Seek(entry, true))
                    {                             // the new range will go into first position
                        // => still need to check if we are overlapping with the next ranges
                        iterator.SeekFirst();
                        //Console.WriteLine("  . new lower bound, but intersects with first range...");
                        m_bounds.Begin = begin;
                    }

                    m_bounds.End = Max(m_bounds.End, end);

                    cursor = iterator.Current;

                    c1 = cmp.Compare(cursor.Begin, begin);
                    c2 = cmp.Compare(cursor.End, end);
                    if (c1 >= 0)
                    {
                        if (c2 == 0)
                        {                                 // same end
                            //   [-------)..           [-------)..
                            // + [=======)        + [==========)
                            // = [=======)..      = [==========)..
                            cursor.Set(entry);
                            return;
                        }

                        if (c2 > 0)
                        {                                 // truncate begin
                            //   [----------)..        [----------)..
                            // + [=======)	      + [=======)
                            // = [=======|--)..   = [=======|-----)..
                            cursor.Begin = end;
                            m_items.Insert(entry);
                            return;
                        }

                        // replace + propagate
                        //   [-------)???..            [-----)????..
                        // + [==========)         + [============)
                        // = [==========)..       = [============)..

                        cursor.Set(entry);
                        inserted = true;
                        //TODO: need to propagate !
                    }
                    else
                    {
                        if (c2 == 0)
                        {                                 // same end
                            //   [------------)
                            //       [========)
                            // = [---|========)

                            cursor.End = begin;
                            m_items.Insert(entry);
                            return;
                        }

                        if (c2 > 0)
                        {
                            //   [------------)
                            //       [=====)
                            // = [---|=====|--)

                            var tmp = new Entry(end, cursor.End, cursor.Value);
                            cursor.End = begin;
                            m_items.InsertItems(entry, tmp);
                            return;
                        }

                        int c3 = cmp.Compare(begin, cursor.End);
                        if (c3 >= 0)
                        {
                            if (c3 == 0 && m_valueComparer.Compare(value, cursor.Value) == 0)
                            {                                     // touching same value => merge
                                cursor.End = end;
                                entry      = cursor;
                                inserted   = true;
                            }
                            else
                            {
                                //   [---)
                                //         [=====????
                                // = [---) [=====????
                            }
                        }
                        else
                        {
                            //   [--------????
                            //       [====????
                            // = [---|====????
                            cursor.End = begin;
                        }
                    }

                    // if we end up here, it means that we may be overlapping with following items
                    // => we need to delete them until we reach the last one, which we need to either delete or mutate
                    // => also, if we haven't inserted the entry yet, we will reuse the first deleted range to insert the entry, and only insert at the end if we haven't found a spot

                    List <Entry> deleted = null;

                    while (true)
                    {
                        if (!iterator.Next())
                        {                                 // we reached past the end of the db
                            break;
                        }

                        // cursor: existing range that we need to either delete or mutate
                        cursor = iterator.Current;

                        c1 = cmp.Compare(cursor.Begin, end);
                        if (c1 == 0)
                        {                                 // touching the next range
                            if (m_valueComparer.Compare(value, cursor.Value) == 0)
                            {                             // contiguous block with same value => merge
                                //         [===========)
                                //   [=====)
                                // = [=================)
                                if (inserted)
                                {
                                    if (cmp.Compare(cursor.End, entry.End) > 0)
                                    {
                                        entry.End = cursor.End;
                                    }
                                    //note: we can't really delete while iterating with a cursor, so just mark it for deletion
                                    if (deleted == null)
                                    {
                                        deleted = new List <Entry>();
                                    }
                                    deleted.Add(cursor);
                                }
                                else
                                {
                                    cursor.Begin = begin;
                                    entry        = cursor;
                                    inserted     = true;
                                }
                                break;
                            }
                            else
                            {
                                //         [-----------)
                                //   [=====)
                                // = [=====|-----------)
                            }
                            break;
                        }
                        else if (c1 > 0)
                        {                                 // we are past the inserted range, nothing to do any more
                            //            [------------)
                            //   [=====)
                            // = [=====)  [------------)
                            //Console.WriteLine("  . no overlap => break");
                            break;
                        }

                        c1 = cmp.Compare(cursor.End, end);
                        if (c1 <= 0)
                        {                                 // we are completely covered => delete
                            //      [-------)           [-------)
                            // + [...=======)      + [...=======...)
                            // = [...=======)      = [...=======...)
                            if (!inserted)
                            {                                     // use that slot to insert ourselves
                                cursor.Set(entry);
                                inserted = true;
                            }
                            else
                            {
                                //note: we can't really delete while iterating with a cursor, so just mark it for deletion
                                if (deleted == null)
                                {
                                    deleted = new List <Entry>();
                                }
                                deleted.Add(cursor);
                            }
                        }
                        else
                        {                                 // we are only partially overlapped
                            //       [------------)
                            //   [....========)
                            // = [....========|---)

                            cursor.Begin = end;
                            break;
                        }
                    }

                    if (deleted != null && deleted.Count > 0)
                    {
                        m_items.RemoveItems(deleted);
                    }

                    if (!inserted)
                    {                             // we did not find an existing spot to re-use, so we need to insert the new range
                        m_items.Insert(entry);
                    }
                    break;
                }
                }
            }
            finally
            {
                CheckInvariants();
            }
        }