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); //we keep the reference to cursor to be able to modify it later entry = cursor; 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); //get the reference to be able to eventually merge it afterwards entry = cursor; 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(); } }