commit f8ccd0d505c7bf310efd33e3dc8ea7e04f6516fc Author: U-WALDRENNACH\haraldwolff Date: Wed Nov 18 00:09:21 2020 +0100 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf793ed --- /dev/null +++ b/.gitignore @@ -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 diff --git a/BTree.cs b/BTree.cs new file mode 100644 index 0000000..e15fdab --- /dev/null +++ b/BTree.cs @@ -0,0 +1,679 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTree : IDict + { + public Comparison 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))) + { + Comparison = (K x, K y) => x.GetHashCode() - y.GetHashCode(); + } + else + { + Comparison = (K x, K y) => ((IComparable)x).CompareTo(y); + } + } + public BTree(Comparison 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 KeysFrom(K first) + { + TreeNode node = Find(first); + while (node != null) + { + yield return node.Key; + node = Next(node); + } + } + + public IEnumerable 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 Keys + { + get + { + TreeNode node = First(headNode); + while (node != null) + { + yield return node.Key; + node = Next(node); + } + } + } + public IEnumerable 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 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 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 : BTree + { + public BTree() + { + } + public BTree(Comparison comparison) + :base(comparison) + { + } + + public void Add(K key) + { + Add(key, null); + } + public void TryAdd(K key) + { + TryAdd(key, null); + } + + public void AddRange(IEnumerable keys) + { + foreach (K key in keys) + { + Add(key, null); + } + } + public void TryAddRange(IEnumerable keys) + { + foreach (K key in keys) + { + TryAdd(key, null); + } + } + } +} diff --git a/BTreeValueList.cs b/BTreeValueList.cs new file mode 100644 index 0000000..e012cab --- /dev/null +++ b/BTreeValueList.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTreeValueList + { + public bool Empty => bTree.Empty; + + BTree> bTree; + + public BTreeValueList() + { + bTree = new BTree>(); + } + public BTreeValueList(Comparison comparison) + { + bTree = new BTree>(comparison); + } + + public IEnumerable this[K key] + { + get + { + if (TryGet(key,out IEnumerable 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 values)) + { + values = new List(); + bTree.Add(key, values); + } + values.Add(value); + return true; + } + public bool TryGet(K key,out IEnumerable values) + { + if ((!bTree.TryGet(key, out List 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 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 _values)) + return false; + return _values.Count > 0; + } + public bool ContainsValue(V value) + { + foreach (List _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 _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 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 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 Keys => bTree.Keys; + public IEnumerable Values => bTree.Values.SelectMany(vl => vl); + + public IEnumerable> GetKeyValuePairs() + { + foreach (K key in Keys) + { + List lv = bTree[key]; + foreach (V value in lv) + yield return new KeyValuePair(key, value); + } + } + public void AddRange(IEnumerable> keyValuePairs) + { + foreach (KeyValuePair keyValuePair in keyValuePairs) + Add(keyValuePair.Key, keyValuePair.Value); + } + } +} diff --git a/BTreeValueSet.cs b/BTreeValueSet.cs new file mode 100644 index 0000000..4140f13 --- /dev/null +++ b/BTreeValueSet.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTreeValueSet + { + public bool Empty => bTree.Empty; + + BTree> bTree; + + public BTreeValueSet() + { + bTree = new BTree>(); + } + public BTreeValueSet(Comparison comparison) + { + bTree = new BTree>(comparison); + } + + public IEnumerable this[K key] + { + get + { + if (TryGet(key,out IEnumerable 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 values)) + { + values = new HashSet(); + bTree.Add(key, values); + } + return values.Add(value); + } + public bool TryGet(K key,out IEnumerable values) + { + if ((!bTree.TryGet(key, out HashSet 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 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 _values)) + return false; + return _values.Count > 0; + } + public bool ContainsValue(V value) + { + foreach (HashSet _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 _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 Keys => bTree.Keys; + public IEnumerable Values => bTree.Values.SelectMany(vl => vl); + + public IEnumerable> GetKeyValuePairs() + { + foreach (K key in Keys) + { + HashSet lv = bTree[key]; + foreach (V value in lv) + yield return new KeyValuePair(key, value); + } + } + public void AddRange(IEnumerable> keyValuePairs) + { + foreach (KeyValuePair keyValuePair in keyValuePairs) + Add(keyValuePair.Key, keyValuePair.Value); + } + + } +} diff --git a/Cache.cs b/Cache.cs new file mode 100644 index 0000000..685626a --- /dev/null +++ b/Cache.cs @@ -0,0 +1,96 @@ +using System; +namespace ln.collections +{ + public class Cache + { + public int Count => values.Count; + + BTree values; + BTree> itemTree; + LinkedList items = new LinkedList(); + + int maxCacheSize = 4096; + + public Cache() + { + values = new BTree(); + itemTree = new BTree>(); + } + public Cache(Comparison compare) + { + values = new BTree(compare); + itemTree = new BTree>(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 item)) + Forget(item); + } + } + + private void Forget(LinkedListItem 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 item)) + { + item = new LinkedListItem(key); + values.Add(key,value); + } + Ensure(item); + } + } + + private void Ensure(LinkedListItem item) + { + if (!item.IsLinked) + itemTree.Add(item.Value, item); + items.Add(item); + } + + } +} diff --git a/IDict.cs b/IDict.cs new file mode 100644 index 0000000..91a328c --- /dev/null +++ b/IDict.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Collections; +namespace ln.collections +{ + public interface IDict : IDict + { + V this[K key] { get; set; } + + void Add(K key, V value); + void Remove(K key); + + bool ContainsKey(K key); + + new IEnumerable Keys { get; } + new IEnumerable 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; } + } +} diff --git a/LinkedList.cs b/LinkedList.cs new file mode 100644 index 0000000..ab026b6 --- /dev/null +++ b/LinkedList.cs @@ -0,0 +1,44 @@ +namespace ln.collections +{ + /** + * A backend Implementation of Double-Linked-List + **/ + public class LinkedList + { + public LinkedListItem FirstItem => (head.Next != head) ? head.Next : null; + public LinkedListItem LastItem => (head.Previous != head) ? head.Previous : null; + + public LinkedListItem Head => head; + + LinkedListItem head = new LinkedListItem(); + + public LinkedList() + { + } + + public void Clear() + { + head = new LinkedListItem(); + } + + public void Add(LinkedListItem 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 item) + { + item.Previous.Next = item.Next; + item.Next.Previous = item.Previous; + item.Next = item; + item.Previous = item; + } + + } +} diff --git a/LinkedListItem.cs b/LinkedListItem.cs new file mode 100644 index 0000000..2c9ac1b --- /dev/null +++ b/LinkedListItem.cs @@ -0,0 +1,25 @@ +using System; +namespace ln.collections +{ + public class LinkedListItem + { + public bool IsLinked => (Previous != this); + + public LinkedListItem Previous; + public LinkedListItem Next; + + public T Value; + + public LinkedListItem() + { + Previous = this; + Next = this; + } + public LinkedListItem(T value) + :this() + { + Value = value; + } + + } +} diff --git a/MappingBTree.cs b/MappingBTree.cs new file mode 100644 index 0000000..03c3714 --- /dev/null +++ b/MappingBTree.cs @@ -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(V value); + + public class MappingBTree : IEnumerable + { + public KeyMapping KeyMapping { get; } + + private BTree tree; + + public MappingBTree(KeyMapping keyMapping) + { + KeyMapping = keyMapping; + tree = new BTree(); + } + public MappingBTree(KeyMapping keyMapping,Comparison comparison) + { + KeyMapping = keyMapping; + tree = new BTree(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 Keys => tree.Keys; + public IEnumerable 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 GetEnumerator() => Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Values.GetEnumerator(); + } +} diff --git a/ObjectIdentityCache.cs b/ObjectIdentityCache.cs new file mode 100644 index 0000000..93ece34 --- /dev/null +++ b/ObjectIdentityCache.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ln.collections +{ + public class ObjectIdentityCache + { + Dictionary objectsByIdentity = new Dictionary(); + Dictionary identitiesByObject = new Dictionary(); + + Func IdGetter; + + public ObjectIdentityCache(Func 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); + } + } + + + } +} diff --git a/WeakKeyDictionary.cs b/WeakKeyDictionary.cs new file mode 100644 index 0000000..cb2a3ef --- /dev/null +++ b/WeakKeyDictionary.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ln.collections +{ + public class WeakKeyDictionary : IDictionary where K : class + { + BTreeValueList> store = new BTreeValueList>(); + + 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 FindKeyValuePair(K key) + { + int keyHashCode = GetKeyHashcode(key); + + if (store.ContainsKey(keyHashCode)) + foreach (WeakKeyValuePair 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 weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair != null) + { + value = weakKeyValuePair.Value; + return true; + } + value = default(V); + return false; + } + + public K GetKeyInstance(K key) + { + WeakKeyValuePair 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 weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair == null) + { + int keyHashCode = GetKeyHashcode(key); + weakKeyValuePair = new WeakKeyValuePair(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 Keys + { + get + { + List keys = new List(); + + foreach (WeakKeyValuePair weakKeyValuePair in store.Values.ToArray()) + { + if (weakKeyValuePair.IsStrong) + keys.Add(weakKeyValuePair.Key); + else + store.TryRemove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + } + + return keys; + } + } + + public ICollection Values + { + get + { + List values = new List(); + + foreach (WeakKeyValuePair 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 item) => Add(item.Key, item.Value); + + public void Clear() => store.Clear(); + public bool Contains(KeyValuePair item) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(item.Key); + if ((weakKeyValuePair != null) && weakKeyValuePair.IsStrong) + { + return Object.Equals(item.Value, weakKeyValuePair.Value); + } + return false; + } + public bool ContainsKey(K key) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + return (weakKeyValuePair != null) && weakKeyValuePair.IsStrong; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (WeakKeyValuePair weakKeyValuePair in store.Values) + { + if (weakKeyValuePair.IsStrong) + array[arrayIndex++] = new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); + } + } + + public IEnumerator> GetEnumerator() + { + foreach (WeakKeyValuePair weakKeyValuePair in store.Values) + { + if (weakKeyValuePair.IsStrong) + yield return new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); + } + } + + public bool Remove(K key) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair != null) + { + store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + return true; + } + return false; + } + + public bool Remove(KeyValuePair item) + { + WeakKeyValuePair 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 where TK: class + { + public readonly int keyHashCode; + + WeakReference 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(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)) + { + WeakKeyValuePair other = obj as WeakKeyValuePair; + return Equals(key, other.Key); + } + return false; + } + } + + + } +} diff --git a/WeakKeyReferenceDictionary.cs b/WeakKeyReferenceDictionary.cs new file mode 100644 index 0000000..7cda1c8 --- /dev/null +++ b/WeakKeyReferenceDictionary.cs @@ -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 : WeakKeyDictionary 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); + } + + } +} diff --git a/WeakValueDictionary.cs b/WeakValueDictionary.cs new file mode 100644 index 0000000..52cdd15 --- /dev/null +++ b/WeakValueDictionary.cs @@ -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 : IDictionary where V:class + { + Dictionary> keyValues = new Dictionary>(); + + public WeakValueDictionary() + { + } + + public V this[K key] { + get => TryGetValue(key, out V value) ? value : throw new KeyNotFoundException(); + set => SetValue(key, value); + } + + public ICollection Keys => keyValues.Keys; + public ICollection Values + { + get + { + List values = new List(); + + foreach (K key in Keys.ToArray()) + { + WeakReference 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 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 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[] array, int arrayIndex) + { + foreach (KeyValuePair> kvp in keyValues) + { + if (kvp.Value.TryGetTarget(out V value)) + array[arrayIndex++] = new KeyValuePair(kvp.Key, value); + } + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair> kvp in keyValues) + { + if (kvp.Value.TryGetTarget(out V value)) + yield return new KeyValuePair(kvp.Key, value); + } + } + + public bool Remove(K key) => keyValues.Remove(key); + public bool Remove(KeyValuePair item) => Remove(item.Key); + + public bool TryGetValue(K key, out V value) + { + if (keyValues.TryGetValue(key, out WeakReference 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(value); + return true; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/ln.collections.csproj b/ln.collections.csproj new file mode 100644 index 0000000..5f98043 --- /dev/null +++ b/ln.collections.csproj @@ -0,0 +1,10 @@ + + + + netcoreapp2.1 + + + + + +