using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BotSharp.MachineLearning.CRFLite.Utils { /// /// Represents a generic interface of an ordered collection. /// /// The type of elements in the collection. public interface ISortedCollection : ICollection { /// /// Gets the comparer used to order items in the collection. /// IComparer Comparer { get; } /// /// Get all items equal to or greater than the specified value, starting with the lowest index and moving forwards. /// IEnumerable WhereGreaterOrEqual(T value); /// /// Get all items less than or equal to the specified value, starting with the highest index and moving backwards. /// IEnumerable WhereLessOrEqualBackwards(T value); /// /// Gets the index of the first item greater than the specified value. /// /// int FirstIndexWhereGreaterThan(T value); /// /// Gets the index of the last item less than the specified key. /// int LastIndexWhereLessThan(T value); /// /// Gets the item at the specified index. /// T At(int index); /// /// Removes the item at the specified index. /// void RemoveAt(int index); /// /// Get all items starting at the index, and moving forward. /// IEnumerable ForwardFromIndex(int index); /// /// Get all items starting at the index, and moving backward. /// IEnumerable BackwardFromIndex(int index); } /// /// Represents a generic interface of ordered key/value pairs. /// /// The type of the key. /// The type of the value. public interface ISortedDictionary : IDictionary { /// /// Get all items having a key equal to or greater than the specified key, starting with the lowest index and moving forwards. /// IEnumerable> WhereGreaterOrEqual(TKey key); /// /// Get all items less than or equal to the specified value, starting with the highest index and moving backwards. /// IEnumerable> WhereLessOrEqualBackwards(TKey keyUpperBound); /// /// Gets the sorted collection of keys. /// new ISortedCollection Keys { get; } /// /// Gets the item at the specified index. /// KeyValuePair At(int index); /// /// Removes the item at the specified index. /// void RemoveAt(int index); /// /// Sets the value at the specified index. /// /// /// void SetValueAt(int index, TValue value); /// /// Get all items starting at the index, and moving forward. /// IEnumerable> ForwardFromIndex(int index); /// /// Get all items starting at the index, and moving backward. /// IEnumerable> BackwardFromIndex(int index); } /// /// An O(log N) implementation of the ISortedDictionary interface. /// /// The type for the sorted keys. /// The type for the associated values. public class BTreeDictionary : ISortedDictionary { #region Fields Node root; readonly Node first; readonly KeyCollection keys; readonly ValueCollection values; readonly IComparer keyComparer; private void ObjectInvariant() { } #endregion #region Construction /// /// Initializes a new BTreeDictionary instance optimized for the specified node capacity. /// /// The capacity in keys for each node in the tree structure. public BTreeDictionary(int nodeCapacity = 128) : this(Comparer.Default, nodeCapacity) { } /// /// Initializes a new BTreeDictionary instance. /// /// The comparer for ordering keys in the structure. /// The capacity in keys for each node in the tree structure. public BTreeDictionary(IComparer keyComparer, int nodeCapacity) { this.keyComparer = keyComparer; this.first = new Node(nodeCapacity); this.root = this.first; this.keys = new KeyCollection(this); this.values = new ValueCollection(this); } #endregion #region Properties /// /// Gets the value associated with the specified key. An arbitrary value will be chosen if key is a duplicate. /// /// The key for which to retrieve the associated value. /// The value associated with the key. public TValue this[TKey key] { get { TValue result; if (this.TryGetValue(key, out result)) return result; throw new InvalidOperationException("error"); } set { Node leaf; int pos; if (Node.Find(root, key, KeyComparer, 0, out leaf, out pos)) leaf.SetValue(pos, value); else { Node.Insert(key, ref leaf, ref pos, ref root); leaf.SetValue(pos, value); } } } /// /// Gets the number of key value pairs in the dictionary. /// public int Count { get { return this.root.TotalCount; } } /// /// Gets the key comparer. /// public IComparer KeyComparer { get { return this.keyComparer; } } /// /// Gets the collection of keys in the dictionary. /// public ISortedCollection Keys { get { return this.keys; } } public IList KeyList { get { return this.keys; } } /// /// Gets the collection of values in the dictionary. /// public ICollection Values { get { return this.values; } } public IList ValueList { get { return this.values; } } /// /// Gets or sets indication whether this dictionary is readonly or mutable. /// public bool IsReadOnly { get; set; } #endregion #region Methods /// /// Gets indication of whether the dictionary contains an entry for the specified key. /// /// The key. /// True if the dictionary contains the key; otherwise, false. public bool ContainsKey(TKey key) { Node leaf; int pos; var found = Node.Find(root, key, KeyComparer, 0, out leaf, out pos); return found; } /// /// Tries to get the value for the specified key. An arbitrary value will be chosen if key is a duplicate. /// /// The key for which to try to get the value. /// The value found for the specified key, or a default value if not found. /// True if the value was found; otherwise, false. public bool TryGetValue(TKey key, out TValue value) { try { Node leaf; int pos; var found = Node.Find(root, key, KeyComparer, 0, out leaf, out pos); value = found ? leaf.GetValue(pos) : default(TValue); return found; } catch (System.Exception) { value = default(TValue); return false; } } /// /// Adds the specified key and value to the dictionary. /// /// The key to add. /// The value to associate with the key. public void Add(TKey key, TValue value) { Node leaf; int pos; var found = Node.Find(root, key, KeyComparer, 0, out leaf, out pos); if (found) { //The key is already in the dictionary, throw exception out throw new InvalidOperationException("The key is already in the dictionary"); } Node.Insert(key, ref leaf, ref pos, ref root); leaf.SetValue(pos, value); } /// /// Gets the key value pair at the specified index. /// /// The index at which to get the key value pair. /// The key value pair at the specified index. public KeyValuePair At(int index) { var leaf = Node.LeafAt(root, ref index); return new KeyValuePair(leaf.GetKey(index), leaf.GetValue(index)); } /// /// Clears the dictionary of all items. /// public void Clear() { Node.Clear(first); root = first; } /// /// Remove the key and associated value from the dictionary. /// /// The key to remove. /// True if the key was removed; otherwise, false if key was not found. public bool Remove(TKey key) { Node leaf; int pos; if (!Node.Find(root, key, KeyComparer, 0, out leaf, out pos)) return false; Node.Remove(leaf, pos, ref root); return true; } /// /// Removes the key and associated value from the dictionary at the specified index. /// /// The index at which to remove the key value pair. public void RemoveAt(int index) { var leaf = Node.LeafAt(root, ref index); Node.Remove(leaf, index, ref root); } /// /// Get all items starting at the index, and moving forward. /// public IEnumerable> ForwardFromIndex(int index) { var node = Node.LeafAt(root, ref index); return Node.ForwardFromIndex(node, index); } /// /// Get all items starting at the index, and moving backward. /// public IEnumerable> BackwardFromIndex(int index) { var node = Node.LeafAt(root, ref index); return Node.BackwardFromIndex(node, index); } /// /// Sets the value at the specified index, leaving the key unchanged. /// /// The index at which to set the value. /// The value to associate at the specified index. public void SetValueAt(int index, TValue value) { var leaf = Node.LeafAt(root, ref index); leaf.SetValue(index, value); } /// /// Gets an enumerator of key value pairs for the entire collection in sorted ascending order. /// /// public IEnumerator> GetEnumerator() { return Node.ForwardFromIndex(first, 0).GetEnumerator(); } /// /// Gets an enumerator of key value pairs in ascending order by key, for all items with a key /// equal or greater than the specified key lower bound. /// /// The value at which to start returning keys. /// All key value pairs having a key equal or greater than the lower bound, in key ascending order. public IEnumerable> WhereGreaterOrEqual(TKey keyLowerBound) { Node leaf; int leafPos; Node.Find(root, keyLowerBound, KeyComparer, 0, out leaf, out leafPos); return Node.ForwardFromIndex(leaf, leafPos); } /// /// Gets an enumerator of key value pairs in descending order by key, for all items with a key /// less than or equal than the specified key upper bound. /// /// The value at which to start returning keys. /// All key value pairs having a key equal or less than the upper bound, in key descending order. public IEnumerable> WhereLessOrEqualBackwards(TKey keyUpperBound) { Node leaf; int leafPos; var found = Node.Find(root, keyUpperBound, KeyComparer, 0, out leaf, out leafPos); if (!found) --leafPos; return Node.BackwardFromIndex(leaf, leafPos); } /// /// Copy the entire dictionary to the specified array, starting at the specified array index. /// /// The array into which to copy. /// The index at which to start copying. public void CopyTo(KeyValuePair[] array, int arrayIndex) { foreach (var item in this) array[arrayIndex++] = item; } #endregion #region Implementation - Nested Types sealed class Node { #region Fields readonly TKey[] keys; readonly TValue[] values; readonly Node[] nodes; int nodeCount; int totalCount; Node parent; Node next; Node prev; private void ObjectInvariant() { } #endregion #region Construction /// /// Initialize the first node in the BTree structure. /// public Node(int nodeCapacity) : this(nodeCapacity, true) { } #endregion #region Properties public int TotalCount { get { return this.totalCount; } } public bool IsRoot { get { return this.parent == null; } } public bool IsLeaf { get { return nodes == null; } } public int NodeCount { get { return this.nodeCount; } } #endregion #region Methods /// /// Gets the key at the specified position. /// public TKey GetKey(int pos) { return this.keys[pos]; } /// /// Gets the value at the specified position. /// public TValue GetValue(int pos) { return this.values[pos]; } /// /// Sets the value at the specified position. /// public void SetValue(int pos, TValue value) { this.values[pos] = value; } /// /// Get the leaf node at the specified index in the tree defined by the specified root. /// public static Node LeafAt(Node root, ref int pos) { int nodeIndex = 0; while (true) { if (root.nodes == null) { return root; } var node = root.nodes[nodeIndex]; if (pos < node.totalCount) { root = node; nodeIndex = 0; } else { pos -= node.totalCount; ++nodeIndex; } } } /// /// Find the node and index in the tree defined by the specified root. /// public static bool Find(Node root, TKey key, IComparer keyComparer, int duplicatesBias, out Node leaf, out int pos) { pos = Array.BinarySearch(root.keys, 0, root.nodeCount, key, keyComparer); while (root.nodes != null) { if (pos >= 0) { if (duplicatesBias != 0) MoveToDuplicatesBoundary(key, keyComparer, duplicatesBias, ref root, ref pos); // Found an exact match. Move down one level. root = root.nodes[pos]; } else { // No exact match. Find greatest lower bound. pos = ~pos; if (pos > 0) --pos; root = root.nodes[pos]; } pos = Array.BinarySearch(root.keys, 0, root.nodeCount, key, keyComparer); } leaf = root; if (pos < 0) { pos = ~pos; return false; } if (duplicatesBias != 0) MoveToDuplicatesBoundary(key, keyComparer, duplicatesBias, ref leaf, ref pos); return true; } /// /// Insert a new key into the leaf node at the specified position. /// public static void Insert(TKey key, ref Node leaf, ref int pos, ref Node root) { // Make sure there is space for the new key. if (EnsureSpace(leaf, ref root) && pos > leaf.nodeCount) { pos -= leaf.nodeCount; leaf = leaf.next; } // Insert the key. int moveCount = leaf.nodeCount - pos; Array.Copy(leaf.keys, pos, leaf.keys, pos + 1, moveCount); leaf.keys[pos] = key; ++leaf.nodeCount; EnsureParentKey(leaf, pos); // Insert space for the value. Caller is responsible for filling in value. Array.Copy(leaf.values, pos, leaf.values, pos + 1, moveCount); // Update total counts. for (var node = leaf; node != null; node = node.parent) ++node.totalCount; } /// /// Remove the item from the node at the specified position. /// public static bool Remove(Node leaf, int pos, ref Node root) { // Update total counts. for (var node = leaf; node != null; node = node.parent) --node.totalCount; // Remove the key and value from the node. --leaf.nodeCount; Array.Copy(leaf.keys, pos + 1, leaf.keys, pos, leaf.nodeCount - pos); Array.Copy(leaf.values, pos + 1, leaf.values, pos, leaf.nodeCount - pos); leaf.keys[leaf.nodeCount] = default(TKey); leaf.values[leaf.nodeCount] = default(TValue); // Make sure parent keys index correctly into this node. if (leaf.nodeCount > 0) EnsureParentKey(leaf, pos); // Merge this node with others if it is below the node capacity threshold. Merge(leaf, ref root); return true; } /// /// Get an ascending enumerable for the collection, starting an the index in the specified leaf node. /// public static IEnumerable> ForwardFromIndex(Node leaf, int pos) { while (leaf != null) { while (pos < leaf.nodeCount) { yield return new KeyValuePair(leaf.GetKey(pos), leaf.GetValue(pos)); ++pos; } pos -= leaf.nodeCount; leaf = leaf.next; } } /// /// Get a descending enumerable, starting at the index in the specified leaf node. /// public static IEnumerable> BackwardFromIndex(Node leaf, int pos) { if (pos == -1) { // Handle special case to start moving in the previous node. leaf = leaf.prev; if (leaf != null) pos = leaf.nodeCount - 1; else pos = 0; } else if (pos == leaf.NodeCount) { // Handle special case to start moving in the next node. if (leaf.next == null) --pos; else { leaf = leaf.next; pos = 0; } } // Loop thru collection, yielding each value in sequence. while (leaf != null) { while (pos >= 0) { yield return new KeyValuePair(leaf.GetKey(pos), leaf.GetValue(pos)); --pos; } leaf = leaf.prev; if (leaf != null) pos += leaf.nodeCount; } } /// /// Clear all keys and values from the specified node. /// public static void Clear(Node firstNode) { int clearCount = firstNode.nodeCount; Array.Clear(firstNode.keys, 0, clearCount); Array.Clear(firstNode.values, 0, clearCount); firstNode.nodeCount = 0; firstNode.totalCount = 0; firstNode.parent = null; firstNode.next = null; } /// /// Get the index relative to the root node, for the position in the specified leaf. /// public static int GetRootIndex(Node leaf, int pos) { var node = leaf; var rootIndex = pos; while (node.parent != null) { int nodePos = Array.IndexOf(node.parent.nodes, node, 0, node.parent.nodeCount); for (int i = 0; i < nodePos; ++i) rootIndex += node.parent.nodes[i].totalCount; node = node.parent; } return rootIndex; } #endregion #region Implementation Node(int nodeCapacity, bool leaf) { this.keys = new TKey[nodeCapacity]; if (leaf) { this.values = new TValue[nodeCapacity]; this.nodes = null; } else { this.values = null; this.nodes = new Node[nodeCapacity]; } this.nodeCount = 0; this.totalCount = 0; this.parent = null; this.next = null; this.prev = null; } /// /// (Assumes: key is a duplicate in node at pos) Move to the side on the range of duplicates, /// as indicated by the sign of duplicatesBias. /// /// /// /// /// /// static void MoveToDuplicatesBoundary(TKey key, IComparer keyComparer, int duplicatesBias, ref Node node, ref int pos) { // Technically, we could adjust the binary search to perform most of this step, but duplicates // are usually unexpected.. algorithm is still O(log N), because scan include at most a scan thru two nodes // worth of keys, for each level. // Also, the binary search option would still need the ugliness of the special case for moving into the // previous node; it would only be a little faster, on average, assuming large numbers of duplicates were common. if (duplicatesBias < 0) { // Move backward over duplicates. while (pos > 0 && 0 == keyComparer.Compare(node.keys[pos - 1], key)) --pos; // Special case: duplicates can span backwards into the previous node because the parent // key pivot might be in the center for the duplicates. if (pos == 0 && node.prev != null) { var prev = node.prev; var prevPos = prev.NodeCount; while (prevPos > 0 && 0 == keyComparer.Compare(prev.keys[prevPos - 1], key)) { --prevPos; } if (prevPos < prev.NodeCount) { node = prev; pos = prevPos; } } } else { // Move forward over duplicates. while (pos < node.NodeCount - 1 && 0 == keyComparer.Compare(node.keys[pos + 1], key)) ++pos; } } static bool EnsureSpace(Node node, ref Node root) { if (node.nodeCount < node.keys.Length) return false; EnsureParent(node, ref root); EnsureSpace(node.parent, ref root); var sibling = new Node(node.keys.Length, node.nodes == null); sibling.next = node.next; sibling.prev = node; sibling.parent = node.parent; if (node.next != null) node.next.prev = sibling; node.next = sibling; int pos = Array.IndexOf(node.parent.nodes, node, 0, node.parent.nodeCount); int siblingPos = pos + 1; Array.Copy(node.parent.keys, siblingPos, node.parent.keys, siblingPos + 1, node.parent.nodeCount - siblingPos); Array.Copy(node.parent.nodes, siblingPos, node.parent.nodes, siblingPos + 1, node.parent.nodeCount - siblingPos); ++node.parent.nodeCount; node.parent.nodes[siblingPos] = sibling; int half = node.nodeCount / 2; int halfCount = node.nodeCount - half; Move(node, half, sibling, 0, halfCount); return true; } static void Move(Node source, int sourceIndex, Node target, int targetIndex, int moveCount) { Move(source.keys, sourceIndex, source.nodeCount, target.keys, targetIndex, target.nodeCount, moveCount); if (source.values != null) Move(source.values, sourceIndex, source.nodeCount, target.values, targetIndex, target.nodeCount, moveCount); int totalMoveCount; if (source.nodes == null) { totalMoveCount = moveCount; } else { Move(source.nodes, sourceIndex, source.nodeCount, target.nodes, targetIndex, target.nodeCount, moveCount); totalMoveCount = 0; for (int i = 0; i < moveCount; ++i) { var child = target.nodes[targetIndex + i]; child.parent = target; totalMoveCount += child.totalCount; } } source.nodeCount -= moveCount; target.nodeCount += moveCount; var sn = source; var tn = target; while (sn != null && sn != tn) { sn.totalCount -= totalMoveCount; tn.totalCount += totalMoveCount; sn = sn.parent; tn = tn.parent; } EnsureParentKey(source, sourceIndex); EnsureParentKey(target, targetIndex); } static void Move(TItem[] source, int sourceIndex, int sourceTotal, TItem[] target, int targetIndex, int targetTotal, int count) { Array.Copy(target, targetIndex, target, targetIndex + count, targetTotal - targetIndex); Array.Copy(source, sourceIndex, target, targetIndex, count); Array.Copy(source, sourceIndex + count, source, sourceIndex, sourceTotal - sourceIndex - count); Array.Clear(source, sourceTotal - count, count); } static void EnsureParent(Node node, ref Node root) { if (node.parent != null) return; var parent = new Node(node.keys.Length, false); parent.totalCount = node.totalCount; parent.nodeCount = 1; parent.keys[0] = node.keys[0]; parent.nodes[0] = node; node.parent = parent; root = parent; } static void EnsureParentKey(Node node, int pos) { while (pos == 0 && node.parent != null) { pos = Array.IndexOf(node.parent.nodes, node, 0, node.parent.nodeCount); node.parent.keys[pos] = node.keys[0]; node = node.parent; } } static void Merge(Node node, ref Node root) { if (node.nodeCount == 0) { // Handle special case: Empty node. if (node.parent == null) return; // Remove the node from the parent nodes. int pos = Array.IndexOf(node.parent.nodes, node, 0, node.parent.nodeCount); --node.parent.nodeCount; Array.Copy(node.parent.keys, pos + 1, node.parent.keys, pos, node.parent.nodeCount - pos); Array.Copy(node.parent.nodes, pos + 1, node.parent.nodes, pos, node.parent.nodeCount - pos); node.parent.keys[node.parent.nodeCount] = default(TKey); node.parent.nodes[node.parent.nodeCount] = null; // Make sure parent (of the parent) keys link down correctly. if (node.parent.nodeCount > 0) EnsureParentKey(node.parent, pos); // Delete the node from the next/prev linked list. node.prev.next = node.next; if (node.next != null) node.next.prev = node.prev; // Merge the parent node. Merge(node.parent, ref root); return; } if (node.next == null) { if (node.parent == null && node.nodeCount == 1 && node.nodes != null) { root = node.nodes[0]; root.parent = null; } return; } if (node.nodeCount >= node.keys.Length / 2) return; int count = node.next.nodeCount; if (node.nodeCount + count > node.keys.Length) count -= (node.nodeCount + count) / 2; Move(node.next, 0, node, node.nodeCount, count); Merge(node.next, ref root); } #endregion } abstract class KeyValueCollectionBase : ICollection { #region Fields protected readonly BTreeDictionary tree; #endregion #region Construction public KeyValueCollectionBase(BTreeDictionary tree) { this.tree = tree; } #endregion #region Properties public int Count { get { return tree.Count; } } #endregion #region Methods public abstract bool Contains(T item); public void CopyTo(T[] array, int arrayIndex) { foreach (var item in this) array[arrayIndex++] = item; } public abstract IEnumerator GetEnumerator(); #endregion #region ICollection<> members void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.IsReadOnly { get { return true; } } public bool Remove(T item) { throw new NotSupportedException(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } #endregion } sealed class ValueCollection : KeyValueCollectionBase, IList { #region Construction public ValueCollection(BTreeDictionary tree) : base(tree) { } #endregion #region Methods public override bool Contains(TValue item) { return this.tree.Any(keyValue => object.Equals(item, keyValue.Value)); } public override IEnumerator GetEnumerator() { return this.tree.Select(keyValue => keyValue.Value).GetEnumerator(); } #endregion int IList.IndexOf(TValue item) { throw new NotImplementedException(); } void IList.Insert(int index, TValue item) { throw new NotImplementedException(); } void IList.RemoveAt(int index) { throw new NotImplementedException(); } public TValue this[int index] { get { return this.tree.At(index).Value; } set { throw new NotImplementedException(); } } } sealed class KeyCollection : KeyValueCollectionBase, ISortedCollection, IList { #region Construction public KeyCollection(BTreeDictionary tree) : base(tree) { } #endregion #region Properties public IComparer Comparer { get { return tree.KeyComparer; } } #endregion #region Methods public int FirstIndexWhereGreaterThan(TKey value) { Node leaf; int pos; var found = Node.Find(tree.root, value, tree.KeyComparer, 0, out leaf, out pos); int result = Node.GetRootIndex(leaf, pos); if (found) ++result; return result; } public int LastIndexWhereLessThan(TKey value) { Node leaf; int pos; var found = Node.Find(tree.root, value, tree.KeyComparer, 0, out leaf, out pos); int result = Node.GetRootIndex(leaf, pos); if (found) --result; return result; } public TKey At(int index) { return this.tree.At(index).Key; } public override bool Contains(TKey item) { return tree.ContainsKey(item); } public override IEnumerator GetEnumerator() { return tree.Select(keyValue => keyValue.Key).GetEnumerator(); } public IEnumerable WhereGreaterOrEqual(TKey lowerBound) { return tree.WhereGreaterOrEqual(lowerBound).Select(keyValue => keyValue.Key); } public IEnumerable WhereLessOrEqualBackwards(TKey upperBound) { return tree.WhereLessOrEqualBackwards(upperBound).Select(keyValue => keyValue.Key); } public IEnumerable ForwardFromIndex(int index) { return this.tree.ForwardFromIndex(index).Select(item => item.Key); } public IEnumerable BackwardFromIndex(int index) { return this.tree.BackwardFromIndex(index).Select(item => item.Key); } #endregion #region IEnumerable members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } #endregion #region ISortedCollection<> members void ISortedCollection.RemoveAt(int index) { throw new NotSupportedException(); } #endregion int IList.IndexOf(TKey item) { throw new NotImplementedException(); } void IList.Insert(int index, TKey item) { throw new NotImplementedException(); } void IList.RemoveAt(int index) { throw new NotImplementedException(); } public TKey this[int index] { get { return this.tree.At(index).Key; } set { throw new NotImplementedException(); } } } #endregion #region IDictionary<> members ICollection IDictionary.Keys { get { return this.Keys; } } #endregion #region ICollection<> members void ICollection>.Add(KeyValuePair item) { this.Add(item.Key, item.Value); } bool ICollection>.Contains(KeyValuePair item) { TValue value; return this.TryGetValue(item.Key, out value) && object.Equals(item.Value, value); } bool ICollection>.IsReadOnly { get { return false; } } bool ICollection>.Remove(KeyValuePair item) { TValue value; if (this.TryGetValue(item.Key, out value) && object.Equals(item.Value, value)) { this.Remove(item.Key); return true; } return false; } #endregion #region IEnumerable members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } #endregion } }