diff --git a/source/niknaks/containers.d b/source/niknaks/containers.d index dd255bc..6f36008 100644 --- a/source/niknaks/containers.d +++ b/source/niknaks/containers.d @@ -589,4 +589,342 @@ unittest // Destroy the map (such that it ends the sweeper destroy(map); +} + +// Given [0, 1, 5] +// and shift right at index 1 +// then 0 moves into 1's place +// 0's position is then filled with T.init + +public T[] shift(T)(T[] array, size_t position, bool rightwards = false, bool shrink = false) +{ + // Out of range + if(position >= array.length) + { + return array; + } + + // if rightwards + if(rightwards) + { + // nothing further left than index 0 + if(!position) + { + return array; + } + + for(size_t i = position; i > 0; i--) + { + array[i] = array[i-1]; + } + + // no shrink, then fill with T.init + if(!shrink) + { + array[0] = T.init; + } + // chomp left-hand side + else + { + array = array[1..$]; + } + } + + return array; +} + +// rightwards testung (no shrink) +unittest +{ + int[] numbas = [1, 5, 2]; + numbas = numbas.shift(1, true); + + // should now be [0, 1, 2] + writeln(numbas); + assert(numbas == [0, 1, 2]); + + numbas = [1, 5, 2]; + numbas = numbas.shift(0, true); + + // should now be [1, 5, 2] + writeln(numbas); + assert(numbas == [1, 5, 2]); + + numbas = [1, 5, 2]; + numbas = numbas.shift(2, true); + + // should now be [0, 1, 5] + writeln(numbas); + assert(numbas == [0, 1, 5]); + + numbas = [1, 2]; + numbas = numbas.shift(1, true); + + // should now be [0, 1] + writeln(numbas); + assert(numbas == [0, 1]); + + numbas = [1, 2]; + numbas = numbas.shift(0, true); + + // should now be [1, 2] + writeln(numbas); + assert(numbas == [1, 2]); + + numbas = []; + numbas = numbas.shift(0, true); + + // should now be [] + writeln(numbas); + assert(numbas == []); +} + +public T[] remove(T)(T[] array, size_t idx) +{ + // Return your array on this + if(!(idx < array.length)) + { + return array; + } + + return null; +} + +// TODO: make delegate kak +// public interface InclusionStratergy(T) +// { +// public bool include(T item); +// } + +// private class AlwaysStrat(T) : InclusionStratergy +// { +// public override bool include(T item) +// { +// return true; +// } +// } + +public template Always(T) +{ + public bool Always(Tree!(T) treeNode) + { + version(unittest) + { + import std.stdio : writeln; + writeln("Strat for: ", treeNode); + } + return true; + } +} + +public template Nothing(T) +{ + public void Nothing(Tree!(T) treeNode) + { + + } +} + +public template InclusionStratergy(T) +{ + public alias InclusionStratergy = bool delegate(Tree!(T) item); +} + +// Called prior to visitation? +public template TouchStratergy(T) +{ + public alias TouchStratergy = void delegate(Tree!(T) item); +} + +import std.string : format; + +// TODO: Technically this is a graph +public class Tree(T) +{ + private T value; + private Tree!(T)[] children; + + this(T value) + { + this.value = value; + } + + this() + { + + } + + public void setValue(T value) + { + this.value = value; + } + + public void appendNode(Tree!(T) node) + { + this.children ~= node; + } + + public bool removeNode(Tree!(T) node) + { + bool found = false; + for(size_t i = 0; i < this.children.length; i++) + { + found = this.children[i] == node; + if(found) + { + + } + } + + return true; + } + + public T[] dfs + ( + InclusionStratergy!(T) strat = toDelegate(&Always!(T)), + TouchStratergy!(T) touch = toDelegate(&Nothing!(T)) + ) + { + version(unittest) + { + writeln("dfs entry: ", this); + } + + T[] collected; + scope(exit) + { + version(unittest) + { + writeln("leaving node ", this, " with collected ", collected); + } + } + + // Touch + touch(this); // root[x] + + foreach(Tree!(T) child; this.children) // subtree[x], + { + if(strat(child)) + { + version(unittest) + { + writeln("dfs, strat good for child: ", child); + } + + // Visit + collected ~= child.dfs(strat, touch); + } + else + { + version(unittest) + { + writeln("dfs, strat ignored for child: ", child); + } + } + } + + // "Visit" + collected ~= this.value; + + + return collected; + } + + public override string toString() + { + return format("TreeNode [val: %s]", this.value); + } +} + + +version(unittest) +{ + import std.functional : toDelegate; + import std.stdio : writeln; + + private void DebugTouch(T)(Tree!(T) node) + { + writeln("Touching tree node ", node); + } +} + +unittest +{ + Tree!(string) treeOfStrings = new Tree!(string)("Top"); + + Tree!(string) subtree_1 = new Tree!(string)("1"); + Tree!(string) subtree_2 = new Tree!(string)("2"); + Tree!(string) subtree_3 = new Tree!(string)("3"); + + treeOfStrings.appendNode(subtree_1); + treeOfStrings.appendNode(subtree_2); + treeOfStrings.appendNode(subtree_3); + + + InclusionStratergy!(string) strat = toDelegate(&Always!(string)); + TouchStratergy!(string) touch = toDelegate(&DebugTouch!(string)); + + string[] result = treeOfStrings.dfs(strat, touch); + writeln("dfs: ", result); + + assert(result[0] == "1"); + assert(result[1] == "2"); + assert(result[2] == "3"); + assert(result[3] == "Top"); +} + +public class VisitationTree(T) : Tree!(T) +{ + private bool visisted; + + this(T value) + { + super(value); + } + + public T[] linearize() + { + return dfs(toDelegate(&_shouldVisit), toDelegate(&_touch)); + } + + private static bool _shouldVisit(Tree!(T) tnode) + { + VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode; + return vnode && !vnode.isVisited(); + } + + private static void _touch(Tree!(T) tnode) + { + VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode; + if(vnode) + { + vnode.mark(); + } + } + + private void mark() + { + this.visisted = true; + } + + private bool isVisited() + { + return this.visisted; + } +} + +unittest +{ + VisitationTree!(string) root = new VisitationTree!(string)("root"); + + VisitationTree!(string) thing = new VisitationTree!(string)("subtree"); + root.appendNode(thing); + thing.appendNode(root); + + string[] linearized = root.linearize(); + writeln(linearized); + + assert(linearized[0] == "subtree"); + assert(linearized[1] == "root"); + } \ No newline at end of file