From e6d2d52a8b98031e47c36c2af719b15e63da25b5 Mon Sep 17 00:00:00 2001 From: dadhi Date: Thu, 11 Jun 2020 11:17:55 +0300 Subject: [PATCH] wip: tree234 for #32 --- src/ImTools/ImTools.Experimental.cs | 188 +++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 5 deletions(-) diff --git a/src/ImTools/ImTools.Experimental.cs b/src/ImTools/ImTools.Experimental.cs index 4332d763..97be82b0 100644 --- a/src/ImTools/ImTools.Experimental.cs +++ b/src/ImTools/ImTools.Experimental.cs @@ -4,6 +4,185 @@ using System.Runtime.CompilerServices; using System.Threading; +namespace ImTools.Experimental.Tree234 +{ + /// Represents an empty map + public class ImMap + { + /// Empty tree to start with. + public static readonly ImMap Empty = new ImMap(); + protected ImMap() { } + } + + /// Wraps the stored data with "fixed" reference semantics - + /// when added to the tree it won't be changed or reconstructed in memory + public sealed class ImMapEntry : ImMap + { + /// The Key is basically the hash, or the Height for ImMapTree + public readonly int Key; + + /// The value - may be modified if you need a Ref{V} semantics + public V Value; + + /// Constructs the entry with the default value + public ImMapEntry(int key) => Key = key; + + /// Constructs the entry with the key and value + public ImMapEntry(int key, V value) + { + Key = key; + Value = value; + } + + /// Prints the key value pair + public override string ToString() => Key + ":" + Value; + } + + /// + public class ImMapLeafs2 : ImMap + { + /// Left entry + public readonly ImMapEntry Entry0; + + /// Right entry + public readonly ImMapEntry Entry1; + + public ImMapLeafs2(ImMapEntry entry0, ImMapEntry entry1) + { + Entry0 = entry0; + Entry1 = entry1; + } + } + + public sealed class ImMapLeafs3 : ImMapLeafs2 + { + /// + public readonly ImMapEntry Entry2; + + public ImMapLeafs3(ImMapEntry entry0, ImMapEntry entry1, ImMapEntry entry2) + : base(entry0, entry1) + { + Entry2 = entry2; + } + } + + public class ImMapBranch2 : ImMap + { + public readonly ImMapEntry Entry0; + + /// + public readonly ImMap Branch0; // can be Branchs | Entry | Leaf2 | Leaf3, if it is an Entry then other Tree cannot be an Entry + + /// + public readonly ImMap Branch1; + + public ImMapBranch2(ImMapEntry entry0, ImMap branch0, ImMap branch1) + { + Entry0 = entry0; + Branch0 = branch0; + Branch1 = branch1; + } + } + + public class ImMapBranch3 : ImMapBranch2 + { + public readonly ImMapEntry Entry1; + + /// + public readonly ImMap Branch2; + + public ImMapBranch3(ImMapEntry entry0, ImMap branch0, ImMap branch1, + ImMapEntry entry1, ImMap branch2) : base(entry0, branch0, branch1) + { + Entry1 = entry1; + Branch2 = branch2; + } + } + + /// ImMap methods + public static class ImMap + { + /// Adds or updates the value by key in the map, always returns a modified map + public static ImMap AddOrUpdateEntry(this ImMap map, ImMapEntry entry) + { + if (map == ImMap.Empty) + return entry; + + var key = entry.Key; + if (map is ImMapEntry leaf) + return key > leaf.Key ? new ImMapLeafs2(leaf, entry) + : key < leaf.Key ? new ImMapLeafs2(entry, leaf) + : (ImMap)entry; + + if (map is ImMapLeafs2 leaf2) + return key > leaf2.Entry1.Key ? new ImMapLeafs3(leaf2.Entry0, leaf2.Entry1, entry) + : key < leaf2.Entry0.Key ? new ImMapLeafs3(entry, leaf2.Entry0, leaf2.Entry1) + : key > leaf2.Entry0.Key && key < leaf2.Entry1.Key ? new ImMapLeafs3(leaf2.Entry0, entry, leaf2.Entry1) + : (ImMap)entry; + + // we need to split + if (map is ImMapLeafs3 leaf3) + { + // [1 3 5] and we are adding 7 + // => + // [3] + // [1] [5, 7] + + if (key > leaf3.Entry2.Key) + return new ImMapBranch2(leaf3.Entry1, + leaf3.Entry0, new ImMapLeafs2(leaf3.Entry2, entry)); + + if (key < leaf3.Entry0.Key) + return new ImMapBranch2(leaf3.Entry1, + new ImMapLeafs2(entry, leaf3.Entry0), leaf3.Entry2); + + if (key < leaf3.Entry2.Key && key > leaf3.Entry1.Key) + return new ImMapBranch2(leaf3.Entry1, + leaf3.Entry0, new ImMapLeafs2(entry, leaf3.Entry2)); + + if (key > leaf3.Entry0.Key && key < leaf3.Entry1.Key) + return new ImMapBranch2(leaf3.Entry1, + new ImMapLeafs2(leaf3.Entry0, entry), leaf3.Entry2); + + return entry; + } + + // todo: @incomplete add to branches + return map; + + // if (map is ImMapBranch branch) + // { + // if (key > branch.Entry.Key) + // // 5 10 + // // 10 => 5 11 + // // 11 + // return key > branch.RightEntry.Key + // ? new ImMapTree(branch.RightEntry, branch.Entry, entry) + // // 5 7 + // // 10 => 5 10 + // // 7 + // : key < branch.RightEntry.Key // rotate if right + // ? new ImMapTree(entry, branch.Entry, branch.RightEntry) + // : (ImMap)new ImMapBranch(branch.Entry, entry); + + // return key < branch.Entry.Key + // ? new ImMapTree(branch.Entry, entry, branch.RightEntry) + // : (ImMap)new ImMapBranch(entry, branch.RightEntry); + // } + + // var tree = (ImMapTree)map; + // return key == tree.Entry.Key + // ? new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight) + // : tree.AddOrUpdateLeftOrRight(key, entry); + } + + /// Adds or updates the value by key in the map, always returns a modified map + [MethodImpl((MethodImplOptions) 256)] + public static ImMap AddOrUpdate(this ImMap map, int key, V value) => + map.AddOrUpdateEntry(new ImMapEntry(key, value)); + } +} + namespace ImTools.Experimental { /// @@ -13,8 +192,6 @@ public class ImMap { /// Empty tree to start with. public static readonly ImMap Empty = new ImMap(); - - /// Prevents multiple creation of an empty tree protected ImMap() { } /// Height of the longest sub-tree/branch - 0 for the empty tree @@ -24,7 +201,8 @@ protected ImMap() { } public override string ToString() => "empty"; } - /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory + /// Wraps the stored data with "fixed" reference semantics - + /// when added to the tree it won't be changed or reconstructed in memory public sealed class ImMapEntry : ImMap { /// @@ -61,7 +239,7 @@ public sealed class ImMapBranch : ImMap /// Contains the once created data node public readonly ImMapEntry Entry; - /// Left sub-tree/branch, or empty. + /// Right sub-tree/branch, or empty. public ImMapEntry RightEntry; /// Constructor @@ -99,7 +277,7 @@ public sealed class ImMapTree : ImMap /// Left sub-tree/branch, or empty. public ImMap Left; - /// Right sub-tree/branch, or empty.md + /// Right sub-tree/branch, or empty public ImMap Right; internal ImMapTree(ImMapEntry entry, ImMap left, ImMap right, int height)