#region Copyright 2012-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace CSharpTest.Net.Collections
{
///
/// Implements an IDictionary interface for an in-memory B+Tree
///
[System.Diagnostics.DebuggerDisplay("Count = {_count}")]
public class BTreeDictionary : IDictionaryEx, ICloneable
{
/// The default `order` of the B+Tree structure.
public const int DefaultOrder = 64;
private Node _root;
private readonly int _order;
private readonly IComparer _comparer;
private readonly KvComparer _kvcomparer;
private int _count;
private bool _isReadOnly;
private Node _first, _last;
private KeyCollection _keys;
private ValueCollection _values;
/// Constructs a BTreeList instance.
public BTreeDictionary() : this(DefaultOrder, Comparer.Default) { }
/// Constructs a BTreeList instance.
public BTreeDictionary(IComparer comparer) : this(DefaultOrder, comparer) { }
/// Constructs a BTreeList instance.
public BTreeDictionary(int order, IComparer comparer)
{
if (order < 4)
throw new ArgumentOutOfRangeException("order");
if (comparer == null)
throw new ArgumentNullException("comparer");
_isReadOnly = false;
_order = order;
_comparer = comparer;
_kvcomparer = new KvComparer(_comparer);
Clear();
}
/// Constructs a BTreeList instance.
public BTreeDictionary(IEnumerable> copyFrom) : this(DefaultOrder, Comparer.Default, copyFrom) { }
/// Constructs a BTreeList instance.
public BTreeDictionary(IComparer comparer, IEnumerable> copyFrom) : this(DefaultOrder, comparer, copyFrom) { }
/// Constructs a BTreeList instance.
public BTreeDictionary(int order, IComparer comparer, IEnumerable> copyFrom)
: this(order, comparer)
{
if (copyFrom == null)
throw new ArgumentNullException("copyFrom");
AddRange(copyFrom);
}
///
/// Gets the number of elements contained in the .
///
public int Count { get { return _count; } }
///
/// Gets a value indicating whether the is read-only.
///
public bool IsReadOnly { get { return _isReadOnly; } }
///
/// Gets the Comparer provided to the constructor or Comparer<TKey>.Default if it was not provided.
///
public IComparer Comparer { get { return _comparer; } }
void IDisposable.Dispose() { Clear(); }
#region IDictionary Members
///
/// Removes all items from the .
///
/// The Collection is read-only.
public void Clear()
{
Modify();
_count = 0;
_root = new Node(_order);
_first = _last = _root;
}
///
/// Gets or sets the element with the specified key.
///
///
/// The element with the specified key.
///
/// The key of the element to get or set.
/// The property is retrieved and is not found.
/// The property is set and the is read-only.
public TValue this[TKey key]
{
get
{
SeekResult result;
if (Seek(_root, key, out result))
return result.Value;
throw new KeyNotFoundException();
}
set
{
Modify();
if (!Add(null, -1, _root, new KeyValuePair(key, value), false))
throw new KeyNotFoundException();
}
}
///
/// Gets the value associated with the specified key.
///
///
/// true if the object that implements contains an element with the specified key; otherwise, false.
///
public bool TryGetValue(TKey key, out TValue value)
{
SeekResult result;
if (Seek(_root, key, out result))
{
value = result.Value;
return true;
}
value = default(TValue);
return false;
}
///
/// Adds an element with the provided key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
/// An element with the same key already exists in the .
/// The is read-only.
public void Add(TKey key, TValue value)
{
Modify();
Add(new KeyValuePair(key, value));
}
///
/// Adds an item to the .
///
/// The object to add to the .
/// An element with the same key already exists in the .
/// The is read-only.
public void Add(KeyValuePair item)
{
Modify();
if (!Add(null, -1, _root, item, true))
throw new ArgumentException("An item with the same key has already been added.");
}
///
/// Adds a set of items to the .
///
/// The items to add to the .
/// An element with the same key already exists in the .
/// The is read-only.
public void AddRange(IEnumerable> items)
{
Modify();
foreach (KeyValuePair pair in items)
Add(pair);
}
///
/// Determines whether the contains an element with the specified key.
///
///
/// true if the contains an element with the key; otherwise, false.
///
public bool ContainsKey(TKey key)
{
SeekResult result;
return Seek(_root, key, out result);
}
///
/// Determines whether the contains a specific key and value pair.
///
/// true if is found in the ; otherwise, false.
/// The object to locate in the .
public bool Contains(KeyValuePair item)
{
SeekResult result;
return Seek(_root, item.Key, out result) &&
EqualityComparer.Default.Equals(item.Value, result.Value);
}
///
/// Removes the element with the specified key from the .
///
///
/// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original .
///
/// The key of the element to remove.
/// The is read-only.
public bool Remove(TKey key)
{
Modify();
KeyValuePair item = new KeyValuePair(key, default(TValue));
return Remove(null, -1, _root, ref item);
}
///
/// Removes the first occurrence of a specific object from the .
///
///
/// true if was successfully removed from the ; otherwise, false.
/// This method also returns false if is not found in the original .
///
/// The object to remove from the .
/// The is read-only.
public bool Remove(KeyValuePair item)
{
Modify();
SeekResult result;
if (Seek(_root, item.Key, out result) &&
EqualityComparer.Default.Equals(item.Value, result.Value))
return Remove(null, -1, _root, ref item);
return false;
}
///
/// Gets an containing the keys of the .
///
public ICollection Keys { get { return _keys != null ? _keys : (_keys = new KeyCollection(this)); } }
///
/// Gets an containing the values in the .
///
public ICollection Values { get { return _values != null ? _values : (_values = new ValueCollection(this)); } }
///
/// Copies the elements of the to an , starting at a particular index.
///
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
if(array == null)
throw new ArgumentNullException();
if(arrayIndex < 0 || (array.Length - arrayIndex) < _count)
throw new ArgumentOutOfRangeException();
Node node = _first;
while(node != null)
{
Array.Copy(node.Values, 0, array, arrayIndex, node.Count);
arrayIndex += node.Count;
node = node.Next;
}
}
///
/// Returns an enumerator that iterates through the collection.
///
public IEnumerator> GetEnumerator() { return new Enumerator(_first, 0); }
[Obsolete] System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
#region Public Members
///
/// Adds a key/value pair to the if the key does not already exist.
///
/// The key of the element to add.
/// The value to be added, if the key does not already exist.
/// The is read-only.
public TValue GetOrAdd(TKey key, TValue value)
{
Modify();
SeekResult result;
if (Seek(_root, key, out result))
return result.Value;
Add(key, value);
return value;
}
///
/// Adds an element with the provided key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
/// The is read-only.
public bool TryAdd(TKey key, TValue value)
{
Modify();
return Add(null, -1, _root, new KeyValuePair(key, value), true);
}
///
/// Updates an element with the provided key to the value if it exists.
///
/// Returns true if the key provided was found and updated to the value.
/// The object to use as the key of the element to update.
/// The new value for the key if found.
/// The is read-only.
public bool TryUpdate(TKey key, TValue value)
{
Modify();
SeekResult result;
if (Seek(_root, key, out result))
{
result.Parent.Values[result.ParentIx] = new KeyValuePair(key, value);
return true;
}
return false;
}
///
/// Updates an element with the provided key to the value if it exists.
///
/// Returns true if the key provided was found and updated to the value.
/// The object to use as the key of the element to update.
/// The new value for the key if found.
/// The value that is compared to the value of the element with key.
/// The is read-only.
public bool TryUpdate(TKey key, TValue value, TValue comparisonValue)
{
Modify();
SeekResult result;
if (Seek(_root, key, out result) &&
EqualityComparer.Default.Equals(result.Parent.Values[result.ParentIx].Value, comparisonValue))
{
result.Parent.Values[result.ParentIx] = new KeyValuePair(key, value);
return true;
}
return false;
}
///
/// Removes the element with the specified key from the .
///
///
/// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original .
///
/// The key of the element to remove.
/// The value that was removed.
/// The is read-only.
public bool TryRemove(TKey key, out TValue value)
{
Modify();
KeyValuePair item = new KeyValuePair(key, default(TValue));
if (Remove(null, -1, _root, ref item))
{
value = item.Value;
return true;
}
value = default(TValue);
return false;
}
///
/// Returns all the items of this collection as an array of .
///
public KeyValuePair[] ToArray()
{
KeyValuePair[] array = new KeyValuePair[_count];
CopyTo(array, 0);
return array;
}
///
/// Returns the first key and it's associated value.
///
public KeyValuePair First()
{
KeyValuePair result;
if (!TryGetFirst(out result))
throw new InvalidOperationException();
return result;
}
///
/// Returns the first key and it's associated value.
///
public bool TryGetFirst(out KeyValuePair item)
{
if(_count == 0)
{
item = default(KeyValuePair);
return false;
}
item = _first.Values[0];
return true;
}
///
/// Returns the last key and it's associated value.
///
public KeyValuePair Last()
{
KeyValuePair result;
if (!TryGetLast(out result))
throw new InvalidOperationException();
return result;
}
///
/// Returns the last key and it's associated value.
///
public bool TryGetLast(out KeyValuePair item)
{
if (_count == 0)
{
item = default(KeyValuePair);
return false;
}
item = _last.Values[_last.Count-1];
return true;
}
///
/// Inclusivly enumerates from start key to the end of the collection
///
public IEnumerable> EnumerateFrom(TKey start)
{
SeekResult result;
Seek(_root, start, out result);
return new Enumerator(result.Parent, result.ParentIx);
}
///
/// Inclusivly enumerates from start key to stop key
///
public IEnumerable> EnumerateRange(TKey start, TKey end)
{
SeekResult result;
Seek(_root, start, out result);
return new Enumerator(result.Parent, result.ParentIx, x => (_comparer.Compare(x.Key, end) <= 0));
}
object ICloneable.Clone() { return Clone(); }
///
/// Returns a writable clone of this collection.
///
public BTreeDictionary Clone()
{
BTreeDictionary copy = (BTreeDictionary)MemberwiseClone();
copy._first = copy._last = null;
copy._root = copy._root.Clone(ref copy._first, ref copy._last);
copy._isReadOnly = false;
return copy;
}
///
/// Returns a read-only clone of this collection. If this instance is already read-only the method will return this.
///
public BTreeDictionary MakeReadOnly()
{
if (_isReadOnly) return this;
BTreeDictionary copy = Clone();
copy._isReadOnly = true;
return copy;
}
#endregion
#region Implementation Details
void Modify() { if (_isReadOnly) throw new NotSupportedException("Collection is read-only."); }
struct SeekResult
{
public TValue Value;
public Node Parent;
public int ParentIx;
}
bool Seek(Node from, TKey item, out SeekResult found)
{
found = new SeekResult();
found.Value = default(TValue);
found.Parent = from;
found.ParentIx = -1;
KeyValuePair seek = new KeyValuePair(item, null);
while (!found.Parent.IsLeaf)
{
int ix = Array.BinarySearch>(found.Parent.Children, 1, found.Parent.Count - 1, seek, _kvcomparer);
if (ix < 0 && (ix = ~ix) > 0)
ix--;
found.Parent = found.Parent.Children[ix].Value;
}
found.ParentIx = Array.BinarySearch>(found.Parent.Values, 0, found.Parent.Count,
new KeyValuePair(item, found.Value), _kvcomparer);
if (found.ParentIx < 0)
{
found.ParentIx = ~found.ParentIx;
return false;
}
found.Value = found.Parent.Values[found.ParentIx].Value;
return true;
}
bool Add(Node parent, int myIx, Node me, KeyValuePair item, bool adding)
{
if (me.Count == _order)
{
if (parent == null)
{
_root = parent = new Node(_order, default(TKey), me);
myIx = 0;
}
TKey split;
Node next = new Node(_order, me, out split, ref _first, ref _last);
parent.Add(myIx + 1, split, next);
if (_comparer.Compare(split, item.Key) <= 0)
me = next;
}
int ix;
if (me.IsLeaf)
{
ix = Array.BinarySearch>(me.Values, 0, me.Count, item, _kvcomparer);
if (ix < 0) ix = ~ix;
else
{
if (adding) return false;
me.Values[ix] = item;
return true;
}
me.Add(ix, item);
_count++;
return true;
}
KeyValuePair seek = new KeyValuePair(item.Key, null);
ix = Array.BinarySearch>(me.Children, 1, me.Count - 1, seek, _kvcomparer);
if (ix < 0 && (ix = ~ix) > 0)
ix--;
return Add(me, ix, me.Children[ix].Value, item, adding);
}
bool Remove(Node parent, int myIx, Node me, ref KeyValuePair item)
{
if (parent == null && ReferenceEquals(me, _root) && me.Count == 1 && me.IsLeaf == false)
_root = me.Children[0].Value;
if (parent != null && me.Count <= (_order >> 2))
{
if (myIx == 0)
parent.Join(myIx, myIx + 1, ref _first, ref _last);
else
parent.Join(myIx - 1, myIx, ref _first, ref _last);
return Remove(null, -1, parent, ref item);
}
int ix;
if (me.IsLeaf)
{
ix = Array.BinarySearch>(me.Values, 0, me.Count, item, _kvcomparer);
if (ix < 0)
return false;
item = me.Values[ix];
me.RemoveValue(ix);
_count--;
return true;
}
KeyValuePair seek = new KeyValuePair(item.Key, null);
ix = Array.BinarySearch>(me.Children, 1, me.Count - 1, seek, _kvcomparer);
if (ix < 0 && (ix = ~ix) > 0)
ix--;
Node child = me.Children[ix].Value;
if (!Remove(me, ix, child, ref item))
return false;
return true;
}
#endregion
#region class KeyCollection
class KeyCollection : ICollection
{
private readonly BTreeDictionary _owner;
public KeyCollection(BTreeDictionary owner)
{
_owner = owner;
}
#region ICollection Members
public int Count { get { return _owner.Count; } }
public bool IsReadOnly { get { return true; } }
public bool Contains(TKey item)
{
return _owner.ContainsKey(item);
}
public void CopyTo(TKey[] array, int arrayIndex)
{
foreach (TKey key in this)
array[arrayIndex++] = key;
}
public IEnumerator GetEnumerator() { return new KeyEnumerator(_owner.GetEnumerator()); }
[Obsolete] System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
class KeyEnumerator : IEnumerator
{
private readonly IEnumerator> _e;
public KeyEnumerator(IEnumerator> e) { _e = e; }
public TKey Current { get { return _e.Current.Key; } }
[Obsolete] object System.Collections.IEnumerator.Current { get { return Current; } }
public void Dispose() { _e.Dispose(); }
public bool MoveNext() { return _e.MoveNext(); }
public void Reset() { _e.Reset(); }
}
[Obsolete] void ICollection.Add(TKey item) { throw new NotSupportedException(); }
[Obsolete] void ICollection.Clear() { throw new NotSupportedException(); }
[Obsolete] bool ICollection.Remove(TKey item) { throw new NotSupportedException(); }
#endregion
}
#endregion
#region class ValueCollection
class ValueCollection : ICollection
{
private readonly BTreeDictionary _owner;
public ValueCollection(BTreeDictionary owner)
{
_owner = owner;
}
#region ICollection Members
public int Count { get { return _owner.Count; } }
public bool IsReadOnly { get { return true; } }
public bool Contains(TValue item)
{
IEqualityComparer c = EqualityComparer.Default;
foreach (TValue value in this)
if (c.Equals(item, value))
return true;
return false;
}
public void CopyTo(TValue[] array, int arrayIndex)
{
foreach (TValue value in this)
array[arrayIndex++] = value;
}
public IEnumerator GetEnumerator() { return new ValueEnumerator(_owner.GetEnumerator()); }
[Obsolete] System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
class ValueEnumerator : IEnumerator
{
private readonly IEnumerator> _e;
public ValueEnumerator(IEnumerator> e) { _e = e; }
public TValue Current { get { return _e.Current.Value; } }
[Obsolete] object System.Collections.IEnumerator.Current { get { return Current; } }
public void Dispose() { _e.Dispose(); }
public bool MoveNext() { return _e.MoveNext(); }
public void Reset() { _e.Reset(); }
}
[Obsolete] void ICollection.Add(TValue item) { throw new NotSupportedException(); }
[Obsolete] void ICollection.Clear() { throw new NotSupportedException(); }
[Obsolete] bool ICollection.Remove(TValue item) { throw new NotSupportedException(); }
#endregion
}
#endregion
#region class Enumerator
class Enumerator : IEnumerator>, IEnumerable>
{
private readonly Node _start;
private readonly int _startIx;
private readonly Predicate> _fnContinue;
private Node _current;
private int _index;
private bool _valid;
public Enumerator(Node start, int index) : this(start, index, null) { }
public Enumerator(Node start, int index, Predicate> fnContinue)
{
_start = start;
_startIx = index;
_fnContinue = fnContinue;
Reset();
}
public void Reset()
{
_valid = false;
_current = _start;
_index = _startIx - 1;
}
public bool MoveNext()
{
if (_current == null) return false;
if (++_index >= _current.Count)
{
_current = _current.Next;
_index = 0;
}
_valid = _current != null && _index >= 0 && _index < _current.Count;
if (_valid && _fnContinue != null && !_fnContinue(Current))
_valid = false;
return _valid;
}
[Obsolete] object System.Collections.IEnumerator.Current { get { return Current; } }
public KeyValuePair Current
{
get
{
if (_valid)
return _current.Values[_index];
throw new InvalidOperationException();
}
}
public void Dispose()
{ }
[Obsolete]
IEnumerator> IEnumerable>.GetEnumerator()
{ return this; }
[Obsolete]
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{ return this; }
}
#endregion
#region class KvComparer
class KvComparer : IComparer>, IComparer>
{
private readonly IComparer _comparer;
public KvComparer(IComparer comparer)
{ _comparer = comparer; }
int IComparer>.Compare(KeyValuePair x, KeyValuePair y)
{
return _comparer.Compare(x.Key, y.Key);
}
int IComparer>.Compare(KeyValuePair x, KeyValuePair y)
{
return _comparer.Compare(x.Key, y.Key);
}
}
#endregion
#region class Node
[System.Diagnostics.DebuggerDisplay("Count = {_count}")]
private class Node
{
int _count;
KeyValuePair[] _children;
KeyValuePair[] _values;
public Node Next, Prev;
public int Count { get { return _count; } }
public bool IsLeaf { get { return _values != null; } }
public KeyValuePair[] Children { get { return _children; } }
public KeyValuePair[] Values { get { return _values; } }
public Node(int order)
{
_count = 0;
_values = new KeyValuePair[order];
}
public Node(int order, TKey key, Node node)
{
_count = 1;
_children = new KeyValuePair[order];
_children[0] = new KeyValuePair(key, node);
}
public Node(int order, Node split, out TKey splitKey, ref Node first, ref Node last)
{
if (split._values != null)
{
_values = new KeyValuePair[order];
int half = split._count >> 1;
Array.Copy(split._values, half, _values, 0, _count = (split._count - half));
Array.Clear(split._values, half, _count);
split._count = half;
splitKey = _values[0].Key;
Prev = split;
Node oldNext = split.Next;
split.Next = this;
if ((Next = oldNext) == null)
last = this;
else
Next.Prev = this;
}
else //if (split.Children != null)
{
_children = new KeyValuePair[order];
int half = split._count >> 1;
Array.Copy(split._children, half, _children, 0, _count = (split._count - half));
Array.Clear(split._children, half, _count);
split._count = half;
splitKey = _children[0].Key;
}
}
public void Add(int ix, KeyValuePair kv)
{
if (ix < _count)
Array.Copy(_values, ix, _values, ix + 1, _count - ix);
_values[ix] = kv;
_count++;
}
public void Add(int ix, TKey key, Node child)
{
if (ix < _count)
Array.Copy(_children, ix, _children, ix + 1, _count - ix);
_children[ix] = new KeyValuePair(key, child);
_count++;
}
public void RemoveValue(int ix)
{
_values[ix] = new KeyValuePair();
_count--;
if (ix < _count)
{
Array.Copy(_values, ix + 1, _values, ix, _count - ix);
_values[_count] = new KeyValuePair();
}
}
void RemoveChild(int ix, ref Node first, ref Node last)
{
Node child = _children[ix].Value;
if(child != null)
{
if (child.Next != null)
child.Next.Prev = child.Prev;
else if (ReferenceEquals(child, last))
last = child.Prev;
if (child.Prev != null)
child.Prev.Next = child.Next;
else if (ReferenceEquals(child, first))
first = child.Next;
}
_children[ix] = new KeyValuePair();
_count--;
if (ix < _count)
{
Array.Copy(_children, ix + 1, _children, ix, _count - ix);
_children[_count] = new KeyValuePair();
}
}
public void Join(int firstIx, int secondIx, ref Node nodeFirst, ref Node nodeLast)
{
if (IsLeaf || firstIx < 0 || secondIx >= Count) return;
Node first = Children[firstIx].Value;
Node second = Children[secondIx].Value;
int order = first.IsLeaf ? first.Values.Length : first.Children.Length;
int total = first.Count + second.Count;
if (total < order)
{
if (first.IsLeaf)
Array.Copy(second.Values, 0, first.Values, first.Count, second.Count);
else
Array.Copy(second.Children, 0, first.Children, first.Count, second.Count);
first._count += second.Count;
RemoveChild(secondIx, ref nodeFirst, ref nodeLast);
}
else if (first.IsLeaf)
{
KeyValuePair[] list = new KeyValuePair[total];
Array.Copy(first.Values, 0, list, 0, first.Count);
Array.Copy(second.Values, 0, list, first.Count, second.Count);
Array.Clear(first.Values, 0, first.Count);
Array.Clear(second.Values, 0, second.Count);
int half = total >> 1;
Array.Copy(list, 0, first.Values, 0, first._count = half);
Array.Copy(list, half, second.Values, 0, second._count = (total - half));
Children[secondIx] = new KeyValuePair(second.Values[0].Key, second);
}
else //if (first.IsLeaf == false)
{
KeyValuePair[] list = new KeyValuePair[total];
Array.Copy(first.Children, 0, list, 0, first.Count);
Array.Copy(second.Children, 0, list, first.Count, second.Count);
Array.Clear(first.Children, 0, first.Count);
Array.Clear(second.Children, 0, second.Count);
list[first.Count] = new KeyValuePair(Children[secondIx].Key, list[first.Count].Value);
int half = total >> 1;
Array.Copy(list, 0, first.Children, 0, first._count = half);
Array.Copy(list, half, second.Children, 0, second._count = (total - half));
Children[secondIx] = new KeyValuePair(second.Children[0].Key, second);
}
}
public Node Clone(ref Node first, ref Node last)
{
Node copy = (Node)MemberwiseClone();
if (copy.IsLeaf)
{
copy._values = (KeyValuePair[]) copy._values.Clone();
copy.Next = null;
copy.Prev = last;
if (first == null)
first = copy;
if (last != null)
last.Next = copy;
last = copy;
}
else
{
copy._children = new KeyValuePair[copy._children.Length];
for( int i=0; i < copy._count; i++)
copy._children[i] = new KeyValuePair(
_children[i].Key,
_children[i].Value.Clone(ref first, ref last)
);
}
return copy;
}
}
#endregion
///
/// Ensures data integrity or raises exception
///
[Conditional("DEBUG")]
public void DebugAssert()
{
#if DEBUG
Node foundFirst = _root;
while (!foundFirst.IsLeaf)
foundFirst = foundFirst.Children[0].Value;
if (!ReferenceEquals(_first, foundFirst))
throw new ApplicationException("The _first node reference is incorrect.");
Node foundLast = _root;
while (!foundLast.IsLeaf)
foundLast = foundLast.Children[foundLast.Count - 1].Value;
if (!ReferenceEquals(_last, foundLast))
throw new ApplicationException("The _last node reference is incorrect.");
int counter = 0;
var tmp = _first;
while (tmp != null)
{
if (tmp.Next == null && !ReferenceEquals(_last, tmp))
throw new ApplicationException("Forward links corrupted.");
if (!tmp.IsLeaf)
throw new ApplicationException("Non Leaf in linked list.");
counter += tmp.Count;
tmp = tmp.Next;
}
if (counter != _count)
throw new ApplicationException(String.Format("Found {0} items in links, {1} expected.", counter, _count));
counter = 0;
tmp = _last;
while (tmp != null)
{
if (tmp.Prev == null && !ReferenceEquals(_first, tmp))
throw new ApplicationException("Forward links corrupted.");
if (!tmp.IsLeaf)
throw new ApplicationException("Non Leaf in linked list.");
counter += tmp.Count;
tmp = tmp.Prev;
}
if (counter != _count)
throw new ApplicationException(String.Format("Found {0} items in links, {1} expected.", counter, _count));
counter = DescendAssert(_root);
if (counter != _count)
throw new ApplicationException(String.Format("Tree crawl found {0} items, {1} expected.", counter, _count));
#endif
}
#if DEBUG
private int DescendAssert(Node node)
{
var eq = EqualityComparer.Default;
int count = 0;
if (node.IsLeaf)
{
for (int i = 0; i < node.Values.Length; i++)
{
if (i < node.Count)
count++;
else
{
if (!eq.Equals(default(TValue), node.Values[i].Value))
throw new ApplicationException("Unexpected non-default value after count.");
}
}
}
else
{
for (int i=0; i < node.Children.Length; i++)
{
if (i < node.Count)
count += DescendAssert(node.Children[i].Value);
else
{
if (node.Children[i].Value != null)
throw new ApplicationException("Unexpected non-null child after count.");
}
}
}
if (count == 0 && _count != 0)
throw new ApplicationException("Unexpected empty count.");
return count;
}
#endif
}
}