diff --git a/sources/OpenMcdf/CFStorage.cs b/sources/OpenMcdf/CFStorage.cs index 8db809b9..0a238fc7 100644 --- a/sources/OpenMcdf/CFStorage.cs +++ b/sources/OpenMcdf/CFStorage.cs @@ -443,7 +443,7 @@ List subStorages return; }; - Children.VisitTreeNodes(internalAction); + Children.VisitTree(internalAction); if (recursive && subStorages.Count > 0) { diff --git a/sources/OpenMcdf/RBTree/IRBNode.cs b/sources/OpenMcdf/RBTree/IRBNode.cs new file mode 100644 index 00000000..4e141674 --- /dev/null +++ b/sources/OpenMcdf/RBTree/IRBNode.cs @@ -0,0 +1,33 @@ +using System; + +namespace RedBlackTree +{ + public enum Color + { + RED = 0, + BLACK = 1 + } + + /// + /// Red Black Node interface + /// + // TODO: Make concrete class in v3 + public interface IRBNode : IComparable + { + IRBNode Left { get; set; } + + IRBNode Right { get; set; } + + Color Color { get; set; } + + IRBNode Parent { get; set; } + + IRBNode Grandparent(); + + IRBNode Sibling(); + + IRBNode Uncle(); + + void AssignValueTo(IRBNode other); + } +} diff --git a/sources/OpenMcdf/RBTree/RBTree.cs b/sources/OpenMcdf/RBTree/RBTree.cs index 577fb69d..340ac882 100644 --- a/sources/OpenMcdf/RBTree/RBTree.cs +++ b/sources/OpenMcdf/RBTree/RBTree.cs @@ -1,13 +1,7 @@ -#define ASSERT - -using System; +using System; using System.Collections; using System.Collections.Generic; -using System.Linq; - -#if ASSERT using System.Diagnostics; -#endif // ------------------------------------------------------------- // This is a porting from java code, under MIT license of | @@ -18,6 +12,7 @@ namespace RedBlackTree { + // TODO: Remove in v3 public class RBTreeException : Exception { public RBTreeException(string msg) @@ -26,6 +21,7 @@ public RBTreeException(string msg) } } + // TODO: Use ArgumentException in v3 public class RBTreeDuplicatedItemException : RBTreeException { public RBTreeDuplicatedItemException(string msg) @@ -34,70 +30,13 @@ public RBTreeDuplicatedItemException(string msg) } } - public enum Color + // TODO: Make internal and seal in v3 + public partial class RBTree : IEnumerable { - RED = 0, - BLACK = 1 - } - - /// - /// Red Black Node interface - /// - public interface IRBNode : IComparable - { - IRBNode Left - { - get; - set; - } - - IRBNode Right - { - get; - set; - } - - Color Color - - { get; set; } - - IRBNode Parent { get; set; } + private static Color NodeColor(IRBNode n) => n == null ? Color.BLACK : n.Color; - IRBNode Grandparent(); - - IRBNode Sibling(); - // { - //#if ASSERT - // Debug.Assert(Parent != null); // Root node has no sibling - //#endif - // if (this == Parent.Left) - // return Parent.Right; - // else - // return Parent.Left; - // } - - IRBNode Uncle(); - // { - //#if ASSERT - // Debug.Assert(Parent != null); // Root node has no uncle - // Debug.Assert(Parent.Parent != null); // Children of root have no uncle - //#endif - // return Parent.Sibling(); - // } - // } - - void AssignValueTo(IRBNode other); - } - - public class RBTree : IEnumerable - { public IRBNode Root { get; set; } - private static Color NodeColor(IRBNode n) - { - return n == null ? Color.BLACK : n.Color; - } - public RBTree() { } @@ -114,20 +53,9 @@ private IRBNode LookupNode(IRBNode template) while (n != null) { int compResult = template.CompareTo(n); - if (compResult == 0) - { return n; - } - else if (compResult < 0) - { - n = n.Left; - } - else - { - //assert compResult > 0; - n = n.Right; - } + n = compResult < 0 ? n.Left : n.Right; } return n; @@ -137,15 +65,14 @@ public bool TryLookup(IRBNode template, out IRBNode val) { IRBNode n = LookupNode(template); - if (n == null) + switch (n) { - val = null; - return false; - } - else - { - val = n; - return true; + case null: + val = null; + return false; + default: + val = n; + return true; } } @@ -216,8 +143,6 @@ public void Insert(IRBNode newNode) if (compResult == 0) { throw new RBTreeDuplicatedItemException("RBNode " + newNode.ToString() + " already present in tree"); - //n.Value = value; - //return; } else if (compResult < 0) { @@ -234,7 +159,6 @@ public void Insert(IRBNode newNode) } else { - //assert compResult > 0; if (n.Right == null) { n.Right = insertedNode; @@ -252,14 +176,8 @@ public void Insert(IRBNode newNode) } InsertCase1(insertedNode); - - NodeInserted?.Invoke(insertedNode); - - //Trace.WriteLine(" "); - //Print(); } - //------------------------------------ private void InsertCase1(IRBNode n) { if (n.Parent == null) @@ -268,16 +186,14 @@ private void InsertCase1(IRBNode n) InsertCase2(n); } - //----------------------------------- private void InsertCase2(IRBNode n) { if (NodeColor(n.Parent) == Color.BLACK) return; // Tree is still valid - else - InsertCase3(n); + + InsertCase3(n); } - //---------------------------- private void InsertCase3(IRBNode n) { if (NodeColor(n.Uncle()) == Color.RED) @@ -293,7 +209,6 @@ private void InsertCase3(IRBNode n) } } - //---------------------------- private void InsertCase4(IRBNode n) { if (n == n.Parent.Right && n.Parent == n.Grandparent().Left) @@ -310,7 +225,6 @@ private void InsertCase4(IRBNode n) InsertCase5(n); } - //---------------------------- private void InsertCase5(IRBNode n) { n.Parent.Color = Color.BLACK; @@ -343,6 +257,7 @@ public void Delete(IRBNode template, out IRBNode deletedAlt) IRBNode n = LookupNode(template); if (n == null) return; // Key not found, do nothing + if (n.Left != null && n.Right != null) { // Copy key/value from predecessor and then delete it instead @@ -366,16 +281,14 @@ public void Delete(IRBNode template, out IRBNode deletedAlt) { Root.Color = Color.BLACK; } - - return; } private void DeleteCase1(IRBNode n) { if (n.Parent == null) return; - else - DeleteCase2(n); + + DeleteCase2(n); } private void DeleteCase2(IRBNode n) @@ -404,7 +317,9 @@ private void DeleteCase3(IRBNode n) DeleteCase1(n.Parent); } else + { DeleteCase4(n); + } } private void DeleteCase4(IRBNode n) @@ -418,7 +333,9 @@ private void DeleteCase4(IRBNode n) n.Parent.Color = Color.BLACK; } else + { DeleteCase5(n); + } } private void DeleteCase5(IRBNode n) @@ -465,141 +382,57 @@ private void DeleteCase6(IRBNode n) public void VisitTree(Action action) { - //IN Order visit - IRBNode walker = Root; + if (action is null) + throw new ArgumentNullException(nameof(action)); - if (walker != null) - DoVisitTree(action, walker); + // IN Order visit + if (Root != null) + VisitNode(action, Root); } - private void DoVisitTree(Action action, IRBNode walker) + private static void VisitNode(Action action, IRBNode node) { - if (walker.Left != null) - { - DoVisitTree(action, walker.Left); - } + if (node.Left != null) + VisitNode(action, node.Left); - action?.Invoke(walker); + action.Invoke(node); - if (walker.Right != null) - { - DoVisitTree(action, walker.Right); - } - } - - internal void VisitTreeNodes(Action action) - { - //IN Order visit - IRBNode walker = Root; - - if (walker != null) - DoVisitTreeNodes(action, walker); - } - - private void DoVisitTreeNodes(Action action, IRBNode walker) - { - if (walker.Left != null) - { - DoVisitTreeNodes(action, walker.Left); - } - - action?.Invoke(walker); - - if (walker.Right != null) - { - DoVisitTreeNodes(action, walker.Right); - } - } - - public class RBTreeEnumerator : IEnumerator - { - int position = -1; - private readonly List list = new(); - - internal RBTreeEnumerator(RBTree tree) - { - tree.VisitTreeNodes(item => list.Add(item)); - } - - public void Dispose() - { - } - - public IRBNode Current => list[position]; - - object IEnumerator.Current => list[position]; - - public bool MoveNext() - { - position++; - return position < list.Count; - } - - public void Reset() - { - position = -1; - } + if (node.Right != null) + VisitNode(action, node.Right); } public IEnumerator GetEnumerator() => new RBTreeEnumerator(this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private const int INDENT_STEP = 15; - public void Print() { - PrintHelper(Root, 0); + if (Root is null) + Trace.WriteLine(""); + else + Print(Root); } - private static void PrintHelper(IRBNode n, int indent) + private static void Print(IRBNode n) { - if (n == null) - { - Trace.WriteLine(""); - return; - } - if (n.Left != null) { - PrintHelper(n.Left, indent + INDENT_STEP); + Trace.Indent(); + Print(n.Left); + Trace.Unindent(); } - for (int i = 0; i < indent; i++) - Trace.Write(" "); if (n.Color == Color.BLACK) - Trace.WriteLine(" " + n.ToString() + " "); + Trace.WriteLine($" {n} "); else - Trace.WriteLine("<" + n.ToString() + ">"); + Trace.WriteLine($"<{n}>"); if (n.Right != null) { - PrintHelper(n.Right, indent + INDENT_STEP); + Trace.Indent(); + Print(n.Right); + Trace.Unindent(); } } - - internal void FireNodeOperation(IRBNode node, NodeOperation operation) - { - NodeOperation?.Invoke(node, operation); - } - - //internal void FireValueAssigned(RBNode node, V value) - //{ - // if (ValueAssignedAction != null) - // ValueAssignedAction(node, value); - //} - - internal event Action NodeInserted; - //internal event Action> NodeDeleted; - internal event Action NodeOperation; - } - - internal enum NodeOperation - { - LeftAssigned, - RightAssigned, - ColorAssigned, - ParentAssigned, - ValueAssigned } } diff --git a/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs b/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs new file mode 100644 index 00000000..7d23ec63 --- /dev/null +++ b/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Collections.Generic; + +namespace RedBlackTree +{ + public partial class RBTree + { + // TODO: Make internal in v3 (can seal in v2 since constructor is internal) + public sealed class RBTreeEnumerator : IEnumerator + { + private readonly List list = new(); + int position = -1; + + internal RBTreeEnumerator(RBTree tree) + { + tree.VisitTree(item => list.Add(item)); + } + + public void Dispose() + { + } + + public IRBNode Current => list[position]; + + object IEnumerator.Current => list[position]; + + public bool MoveNext() + { + position++; + return position < list.Count; + } + + public void Reset() + { + position = -1; + } + } + } +}