Initial Commit
commit
f8ccd0d505
|
@ -0,0 +1,41 @@
|
||||||
|
# Autosave files
|
||||||
|
*~
|
||||||
|
|
||||||
|
# build
|
||||||
|
[Oo]bj/
|
||||||
|
[Bb]in/
|
||||||
|
packages/
|
||||||
|
TestResults/
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.DS_Store
|
||||||
|
*.sln.cache
|
||||||
|
*.suo
|
||||||
|
*.cache
|
||||||
|
*.pidb
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.log
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.user
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
Thumbs.db
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Mac bundle stuff
|
||||||
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# resharper
|
||||||
|
*_Resharper.*
|
||||||
|
*.Resharper
|
||||||
|
|
||||||
|
# dotCover
|
||||||
|
*.dotCover
|
|
@ -0,0 +1,679 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class BTree<K,V> : IDict<K,V>
|
||||||
|
{
|
||||||
|
public Comparison<K> Comparison { get; }
|
||||||
|
public int Count => count;
|
||||||
|
public bool Empty => count == 0;
|
||||||
|
|
||||||
|
public int LeftDepth => headNode != null ? headNode.LeftDepth : 0;
|
||||||
|
public int RightDepth => headNode != null ? headNode.RightDepth : 0;
|
||||||
|
public int Depth => Math.Max(LeftDepth, RightDepth);
|
||||||
|
|
||||||
|
public int LimBalance
|
||||||
|
{
|
||||||
|
get => limBalance;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == 0)
|
||||||
|
throw new ArgumentException(nameof(value));
|
||||||
|
limBalance = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode headNode;
|
||||||
|
private int count;
|
||||||
|
private int limBalance;
|
||||||
|
|
||||||
|
public BTree()
|
||||||
|
{
|
||||||
|
if (!typeof(K).GetInterfaces().Contains(typeof(IComparable<K>)))
|
||||||
|
{
|
||||||
|
Comparison = (K x, K y) => x.GetHashCode() - y.GetHashCode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Comparison = (K x, K y) => ((IComparable<K>)x).CompareTo(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public BTree(Comparison<K> comparison)
|
||||||
|
{
|
||||||
|
Comparison = comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V this[K key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
TreeNode node = Find(key);
|
||||||
|
if (object.ReferenceEquals(node,null))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
return node.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
TreeNode node = Find(key);
|
||||||
|
if (object.ReferenceEquals(node, null))
|
||||||
|
{
|
||||||
|
Add(key, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Boolean TryGet(K key, out V value)
|
||||||
|
{
|
||||||
|
TreeNode node = Find(key);
|
||||||
|
if (object.ReferenceEquals(node, null))
|
||||||
|
{
|
||||||
|
value = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Add(K key,V value)
|
||||||
|
{
|
||||||
|
TreeNode treeNode = new TreeNode(this, key, value);
|
||||||
|
Insert(treeNode);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
public virtual void TryAdd(K key,V value)
|
||||||
|
{
|
||||||
|
TreeNode treeNode = Find(key);
|
||||||
|
if (object.ReferenceEquals(treeNode, null))
|
||||||
|
Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Remove(K key)
|
||||||
|
{
|
||||||
|
TreeNode node = Find(key);
|
||||||
|
if (object.ReferenceEquals(node, null))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
|
||||||
|
Remove(node);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
public virtual bool TryRemove(K key)
|
||||||
|
{
|
||||||
|
TreeNode node = Find(key);
|
||||||
|
if (node != null)
|
||||||
|
Remove(node);
|
||||||
|
|
||||||
|
return node != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(K key)
|
||||||
|
{
|
||||||
|
return Find(key) != null;
|
||||||
|
}
|
||||||
|
public bool ContainsValue(V value)
|
||||||
|
{
|
||||||
|
return Values.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public K GetKey(K key)
|
||||||
|
{
|
||||||
|
if (TryGetKey(key, out K storedKey))
|
||||||
|
return storedKey;
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
public bool TryGetKey(K key,out K storedKey)
|
||||||
|
{
|
||||||
|
TreeNode treeNode = Find(key);
|
||||||
|
if (treeNode == null)
|
||||||
|
{
|
||||||
|
storedKey = default(K);
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
storedKey = treeNode.Key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
headNode = null;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K First()
|
||||||
|
{
|
||||||
|
TreeNode node = First(headNode);
|
||||||
|
if (node != null)
|
||||||
|
return node.Key;
|
||||||
|
throw new KeyNotFoundException("BTree is empty");
|
||||||
|
}
|
||||||
|
public V FirstValue()
|
||||||
|
{
|
||||||
|
TreeNode node = First(headNode);
|
||||||
|
if (node != null)
|
||||||
|
return node.Value;
|
||||||
|
throw new KeyNotFoundException("BTree is empty");
|
||||||
|
}
|
||||||
|
public bool TryGetFirstValue(out V value)
|
||||||
|
{
|
||||||
|
TreeNode node = First(headNode);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
value = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K Last()
|
||||||
|
{
|
||||||
|
TreeNode node = Last(headNode);
|
||||||
|
if (node != null)
|
||||||
|
return node.Key;
|
||||||
|
throw new KeyNotFoundException("BTree is empty");
|
||||||
|
}
|
||||||
|
public V LastValue()
|
||||||
|
{
|
||||||
|
TreeNode node = Last(headNode);
|
||||||
|
if (node != null)
|
||||||
|
return node.Value;
|
||||||
|
throw new KeyNotFoundException("BTree is empty");
|
||||||
|
}
|
||||||
|
public bool TryGetLastValue(out V value)
|
||||||
|
{
|
||||||
|
TreeNode node = Last(headNode);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
value = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K Previous(K current)
|
||||||
|
{
|
||||||
|
TreeNode node = Previous(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
return node.Key;
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
public bool TryGetPrevious(K current, out K previous)
|
||||||
|
{
|
||||||
|
TreeNode node = Previous(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
previous = node.Key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
previous = default(K);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool TryGetPreviousValue(K current, out V previous)
|
||||||
|
{
|
||||||
|
TreeNode node = Previous(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
previous = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
previous = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K Next(K current)
|
||||||
|
{
|
||||||
|
TreeNode node = Next(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
return node.Key;
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
public bool TryGetNext(K current, out K next)
|
||||||
|
{
|
||||||
|
TreeNode node = Next(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
next = node.Key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
next = default(K);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool TryGetNextValue(K current, out V next)
|
||||||
|
{
|
||||||
|
TreeNode node = Next(Find(current));
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
next = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
next = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private TreeNode First(TreeNode node)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(node, null))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
while (node.Left != null)
|
||||||
|
node = node.Left;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode Last(TreeNode node)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(node, null))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
while (node.Right != null)
|
||||||
|
node = node.Right;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode Find(K key)
|
||||||
|
{
|
||||||
|
TreeNode node = headNode;
|
||||||
|
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
int comp = Comparison(key, node.Key);
|
||||||
|
if (comp == 0)
|
||||||
|
return node;
|
||||||
|
else if (comp < 0)
|
||||||
|
node = node.Left;
|
||||||
|
else
|
||||||
|
node = node.Right;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private TreeNode FindFirstGE(K key)
|
||||||
|
{
|
||||||
|
TreeNode node = headNode;
|
||||||
|
TreeNode minNode = null;
|
||||||
|
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
if ((Comparison(node.Key, key) >= 0) && ((minNode == null) || (Comparison(node.Key, minNode.Key) < 0)))
|
||||||
|
minNode = node;
|
||||||
|
|
||||||
|
int comp = Comparison(key, node.Key);
|
||||||
|
if (comp == 0)
|
||||||
|
return node;
|
||||||
|
else if (comp < 0)
|
||||||
|
{
|
||||||
|
if (node.Left == null)
|
||||||
|
return minNode;
|
||||||
|
node = node.Left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node.Right == null)
|
||||||
|
return minNode;
|
||||||
|
node = node.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode Previous(TreeNode node)
|
||||||
|
{
|
||||||
|
if (node.Left != null)
|
||||||
|
return Last(node.Left);
|
||||||
|
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(node.Parent, null))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (node.Parent.Right == node)
|
||||||
|
return node.Parent;
|
||||||
|
|
||||||
|
node = node.Parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode Next(TreeNode node)
|
||||||
|
{
|
||||||
|
if (node.Right != null)
|
||||||
|
return First(node.Right);
|
||||||
|
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(node.Parent, null))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (node.Parent.Left == node)
|
||||||
|
return node.Parent;
|
||||||
|
|
||||||
|
node = node.Parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Insert(TreeNode newNode)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(headNode, null))
|
||||||
|
{
|
||||||
|
headNode = newNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TreeNode node = headNode;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int c = Comparison(newNode.Key, node.Key);
|
||||||
|
if (c == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Key exists");
|
||||||
|
}
|
||||||
|
else if (c < 0)
|
||||||
|
{
|
||||||
|
if (node.Left == null)
|
||||||
|
{
|
||||||
|
newNode.Attach(node);
|
||||||
|
Balance(node.Parent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node = node.Left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node.Right == null)
|
||||||
|
{
|
||||||
|
newNode.Attach(node);
|
||||||
|
Balance(node.Parent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node = node.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Remove(TreeNode node)
|
||||||
|
{
|
||||||
|
TreeNode p = node.Parent;
|
||||||
|
|
||||||
|
if ((node.Left != null) && (node.Right != null))
|
||||||
|
{
|
||||||
|
TreeNode replace = Last(node.Left);
|
||||||
|
TreeNode replaceParent = replace.Parent;
|
||||||
|
|
||||||
|
if (replaceParent == node)
|
||||||
|
{
|
||||||
|
node.Detach();
|
||||||
|
node.Right.Attach(node.Left);
|
||||||
|
node.Left.Attach(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TreeNode left = node.Left;
|
||||||
|
TreeNode right = node.Right;
|
||||||
|
|
||||||
|
node.Detach();
|
||||||
|
if (left != null)
|
||||||
|
left.Detach();
|
||||||
|
if (right != null)
|
||||||
|
right.Detach();
|
||||||
|
|
||||||
|
replace.Detach();
|
||||||
|
if (replace.Left != null)
|
||||||
|
replace.Left.Attach(replaceParent);
|
||||||
|
|
||||||
|
replace.Attach(p);
|
||||||
|
if (left != null)
|
||||||
|
left.Attach(replace);
|
||||||
|
if (right != null)
|
||||||
|
right.Attach(replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
Balance(replace);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
node.Detach();
|
||||||
|
if (node.Left != null)
|
||||||
|
node.Left.Attach(p);
|
||||||
|
else if (node.Right != null)
|
||||||
|
node.Right.Attach(p);
|
||||||
|
|
||||||
|
Balance(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RotateRight(TreeNode node)
|
||||||
|
{
|
||||||
|
TreeNode l = node.Left;
|
||||||
|
TreeNode p = node.Parent;
|
||||||
|
|
||||||
|
node.Detach();
|
||||||
|
l.Attach(p);
|
||||||
|
if (l.Right!= null)
|
||||||
|
l.Right.Attach(node);
|
||||||
|
node.Attach(l);
|
||||||
|
}
|
||||||
|
private void RotateLeft(TreeNode node)
|
||||||
|
{
|
||||||
|
TreeNode r = node.Right;
|
||||||
|
TreeNode p = node.Parent;
|
||||||
|
|
||||||
|
node.Detach();
|
||||||
|
r.Attach(p);
|
||||||
|
if (r.Left != null)
|
||||||
|
r.Left.Attach(node);
|
||||||
|
node.Attach(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Balance(TreeNode node)
|
||||||
|
{
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
TreeNode p = node.Parent;
|
||||||
|
int myBalance = node.LeftDepth - node.RightDepth;
|
||||||
|
if (myBalance + limBalance < 0)
|
||||||
|
{
|
||||||
|
RotateLeft(node);
|
||||||
|
}
|
||||||
|
else if (myBalance - limBalance > 0)
|
||||||
|
{
|
||||||
|
RotateRight(node);
|
||||||
|
}
|
||||||
|
node = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<K> KeysFrom(K first)
|
||||||
|
{
|
||||||
|
TreeNode node = Find(first);
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
yield return node.Key;
|
||||||
|
node = Next(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<K> KeysGreaterEqual(K first)
|
||||||
|
{
|
||||||
|
TreeNode node = FindFirstGE(first);
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
yield return node.Key;
|
||||||
|
node = Next(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(object key, object value) => Add((K)key, (V)value);
|
||||||
|
public void Remove(object key) => Remove((K)key);
|
||||||
|
public bool ContainsKey(object key) => ContainsKey((K)key);
|
||||||
|
public object this[object key] { get => this[(K)key]; set => this[(K)key] = (V)value; }
|
||||||
|
|
||||||
|
public IEnumerable<K> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
TreeNode node = First(headNode);
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
yield return node.Key;
|
||||||
|
node = Next(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public IEnumerable<V> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
TreeNode node = First(headNode);
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
yield return node.Value;
|
||||||
|
node = Next(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable IDict.Keys => Keys;
|
||||||
|
IEnumerable IDict.Values => Values;
|
||||||
|
|
||||||
|
class TreeNode
|
||||||
|
{
|
||||||
|
public BTree<K, V> Tree { get; }
|
||||||
|
|
||||||
|
public K Key { get; private set; }
|
||||||
|
public V Value { get; set; }
|
||||||
|
|
||||||
|
public TreeNode Parent { get; private set; }
|
||||||
|
|
||||||
|
public int Depth => 1 + Math.Max(LeftDepth, RightDepth);
|
||||||
|
|
||||||
|
//public int LeftDepth => Left != null ? Left.Depth : 0;
|
||||||
|
//public int RightDepth => Right != null ? Right.Depth : 0;
|
||||||
|
public int LeftDepth => leftDepth;
|
||||||
|
public int RightDepth => rightDepth;
|
||||||
|
|
||||||
|
public TreeNode Left { get; private set; }
|
||||||
|
public TreeNode Right { get; private set; }
|
||||||
|
|
||||||
|
private int leftDepth = 0;
|
||||||
|
private int rightDepth = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public TreeNode(BTree<K, V> tree, K key, V value)
|
||||||
|
{
|
||||||
|
Tree = tree;
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Attach(TreeNode parent)
|
||||||
|
{
|
||||||
|
if (!object.ReferenceEquals(this.Parent, null))
|
||||||
|
Detach();
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
Tree.headNode = this;
|
||||||
|
Parent = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int comp = Tree.Comparison(Key, parent.Key);
|
||||||
|
if (comp == 0)
|
||||||
|
throw new ArgumentException(String.Format("Key already exists in BTree ({0})",Key));
|
||||||
|
else if (comp < 0)
|
||||||
|
{
|
||||||
|
if (parent.Left != null)
|
||||||
|
throw new InvalidOperationException("Another Key is already attached to Parent (left)");
|
||||||
|
parent.Left = this;
|
||||||
|
parent.leftDepth = Depth + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent.Right != null)
|
||||||
|
throw new InvalidOperationException("Another Key is already attached to Parent (right)");
|
||||||
|
parent.Right = this;
|
||||||
|
parent.rightDepth = Depth + 1;
|
||||||
|
}
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Detach()
|
||||||
|
{
|
||||||
|
if (this.Parent != null)
|
||||||
|
{
|
||||||
|
if (this.Parent.Left == this)
|
||||||
|
{
|
||||||
|
this.Parent.Left = null;
|
||||||
|
this.Parent.leftDepth = 0;
|
||||||
|
}
|
||||||
|
else if (this.Parent.Right == this)
|
||||||
|
{
|
||||||
|
this.Parent.Right = null;
|
||||||
|
this.Parent.rightDepth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent = null;
|
||||||
|
} else if (Tree.headNode == this)
|
||||||
|
{
|
||||||
|
Tree.headNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("[TreeNode Key={0} Value={1}]",Key,Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BTree<K> : BTree<K,object>
|
||||||
|
{
|
||||||
|
public BTree()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public BTree(Comparison<K> comparison)
|
||||||
|
:base(comparison)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(K key)
|
||||||
|
{
|
||||||
|
Add(key, null);
|
||||||
|
}
|
||||||
|
public void TryAdd(K key)
|
||||||
|
{
|
||||||
|
TryAdd(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRange(IEnumerable<K> keys)
|
||||||
|
{
|
||||||
|
foreach (K key in keys)
|
||||||
|
{
|
||||||
|
Add(key, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void TryAddRange(IEnumerable<K> keys)
|
||||||
|
{
|
||||||
|
foreach (K key in keys)
|
||||||
|
{
|
||||||
|
TryAdd(key, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class BTreeValueList<K, V>
|
||||||
|
{
|
||||||
|
public bool Empty => bTree.Empty;
|
||||||
|
|
||||||
|
BTree<K, List<V>> bTree;
|
||||||
|
|
||||||
|
public BTreeValueList()
|
||||||
|
{
|
||||||
|
bTree = new BTree<K, List<V>>();
|
||||||
|
}
|
||||||
|
public BTreeValueList(Comparison<K> comparison)
|
||||||
|
{
|
||||||
|
bTree = new BTree<K, List<V>>(comparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<V> this[K key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TryGet(key,out IEnumerable<V> values))
|
||||||
|
{
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(K key, V value)
|
||||||
|
{
|
||||||
|
if (!TryAdd(key, value))
|
||||||
|
throw new ArgumentException("duplicate key");
|
||||||
|
}
|
||||||
|
public void Remove(K key, V value)
|
||||||
|
{
|
||||||
|
if (!TryRemove(key, value))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
public void Remove(K key)
|
||||||
|
{
|
||||||
|
if (!TryRemove(key))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAdd(K key,V value)
|
||||||
|
{
|
||||||
|
if (!bTree.TryGet(key, out List<V> values))
|
||||||
|
{
|
||||||
|
values = new List<V>();
|
||||||
|
bTree.Add(key, values);
|
||||||
|
}
|
||||||
|
values.Add(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool TryGet(K key,out IEnumerable<V> values)
|
||||||
|
{
|
||||||
|
if ((!bTree.TryGet(key, out List<V> v)) || (v.Count == 0))
|
||||||
|
{
|
||||||
|
values = v;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
values = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool TryRemove(K key)
|
||||||
|
{
|
||||||
|
return bTree.TryRemove(key);
|
||||||
|
}
|
||||||
|
public bool TryRemove(K key,V value)
|
||||||
|
{
|
||||||
|
if (bTree.TryGet(key, out List<V> values))
|
||||||
|
{
|
||||||
|
bool success = values.Remove(value);
|
||||||
|
if (values.Count == 0)
|
||||||
|
bTree.Remove(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool ContainsKey(K key)
|
||||||
|
{
|
||||||
|
if (!bTree.TryGet(key, out List<V> _values))
|
||||||
|
return false;
|
||||||
|
return _values.Count > 0;
|
||||||
|
}
|
||||||
|
public bool ContainsValue(V value)
|
||||||
|
{
|
||||||
|
foreach (List<V> _values in bTree.Values)
|
||||||
|
{
|
||||||
|
foreach (V v in _values)
|
||||||
|
if (v.Equals(value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count(K key)
|
||||||
|
{
|
||||||
|
if ((!bTree.TryGet(key, out List<V> _values)) || (_values.Count == 0))
|
||||||
|
return 0;
|
||||||
|
return _values.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
bTree.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public V Shift()
|
||||||
|
{
|
||||||
|
K k = bTree.First();
|
||||||
|
if (bTree.TryGet(k,out List<V> values))
|
||||||
|
{
|
||||||
|
V f = values[0];
|
||||||
|
Remove(k, f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
throw new Exception("Serious BUG");
|
||||||
|
}
|
||||||
|
public V Pop()
|
||||||
|
{
|
||||||
|
K k = bTree.Last();
|
||||||
|
if (bTree.TryGet(k, out List<V> values))
|
||||||
|
{
|
||||||
|
V f = values[values.Count-1];
|
||||||
|
Remove(k, f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
throw new Exception("Serious BUG");
|
||||||
|
}
|
||||||
|
|
||||||
|
public K First => bTree.First();
|
||||||
|
public K Last => bTree.Last();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<K> Keys => bTree.Keys;
|
||||||
|
public IEnumerable<V> Values => bTree.Values.SelectMany(vl => vl);
|
||||||
|
|
||||||
|
public IEnumerable<KeyValuePair<K, V>> GetKeyValuePairs()
|
||||||
|
{
|
||||||
|
foreach (K key in Keys)
|
||||||
|
{
|
||||||
|
List<V> lv = bTree[key];
|
||||||
|
foreach (V value in lv)
|
||||||
|
yield return new KeyValuePair<K, V>(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void AddRange(IEnumerable<KeyValuePair<K, V>> keyValuePairs)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<K, V> keyValuePair in keyValuePairs)
|
||||||
|
Add(keyValuePair.Key, keyValuePair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class BTreeValueSet<K, V>
|
||||||
|
{
|
||||||
|
public bool Empty => bTree.Empty;
|
||||||
|
|
||||||
|
BTree<K, HashSet<V>> bTree;
|
||||||
|
|
||||||
|
public BTreeValueSet()
|
||||||
|
{
|
||||||
|
bTree = new BTree<K, HashSet<V>>();
|
||||||
|
}
|
||||||
|
public BTreeValueSet(Comparison<K> comparison)
|
||||||
|
{
|
||||||
|
bTree = new BTree<K, HashSet<V>>(comparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<V> this[K key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TryGet(key,out IEnumerable<V> values))
|
||||||
|
{
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(K key, V value)
|
||||||
|
{
|
||||||
|
if (!TryAdd(key, value))
|
||||||
|
throw new ArgumentException("duplicate key");
|
||||||
|
}
|
||||||
|
public void Remove(K key, V value)
|
||||||
|
{
|
||||||
|
if (!TryRemove(key, value))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
public void Remove(K key)
|
||||||
|
{
|
||||||
|
if (!TryRemove(key))
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAdd(K key,V value)
|
||||||
|
{
|
||||||
|
if (!bTree.TryGet(key, out HashSet<V> values))
|
||||||
|
{
|
||||||
|
values = new HashSet<V>();
|
||||||
|
bTree.Add(key, values);
|
||||||
|
}
|
||||||
|
return values.Add(value);
|
||||||
|
}
|
||||||
|
public bool TryGet(K key,out IEnumerable<V> values)
|
||||||
|
{
|
||||||
|
if ((!bTree.TryGet(key, out HashSet<V> v)) || (v.Count == 0))
|
||||||
|
{
|
||||||
|
values = v;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
values = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool TryRemove(K key)
|
||||||
|
{
|
||||||
|
return bTree.TryRemove(key);
|
||||||
|
}
|
||||||
|
public bool TryRemove(K key,V value)
|
||||||
|
{
|
||||||
|
if (bTree.TryGet(key, out HashSet<V> values))
|
||||||
|
{
|
||||||
|
bool success = values.Remove(value);
|
||||||
|
if (values.Count == 0)
|
||||||
|
bTree.Remove(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool ContainsKey(K key)
|
||||||
|
{
|
||||||
|
if (!bTree.TryGet(key, out HashSet<V> _values))
|
||||||
|
return false;
|
||||||
|
return _values.Count > 0;
|
||||||
|
}
|
||||||
|
public bool ContainsValue(V value)
|
||||||
|
{
|
||||||
|
foreach (HashSet<V> _values in bTree.Values)
|
||||||
|
{
|
||||||
|
foreach (V v in _values)
|
||||||
|
if (v.Equals(value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count(K key)
|
||||||
|
{
|
||||||
|
if ((!bTree.TryGet(key, out HashSet<V> _values)) || (_values.Count == 0))
|
||||||
|
return 0;
|
||||||
|
return _values.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
bTree.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public K First => bTree.First();
|
||||||
|
public K Last => bTree.Last();
|
||||||
|
|
||||||
|
public IEnumerable<K> Keys => bTree.Keys;
|
||||||
|
public IEnumerable<V> Values => bTree.Values.SelectMany(vl => vl);
|
||||||
|
|
||||||
|
public IEnumerable<KeyValuePair<K, V>> GetKeyValuePairs()
|
||||||
|
{
|
||||||
|
foreach (K key in Keys)
|
||||||
|
{
|
||||||
|
HashSet<V> lv = bTree[key];
|
||||||
|
foreach (V value in lv)
|
||||||
|
yield return new KeyValuePair<K, V>(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void AddRange(IEnumerable<KeyValuePair<K, V>> keyValuePairs)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<K, V> keyValuePair in keyValuePairs)
|
||||||
|
Add(keyValuePair.Key, keyValuePair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class Cache<K,V>
|
||||||
|
{
|
||||||
|
public int Count => values.Count;
|
||||||
|
|
||||||
|
BTree<K, V> values;
|
||||||
|
BTree<K, LinkedListItem<K>> itemTree;
|
||||||
|
LinkedList<K> items = new LinkedList<K>();
|
||||||
|
|
||||||
|
int maxCacheSize = 4096;
|
||||||
|
|
||||||
|
public Cache()
|
||||||
|
{
|
||||||
|
values = new BTree<K, V>();
|
||||||
|
itemTree = new BTree<K, LinkedListItem<K>>();
|
||||||
|
}
|
||||||
|
public Cache(Comparison<K> compare)
|
||||||
|
{
|
||||||
|
values = new BTree<K, V>(compare);
|
||||||
|
itemTree = new BTree<K, LinkedListItem<K>>(compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxCacheSize
|
||||||
|
{
|
||||||
|
get => maxCacheSize;
|
||||||
|
set {
|
||||||
|
maxCacheSize = value;
|
||||||
|
while (maxCacheSize < values.Count)
|
||||||
|
{
|
||||||
|
Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
values.Clear();
|
||||||
|
itemTree.Clear();
|
||||||
|
items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet(K key,out V value)
|
||||||
|
{
|
||||||
|
return values.TryGet(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Forget()
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
Forget(items.FirstItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Forget(K key)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
if (itemTree.TryGet(key,out LinkedListItem<K> item))
|
||||||
|
Forget(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Forget(LinkedListItem<K> item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
values.Remove(item.Value);
|
||||||
|
itemTree.Remove(item.Value);
|
||||||
|
items.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Ensure(K key,V value)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
if (!itemTree.TryGet(key, out LinkedListItem<K> item))
|
||||||
|
{
|
||||||
|
item = new LinkedListItem<K>(key);
|
||||||
|
values.Add(key,value);
|
||||||
|
}
|
||||||
|
Ensure(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Ensure(LinkedListItem<K> item)
|
||||||
|
{
|
||||||
|
if (!item.IsLinked)
|
||||||
|
itemTree.Add(item.Value, item);
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public interface IDict<K,V> : IDict
|
||||||
|
{
|
||||||
|
V this[K key] { get; set; }
|
||||||
|
|
||||||
|
void Add(K key, V value);
|
||||||
|
void Remove(K key);
|
||||||
|
|
||||||
|
bool ContainsKey(K key);
|
||||||
|
|
||||||
|
new IEnumerable<K> Keys { get; }
|
||||||
|
new IEnumerable<V> Values { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDict
|
||||||
|
{
|
||||||
|
object this[object key] { get; set; }
|
||||||
|
|
||||||
|
void Add(object key, object value);
|
||||||
|
void Remove(object key);
|
||||||
|
|
||||||
|
bool ContainsKey(object key);
|
||||||
|
|
||||||
|
IEnumerable Keys { get; }
|
||||||
|
IEnumerable Values { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A backend Implementation of Double-Linked-List
|
||||||
|
**/
|
||||||
|
public class LinkedList<T>
|
||||||
|
{
|
||||||
|
public LinkedListItem<T> FirstItem => (head.Next != head) ? head.Next : null;
|
||||||
|
public LinkedListItem<T> LastItem => (head.Previous != head) ? head.Previous : null;
|
||||||
|
|
||||||
|
public LinkedListItem<T> Head => head;
|
||||||
|
|
||||||
|
LinkedListItem<T> head = new LinkedListItem<T>();
|
||||||
|
|
||||||
|
public LinkedList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
head = new LinkedListItem<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(LinkedListItem<T> item)
|
||||||
|
{
|
||||||
|
if (item.Previous != item)
|
||||||
|
Remove(item);
|
||||||
|
|
||||||
|
item.Next = head;
|
||||||
|
item.Previous = head.Previous;
|
||||||
|
item.Previous.Next = item;
|
||||||
|
item.Next.Previous = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(LinkedListItem<T> item)
|
||||||
|
{
|
||||||
|
item.Previous.Next = item.Next;
|
||||||
|
item.Next.Previous = item.Previous;
|
||||||
|
item.Next = item;
|
||||||
|
item.Previous = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class LinkedListItem<T>
|
||||||
|
{
|
||||||
|
public bool IsLinked => (Previous != this);
|
||||||
|
|
||||||
|
public LinkedListItem<T> Previous;
|
||||||
|
public LinkedListItem<T> Next;
|
||||||
|
|
||||||
|
public T Value;
|
||||||
|
|
||||||
|
public LinkedListItem()
|
||||||
|
{
|
||||||
|
Previous = this;
|
||||||
|
Next = this;
|
||||||
|
}
|
||||||
|
public LinkedListItem(T value)
|
||||||
|
:this()
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// /**
|
||||||
|
// * File: MappingBTree.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public delegate K KeyMapping<K, V>(V value);
|
||||||
|
|
||||||
|
public class MappingBTree<K,V> : IEnumerable<V>
|
||||||
|
{
|
||||||
|
public KeyMapping<K,V> KeyMapping { get; }
|
||||||
|
|
||||||
|
private BTree<K, V> tree;
|
||||||
|
|
||||||
|
public MappingBTree(KeyMapping<K, V> keyMapping)
|
||||||
|
{
|
||||||
|
KeyMapping = keyMapping;
|
||||||
|
tree = new BTree<K, V>();
|
||||||
|
}
|
||||||
|
public MappingBTree(KeyMapping<K, V> keyMapping,Comparison<K> comparison)
|
||||||
|
{
|
||||||
|
KeyMapping = keyMapping;
|
||||||
|
tree = new BTree<K, V>(comparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V this[K key]
|
||||||
|
{
|
||||||
|
get => tree[key];
|
||||||
|
set => tree[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
tree.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => tree.Count;
|
||||||
|
public void Add(V v) => tree.Add(KeyMapping(v), v);
|
||||||
|
public void TryAdd(V v) => tree.TryAdd(KeyMapping(v), v);
|
||||||
|
public void Remove(V v) => tree.Remove(KeyMapping(v));
|
||||||
|
public bool TryRemove(V v) => tree.TryRemove(KeyMapping(v));
|
||||||
|
|
||||||
|
public bool TryGet(K key, out V v) => tree.TryGet(key, out v);
|
||||||
|
public void RemoveKey(K key) => tree.Remove(key);
|
||||||
|
|
||||||
|
public bool Contains(V v) => tree.ContainsKey(KeyMapping(v));
|
||||||
|
public bool ContainsKey(K k) => tree.ContainsKey(k);
|
||||||
|
|
||||||
|
public IEnumerable<K> Keys => tree.Keys;
|
||||||
|
public IEnumerable<V> Values => tree.Values;
|
||||||
|
|
||||||
|
public V Next(V value) => tree[tree.Next(KeyMapping(value))];
|
||||||
|
public V Previous(V value) => tree[tree.Previous(KeyMapping(value))];
|
||||||
|
|
||||||
|
public IEnumerator<V> GetEnumerator() => Values.GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => Values.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class ObjectIdentityCache
|
||||||
|
{
|
||||||
|
Dictionary<object, object> objectsByIdentity = new Dictionary<object, object>();
|
||||||
|
Dictionary<object, object> identitiesByObject = new Dictionary<object, object>();
|
||||||
|
|
||||||
|
Func<object, object> IdGetter;
|
||||||
|
|
||||||
|
public ObjectIdentityCache(Func<object,object> idGetter)
|
||||||
|
{
|
||||||
|
if (idGetter == null)
|
||||||
|
throw new ArgumentNullException(nameof(idGetter));
|
||||||
|
|
||||||
|
IdGetter = idGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object this[object identity] => objectsByIdentity[identity];
|
||||||
|
|
||||||
|
public void Update(object o)
|
||||||
|
{
|
||||||
|
if (identitiesByObject.TryGetValue(o, out object knownObject) && (!object.Equals(o, knownObject)))
|
||||||
|
throw new ArgumentException("another instance already known with this identity", nameof(o));
|
||||||
|
|
||||||
|
if (!object.ReferenceEquals(null, knownObject))
|
||||||
|
{
|
||||||
|
object knownIdentity = identitiesByObject[knownObject];
|
||||||
|
object objectIdentity = IdGetter(knownObject);
|
||||||
|
|
||||||
|
if (!object.Equals(knownIdentity, objectIdentity))
|
||||||
|
{
|
||||||
|
objectsByIdentity.Remove(knownIdentity);
|
||||||
|
objectsByIdentity.Add(knownIdentity, knownObject);
|
||||||
|
identitiesByObject[knownObject] = objectIdentity;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
object objectIdentity = IdGetter(o);
|
||||||
|
|
||||||
|
objectsByIdentity.Add(objectIdentity, o);
|
||||||
|
identitiesByObject.Add(o, objectIdentity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveObject(object o)
|
||||||
|
{
|
||||||
|
if (identitiesByObject.TryGetValue(o, out object objectIdentity))
|
||||||
|
{
|
||||||
|
identitiesByObject.Remove(o);
|
||||||
|
objectsByIdentity.Remove(objectIdentity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void RemoveIdentity(object objectIdentity)
|
||||||
|
{
|
||||||
|
if (objectsByIdentity.TryGetValue(objectIdentity, out object o))
|
||||||
|
{
|
||||||
|
objectsByIdentity.Remove(objectIdentity);
|
||||||
|
identitiesByObject.Remove(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class WeakKeyDictionary<K, V> : IDictionary<K, V> where K : class
|
||||||
|
{
|
||||||
|
BTreeValueList<int, WeakKeyValuePair<K, V>> store = new BTreeValueList<int, WeakKeyValuePair<K, V>>();
|
||||||
|
|
||||||
|
public WeakKeyDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public V this[K key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TryGetValue(key, out V value))
|
||||||
|
return value;
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetValue(key, value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool KeyEquals(K key1,K key2)
|
||||||
|
{
|
||||||
|
return object.Equals(key1, key2);
|
||||||
|
}
|
||||||
|
public virtual int GetKeyHashcode(K key) => key.GetHashCode();
|
||||||
|
|
||||||
|
private WeakKeyValuePair<K, V> FindKeyValuePair(K key)
|
||||||
|
{
|
||||||
|
int keyHashCode = GetKeyHashcode(key);
|
||||||
|
|
||||||
|
if (store.ContainsKey(keyHashCode))
|
||||||
|
foreach (WeakKeyValuePair<K, V> weakKeyValuePair in store[keyHashCode].ToArray())
|
||||||
|
{
|
||||||
|
K weakKey = weakKeyValuePair.Key;
|
||||||
|
if (weakKey == null)
|
||||||
|
{
|
||||||
|
store.Remove(keyHashCode, weakKeyValuePair);
|
||||||
|
}
|
||||||
|
else if (KeyEquals(key, weakKey))
|
||||||
|
{
|
||||||
|
return weakKeyValuePair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(K key, out V value)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(key);
|
||||||
|
if (weakKeyValuePair != null)
|
||||||
|
{
|
||||||
|
value = weakKeyValuePair.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K GetKeyInstance(K key)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(key);
|
||||||
|
return weakKeyValuePair.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(K key, V value) => SetValue(key, value, false);
|
||||||
|
public bool SetValue(K key, V value, bool replace)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(key);
|
||||||
|
if (weakKeyValuePair == null)
|
||||||
|
{
|
||||||
|
int keyHashCode = GetKeyHashcode(key);
|
||||||
|
weakKeyValuePair = new WeakKeyValuePair<K, V>(keyHashCode, key, value);
|
||||||
|
store.Add(keyHashCode, weakKeyValuePair);
|
||||||
|
return false;
|
||||||
|
} else if (replace)
|
||||||
|
{
|
||||||
|
weakKeyValuePair.Value = value;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Key already present");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<K> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<K> keys = new List<K>();
|
||||||
|
|
||||||
|
foreach (WeakKeyValuePair<K,V> weakKeyValuePair in store.Values.ToArray())
|
||||||
|
{
|
||||||
|
if (weakKeyValuePair.IsStrong)
|
||||||
|
keys.Add(weakKeyValuePair.Key);
|
||||||
|
else
|
||||||
|
store.TryRemove(weakKeyValuePair.keyHashCode, weakKeyValuePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<V> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<V> values = new List<V>();
|
||||||
|
|
||||||
|
foreach (WeakKeyValuePair<K, V> weakKeyValuePair in store.Values)
|
||||||
|
{
|
||||||
|
if (weakKeyValuePair.IsStrong)
|
||||||
|
values.Add(weakKeyValuePair.Value);
|
||||||
|
else
|
||||||
|
store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => store.Keys.Count();
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public void Add(K key, V value)
|
||||||
|
{
|
||||||
|
SetValue(key, value);
|
||||||
|
}
|
||||||
|
public void Add(KeyValuePair<K, V> item) => Add(item.Key, item.Value);
|
||||||
|
|
||||||
|
public void Clear() => store.Clear();
|
||||||
|
public bool Contains(KeyValuePair<K, V> item)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(item.Key);
|
||||||
|
if ((weakKeyValuePair != null) && weakKeyValuePair.IsStrong)
|
||||||
|
{
|
||||||
|
return Object.Equals(item.Value, weakKeyValuePair.Value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool ContainsKey(K key)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(key);
|
||||||
|
return (weakKeyValuePair != null) && weakKeyValuePair.IsStrong;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
foreach (WeakKeyValuePair<K, V> weakKeyValuePair in store.Values)
|
||||||
|
{
|
||||||
|
if (weakKeyValuePair.IsStrong)
|
||||||
|
array[arrayIndex++] = new KeyValuePair<K, V>(weakKeyValuePair.Key,weakKeyValuePair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach (WeakKeyValuePair<K,V> weakKeyValuePair in store.Values)
|
||||||
|
{
|
||||||
|
if (weakKeyValuePair.IsStrong)
|
||||||
|
yield return new KeyValuePair<K, V>(weakKeyValuePair.Key,weakKeyValuePair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(K key)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(key);
|
||||||
|
if (weakKeyValuePair != null)
|
||||||
|
{
|
||||||
|
store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(KeyValuePair<K, V> item)
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<K, V> weakKeyValuePair = FindKeyValuePair(item.Key);
|
||||||
|
if ((weakKeyValuePair != null) && (object.Equals(item.Value, weakKeyValuePair.Value)))
|
||||||
|
{
|
||||||
|
store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
class WeakKeyValuePair<TK,TV> where TK: class
|
||||||
|
{
|
||||||
|
public readonly int keyHashCode;
|
||||||
|
|
||||||
|
WeakReference<TK> reference;
|
||||||
|
|
||||||
|
public TK Key => reference.TryGetTarget(out TK target) ? target : null;
|
||||||
|
public bool IsStrong => reference.TryGetTarget(out TK target);
|
||||||
|
|
||||||
|
public V Value { get; set; }
|
||||||
|
|
||||||
|
public WeakKeyValuePair(int keyHashCode,TK key,V value)
|
||||||
|
{
|
||||||
|
reference = new WeakReference<TK>(key);
|
||||||
|
this.keyHashCode = keyHashCode;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => keyHashCode;
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(this, obj))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
TK key = Key;
|
||||||
|
if ((key != null) && (obj is WeakKeyValuePair<TK,TV>))
|
||||||
|
{
|
||||||
|
WeakKeyValuePair<TK, TV> other = obj as WeakKeyValuePair<TK, TV>;
|
||||||
|
return Equals(key, other.Key);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// /**
|
||||||
|
// * File: WeakKeyReferenceDictionary.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class WeakKeyReferenceDictionary<K,V> : WeakKeyDictionary<K,V> where K:class
|
||||||
|
{
|
||||||
|
public WeakKeyReferenceDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool KeyEquals(K key1, K key2)
|
||||||
|
{
|
||||||
|
return Object.ReferenceEquals(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetKeyHashcode(K key)
|
||||||
|
{
|
||||||
|
return RuntimeHelpers.GetHashCode(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
// /**
|
||||||
|
// * File: WeakValueDictionary.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
namespace ln.collections
|
||||||
|
{
|
||||||
|
public class WeakValueDictionary<K,V> : IDictionary<K,V> where V:class
|
||||||
|
{
|
||||||
|
Dictionary<K, WeakReference<V>> keyValues = new Dictionary<K, WeakReference<V>>();
|
||||||
|
|
||||||
|
public WeakValueDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public V this[K key] {
|
||||||
|
get => TryGetValue(key, out V value) ? value : throw new KeyNotFoundException();
|
||||||
|
set => SetValue(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<K> Keys => keyValues.Keys;
|
||||||
|
public ICollection<V> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<V> values = new List<V>();
|
||||||
|
|
||||||
|
foreach (K key in Keys.ToArray())
|
||||||
|
{
|
||||||
|
WeakReference<V> weakValue = keyValues[key];
|
||||||
|
if (weakValue.TryGetTarget(out V value))
|
||||||
|
{
|
||||||
|
values.Add(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keyValues.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => keyValues.Count;
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public void Add(KeyValuePair<K, V> item) => Add(item.Key, item.Value);
|
||||||
|
public void Add(K key, V value)
|
||||||
|
{
|
||||||
|
if (keyValues.ContainsKey(key))
|
||||||
|
throw new ArgumentException();
|
||||||
|
|
||||||
|
SetValue(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() => keyValues.Clear();
|
||||||
|
public bool Contains(KeyValuePair<K, V> item)
|
||||||
|
{
|
||||||
|
if (TryGetValue(item.Key,out V value))
|
||||||
|
{
|
||||||
|
return Equals(item.Value, value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(K key) => keyValues.ContainsKey(key);
|
||||||
|
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<K,WeakReference<V>> kvp in keyValues)
|
||||||
|
{
|
||||||
|
if (kvp.Value.TryGetTarget(out V value))
|
||||||
|
array[arrayIndex++] = new KeyValuePair<K, V>(kvp.Key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<K, WeakReference<V>> kvp in keyValues)
|
||||||
|
{
|
||||||
|
if (kvp.Value.TryGetTarget(out V value))
|
||||||
|
yield return new KeyValuePair<K, V>(kvp.Key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(K key) => keyValues.Remove(key);
|
||||||
|
public bool Remove(KeyValuePair<K, V> item) => Remove(item.Key);
|
||||||
|
|
||||||
|
public bool TryGetValue(K key, out V value)
|
||||||
|
{
|
||||||
|
if (keyValues.TryGetValue(key, out WeakReference<V> weakValue))
|
||||||
|
{
|
||||||
|
if (weakValue.TryGetTarget(out value))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
keyValues.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool SetValue(K key,V value)
|
||||||
|
{
|
||||||
|
keyValues[key] = new WeakReference<V>(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Loading…
Reference in New Issue