/// <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(); } }
public void Mark(TKey begin, TKey end) { if (m_comparer.Compare(begin, end) >= 0) { throw new InvalidOperationException("End key must be greater than the Begin key."); } var entry = new Entry(begin, end); Entry cursor; switch (m_items.Count) { case 0: { // the list empty // no checks required m_items.Insert(entry); m_bounds.ReplaceWith(entry); break; } case 1: { // there is only one value cursor = m_items[0]; if (!Resolve(cursor, entry)) { // no conflict m_items.Insert(entry); m_bounds.Update( Min(entry.Begin, cursor.Begin), Max(entry.End, cursor.End) ); } else { // merged with the previous range m_bounds.ReplaceWith(cursor); } break; } default: { // check with the bounds first if (m_comparer.Compare(begin, m_bounds.End) > 0) { // completely to the right m_items.Insert(entry); m_bounds.Update(m_bounds.Begin, end); break; } if (m_comparer.Compare(end, m_bounds.Begin) < 0) { // completely to the left m_items.Insert(entry); m_bounds.Update(begin, m_bounds.End); break; } if (m_comparer.Compare(begin, m_bounds.Begin) <= 0 && m_comparer.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.ReplaceWith(entry); break; } // overlaps with existing ranges, we may need to resolve conflicts int offset, level; bool inserted = false; // once inserted, will it conflict with the previous entry ? if ((level = m_items.FindPrevious(entry, true, out offset, out cursor)) >= 0) { if (Resolve(cursor, entry)) { entry = cursor; inserted = true; } } // also check for potential conflicts with the next entries while (true) { level = m_items.FindNext(entry, false, out offset, out cursor); if (level < 0) { break; } if (inserted) { // we already have inserted the key so conflicts will remove the next segment if (Resolve(entry, cursor)) { // next segment has been removed //Console.WriteLine(" > folded with previous: " + entry); m_items.RemoveAt(level, offset); } else { break; } } else { // we havent inserted the key yet, so in case of conflict, we will use the next segment's slot if (Resolve(cursor, entry)) { //Console.WriteLine(" > merged in place: " + cursor); inserted = true; } else { break; } } } if (!inserted) { // no conflict, we have to insert the new range m_items.Insert(entry); } m_bounds.Update( Min(m_bounds.Begin, entry.Begin), Max(m_bounds.End, entry.End) ); break; } } //TODO: check constraints ! }
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(); } }