#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;
namespace CSharpTest.Net.Collections
{
///
/// Implements an IList interface for an in-memory B+Tree of unique values
///
[System.Diagnostics.DebuggerDisplay("Count = {_count}")]
public class BTreeList : IList, ICloneable, IDisposable
{
/// 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;
/// Constructs a BTreeList instance.
public BTreeList() : this(DefaultOrder, Comparer.Default) { }
/// Constructs a BTreeList instance.
public BTreeList(IComparer comparer) : this(DefaultOrder, comparer) { }
/// Constructs a BTreeList instance.
public BTreeList(int order, IComparer comparer)
{
_isReadOnly = false;
_order = order;
_comparer = comparer;
_kvcomparer = new KvComparer(_comparer);
Clear();
}
/// Constructs a BTreeList instance.
public BTreeList(IEnumerable copyFrom) : this(DefaultOrder, Comparer.Default, copyFrom) { }
/// Constructs a BTreeList instance.
public BTreeList(IComparer comparer, IEnumerable copyFrom) : this(DefaultOrder, comparer, copyFrom) { }
/// Constructs a BTreeList instance.
public BTreeList(int order, IComparer comparer, IEnumerable copyFrom)
: this(order, comparer)
{
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 IList Members
///
/// Removes all items from the .
///
/// The Collection is read-only.
public void Clear()
{
Modify();
_count = 0;
_root = new Node(_order);
_first = _last = _root;
}
///
/// Warning O(n) operation: This method works; however, it is not intended for use on sufficiently large lists.
///
T IList.this[int index]
{
get
{
if (index >= 0 && index < _count)
{
Node parent = _first;
while (parent != null)
{
if (index < parent.Count)
return parent.Values[index];
index -= parent.Count;
parent = parent.Next;
}
}
throw new ArgumentOutOfRangeException("index");
}
set { throw new NotSupportedException(); }
}
///
/// Adds an element with the provided value to the .
///
/// The object to use as the value of the element to add.
/// An element with the same value already exists in the .
/// The is read-only.
public void Add(T value)
{
Modify();
if (!Add(null, -1, _root, value, true))
throw new ArgumentException("An item with the same value has already been added.");
}
///
/// Adds a set of items to the .
///
/// The items to add to the .
/// An element with the same value already exists in the .
/// The is read-only.
public void AddRange(IEnumerable items)
{
Modify();
foreach (T pair in items)
Add(pair);
}
///
/// Determines whether the contains a specific value pair.
///
/// true if is found in the ; otherwise, false.
/// The object to locate in the .
public bool Contains(T item)
{
SeekResult result;
return Seek(_root, item, out result);
}
///
/// Warning O(n) operation: This method works; however, it is not intended for use on sufficiently large lists.
/// Determines the index of a specific item in the .
///
/// The index of if found in the list; otherwise, -1.
/// The object to locate in the .
int IList.IndexOf(T item)
{
SeekResult result;
if(Seek(_root, item, out result))
{
int ordinal = result.ParentIx;
while((result.Parent = result.Parent.Prev) != null)
ordinal += result.Parent.Count;
return ordinal;
}
return -1;
}
void IList.Insert(int index, T item)
{ throw new NotSupportedException(); }
///
/// Warning O(n) operation: This method works; however, it is not intended for use on sufficiently large lists.
///
void IList.RemoveAt(int index)
{
Modify();
IList l = this;
Remove(l[index]);
}
///
/// Removes the element with the specified value from the .
///
///
/// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original .
///
/// The value of the element to remove.
/// The is read-only.
public bool Remove(T value)
{
Modify();
return Remove(null, -1, _root, value);
}
///
/// Copies the elements of the to an , starting at a particular index.
///
public void CopyTo(T[] 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 an element with the provided value to the .
///
/// The object to use as the value of the element to add.
/// The is read-only.
public bool TryAddItem(T value)
{
Modify();
return Add(null, -1, _root, value, true);
}
///
/// Returns all the items of this collection as an array of .
///
public T[] ToArray()
{
T[] array = new T[_count];
CopyTo(array, 0);
return array;
}
///
/// Inclusivly enumerates from start value to the end of the collection
///
public IEnumerable EnumerateFrom(T start)
{
SeekResult result;
Seek(_root, start, out result);
return new Enumerator(result.Parent, result.ParentIx);
}
///
/// Inclusivly enumerates from start value to stop value
///
public IEnumerable EnumerateRange(T start, T end)
{
SeekResult result;
Seek(_root, start, out result);
return new Enumerator(result.Parent, result.ParentIx, x => (_comparer.Compare(x, end) <= 0));
}
object ICloneable.Clone() { return Clone(); }
///
/// Returns a writable clone of this collection.
///
public BTreeList Clone()
{
BTreeList copy = (BTreeList)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 BTreeList MakeReadOnly()
{
if (_isReadOnly) return this;
BTreeList 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 Node Parent;
public int ParentIx;
}
bool Seek(Node from, T item, out SeekResult found)
{
found = new SeekResult();
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, item, _comparer);
if (found.ParentIx < 0)
{
found.ParentIx = ~found.ParentIx;
return false;
}
return true;
}
bool Add(Node parent, int myIx, Node me, T item, bool adding)
{
if (me.Count == _order)
{
if (parent == null)
{
_root = parent = new Node(_order, default(T), me);
myIx = 0;
}
T split;
Node next = new Node(_order, me, out split, ref _first, ref _last);
parent.Add(myIx + 1, split, next);
if (_comparer.Compare(split, item) <= 0)
me = next;
}
int ix;
if (me.IsLeaf)
{
ix = Array.BinarySearch(me.Values, 0, me.Count, item, _comparer);
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, 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, T 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, item);
}
int ix;
if (me.IsLeaf)
{
ix = Array.BinarySearch(me.Values, 0, me.Count, item, _comparer);
if (ix < 0)
return false;
me.RemoveValue(ix);
_count--;
return true;
}
KeyValuePair seek = new KeyValuePair(item, 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, item))
return false;
return true;
}
#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 T 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>
{
private readonly IComparer _comparer;
public KvComparer(IComparer comparer)
{ _comparer = comparer; }
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;
T[] _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 T[] Values { get { return _values; } }
public Node(int order)
{
_count = 0;
_values = new T[order];
}
public Node(int order, T value, Node node)
{
_count = 1;
_children = new KeyValuePair[order];
_children[0] = new KeyValuePair(value, node);
}
public Node(int order, Node split, out T splitKey, ref Node first, ref Node last)
{
if (split._values != null)
{
_values = new T[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];
Prev = split;
Node oldNext = split.Next;
split.Next = this;
if ((Next = oldNext) == null)
last = split;
}
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, T kv)
{
if (ix < _count)
Array.Copy(_values, ix, _values, ix + 1, _count - ix);
_values[ix] = kv;
_count++;
}
public void Add(int ix, T value, Node child)
{
if (ix < _count)
Array.Copy(_children, ix, _children, ix + 1, _count - ix);
_children[ix] = new KeyValuePair(value, child);
_count++;
}
public void RemoveValue(int ix)
{
_values[ix] = default(T);
_count--;
if (ix < _count)
Array.Copy(_values, ix + 1, _values, ix, _count - ix);
}
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);
}
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)
{
T[] list = new T[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], 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 = (T[])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
}
}