/******************************************************************************* A Red Black Tree container Authors: Bill Baxter, ArcLib team, see AUTHORS file Maintainer: Bill Baxter License: zlib/libpng license: $(LICENSE) Copyright: Bill Baxter, ArcLib team Description: A Red Black Tree container based on the work found here. Examples: --------------------- import arc.templates.redblacktree; int main() { auto RedBlackTree!(int) tree = new RedBlackTree!(int); for (int i = 0; i < 10; i++) tree.insert(i); //tree.clear(); auto RedBlackTree!(int) t2 = new RedBlackTree!(int)(tree); foreach(RedBlackTree!(int).node_type n; t2) writefln(n.value); writefln(tree); RedBlackTree!(int).node_type n = tree.search(8); if (n !is null) writefln("found value ", n.value); if (n is null) writefln("null"); if (99 in tree) { writefln("99 is in tree"); } writefln("hi"); if (tree.is_valid) writefln("Tree is valid"); return 0; } --------------------- *******************************************************************************/ module redblacktree; import std.stdio; import std.string : format; alias format string_format; class IndexException : Exception { this() { super(""); } this(char[] descrip) { super(descrip); } } struct _GenericLess(T) { static if( is(T==class)) { // Pass normally (by reference) for class types static bool opCall(T a, T b) { return a=2) { // Chop off the last ', ' middle = middle[0..$-2]; } return middle; } /// insert value into the tree without duplication. /// Return true if it was actually added, /// or false if the item was already present. bool insert( T value ) { bool is_dupe = false; root = insert_r(root, value, is_dupe); root.red = 0; if (!is_dupe) _size++; return !is_dupe; } /// insert value into the tree without duplication. /// Return true if it was actually added, /// or false if the item was already present. void insert_unconditional( T value ) { bool is_dupe = false; root = insert_r(root, value, is_dupe, false); root.red = 0; assert(!is_dupe); _size++; } /// search for a key in the tree and return it if found, null otherwise Node search(T value, Node node = null) { if(node is null) node = root; while(node !is null) { if (_value_eq(value,node.value)) { break; } int nResult = (_value_less(value, node.value) ? 0 : 1); node = node.link[nResult]; } return node; } /// search for the minimal element in the tree, return it if found, /// or null if the tree is empty Node search_min(Node node = null) { if (node is null) node = root; while(node !is null) { if (node.link[0] is null) { break; } node = node.link[0]; } return node; } /// search for the maximal element in the tree, return it if found, /// or null if the tree is empty Node search_max(Node node = null) { if (node is null) node = root; while(node !is null) { if (node.link[1] is null) { break; } node = node.link[1]; } return node; } /// remove all values from the tree void clear() { destroy_r(root); _size = 0; root = null; } /// returns whether the binary tree is valid or not int is_valid() { return assertNode(root); } /// return tree nodes size int size() { return _size; } /// true if empty bool empty() { return _size == 0; } /// merge values from tree into this one (union, but that's a keyword) void merge(RedBlackTree tree) { insertContents(tree.root); } /// remove another tree's values from this tree void intersect(RedBlackTree tree) { removeContents(tree.root); } /// make this tree a duplicate of another void duplicate(RedBlackTree tree) { // TODO: we can create a copy faster by skipping the inserts and just // copying the structure node-by-node. clear(); insertContents(tree.root); _size = tree._size; } /// make a duplicate of this tree, D-style RedBlackTree dup() { return new RedBlackTree(this); } // foreach iterator forwards on Nodes int opApply(int delegate(ref Node) dg) { return _applyNode!(LEFT,RIGHT)(root, dg); } // foreach iterator forwards on values int opApply(int delegate(ref T) dg) { return _applyValue!(LEFT,RIGHT)(root, dg); } // foreach iterator forwards on Nodes int opApplyReverse(int delegate(ref Node) dg) { return _applyNode!(RIGHT,LEFT)(root, dg); } // foreach iterator forwards on values int opApplyReverse(int delegate(ref T) dg) { return _applyValue!(RIGHT,LEFT)(root, dg); } // Return whether value is in tree 'if (4 in tree)' /// Return its node if found or null otherwise. Node opIn_r(T value) { Node n = search(value); return n; } /// removes the given node /// Returns true if a node was removed, false otherwise (node wasn't present) bool remove(T value) { return _pop!(_pop_get_dir_less, _pop_is_found_eq)(value); } /// Removes the given node and returns it. /// If the value was not in the tree, returns null. Node pop(T value) { Node ret; _pop!(_pop_get_dir_less, _pop_is_found_eq)(value, &ret); return ret; } /// Removes minimum node and returns it. /// If the tree was empty, returns null. /// (synonymous with pop_min) Node pop() { return pop_min(); } /// Removes the minimal node and returns it. /// If the tree was empty, returns null. Node pop_min() { Node ret; _pop!(_pop_get_dir_min,_pop_is_found_min)(T.init, &ret); return ret; } /// Removes the maximum node and returns it. /// If the tree was empty, returns null. Node pop_max() { Node ret; _pop!(_pop_get_dir_max,_pop_is_found_max)(T.init, &ret); return ret; } private: bool _pop_get_dir_less(Node q, T value) { return _value_less(q.value, value); } bool _pop_is_found_eq(Node q, T value) { return _value_eq(q.value, value); } bool _pop_get_dir_min(Node q, T value) { return 0; } bool _pop_is_found_min(Node q, T value) { return q.link[0] is null; } bool _pop_get_dir_max(Node q, T value) { return 1; } bool _pop_is_found_max(Node q, T value) { return q.link[1] is null; } /// Removes the given node. /// If the value was in the tree, returns true and sets '*removed' to point to it. /// If the value was not in the tree, returns false and sets '*removed' to null. bool _pop(alias get_dir, alias is_found)(T value, Node* removed = null) { if (removed !is null) *removed = null; if (root is null) { return false; } Node head = new Node(value); /* False tree root */ Node q, p, g; /* Helpers */ Node f = null; /* Found item */ int dir = RIGHT; /* Set up helpers */ q = head; g = p = null; q.link[RIGHT] = root; /* Search and push a red down */ while ( q.link[dir] !is null ) { int last = dir; /* Update helpers */ g = p, p = q; q = q.link[dir]; //dir = _value_less(q.value, value); dir = get_dir(q, value); /* Save found node */ //if ( _value_eq(q.value, value) ) { if ( is_found(q, value) ) { f = q; } /* Push the red node down */ if ( !isRed(q) && !isRed(q.link[dir]) ) { if ( isRed(q.link[!dir]) ) { p = p.link[last] = singleRotation ( q, dir ); } else if ( !isRed ( q.link[!dir] ) ) { Node s = p.link[!last]; if ( s !is null ) { if ( !isRed ( s.link[!last] ) && !isRed ( s.link[last] ) ) { /* Color flip */ p.red = 0; s.red = 1; q.red = 1; } else { int dir2 = g.link[RIGHT] is p; if ( isRed ( s.link[last] ) ) { g.link[dir2] = doubleRotation ( p, last ); } else if ( isRed ( s.link[!last] ) ) { g.link[dir2] = singleRotation ( p, last ); } /* Ensure correct coloring */ q.red = g.link[dir2].red = 1; g.link[dir2].link[0].red = 0; g.link[dir2].link[1].red = 0; } } } } } /* Replace and remove if found */ if ( f !is null ) { if (removed is null) { f.value = q.value; p.link[p.link[1] is q] = q.link[q.link[0] is null]; delete q; } else { T tmp = f.value; f.value = q.value; q.value = tmp; p.link[p.link[1] is q] = q.link[q.link[0] is null]; *removed = q; } _size--; } /* Update root and make it black */ root = head.link[1]; if ( root !is null ) root.red = 0; delete head; return f !is null; } bool _value_less(T a, T b) { return Compare(a,b); } bool _value_eq(T a, T b) { return !Compare(a,b) && !Compare(b,a); } // copy from leafSrc into leafDst in order inserting one-by-one void insertContents(Node leafSrc) { if (leafSrc !is null) { insertContents(leafSrc.link[LEFT]); insert(leafSrc.value); insertContents(leafSrc.link[RIGHT]); } } // Remove all the values in leafSrc one-by-one void removeContents(Node leafSrc) { if (leafSrc !is null) { removeContents(leafSrc.link[LEFT]); remove(leafSrc.value); removeContents(leafSrc.link[RIGHT]); } } // iterate tree forwards or backwards int _applyNode(int pre=LEFT,int post=RIGHT)(Node node, int delegate(inout Node) dg) { int result = 0; while(node !is null) { result = _applyNode!(pre,post)(node.link[pre], dg); if (result) return result; result = dg(node); if (result) return result; node = node.link[post]; } return result; } // iterate tree forwards or backwards -- values int _applyValue(int pre=LEFT, int post=RIGHT)(Node node, int delegate(inout T) dg) { int result = 0; while(node !is null) { result = _applyValue!(pre,post)(node.link[pre], dg); if (result) return result; result = dg(node.value); if (result) return result; node = node.link[post]; } return result; } // insert recursive Node insert_r (Node node, T value, ref bool is_dupe, bool unique=true ) { if ( node is null ) { node = new Node( value ); } else if ( !unique || !_value_eq(value, node.value) ) { int dir = _value_less(node.value, value); node.link[dir] = insert_r ( node.link[dir], value, is_dupe, unique ); if ( isRed ( node.link[dir] ) ) { if ( isRed ( node.link[!dir] ) ) { /* Case 1 */ node.red = 1; node.link[LEFT].red = 0; node.link[RIGHT].red = 0; } else { /* Cases 2 & 3 */ if ( isRed ( node.link[dir].link[dir] ) ) node = singleRotation ( node, !dir ); else if ( isRed ( node.link[dir].link[!dir] ) ) node = doubleRotation ( node, !dir ); } } } else { is_dupe = true; } return node; } // recursive print routine void print_r ( Node node ) { if ( node !is null ) { print_r ( node.link[LEFT] ); writefln (node.value); print_r ( node.link[RIGHT] ); } } // recursive to_string routine char[] to_string_r ( Node node ) { char[] ret; if ( node !is null ) { ret ~= to_string_r ( node.link[LEFT] ); ret ~= string_format(node.value, ", "); ret ~= to_string_r ( node.link[RIGHT] ); } return ret; } // recursive destruction of all tree elements void destroy_r(Node node) { if (node !is null) { destroy_r(node.link[LEFT]); destroy_r(node.link[RIGHT]); delete node; node = null; } } // assert node -- check that node is valid in the tree int assertNode ( Node node ) { int lh, rh; if ( node is null ) return 1; else { Node ln = node.link[LEFT]; Node rn = node.link[RIGHT]; /* Consecutive red links */ if ( isRed ( node ) ) { if ( isRed ( ln ) || isRed ( rn ) ) { writefln ( "Red violation" ); return 0; } } lh = assertNode ( ln ); rh = assertNode ( rn ); /* Invalid binary search tree */ if ( ( ln !is null && !_value_less(ln.value,node.value)) || ( rn !is null && !_value_less(node.value, rn.value))) { writefln ( "Binary tree violation" ); return 0; } /* Black height mismatch */ if ( lh != 0 && rh != 0 && lh != rh ) { writefln ( "Black violation" ); return 0; } /* Only count black links */ if ( lh != 0 && rh != 0 ) return isRed ( node ) ? lh : lh + 1; else return 0; } } // single rotation Node singleRotation (Node node, int dir ) { Node save = node.link[!dir]; node.link[!dir] = save.link[dir]; save.link[dir] = node; node.red = 1; save.red = 0; return save; } // double rotation Node doubleRotation (Node node, int dir ) { node.link[!dir] = singleRotation ( node.link[!dir], !dir ); return singleRotation ( node, dir ); } // node is red? or not int isRed ( Node node ) { return node !is null && node.red == 1; } enum { LEFT=0, RIGHT=1 } // a tree node static class Node { this(T value) { this.value = value; red = 1; // 1 is red, 0 is black link[LEFT] = null; link[RIGHT] = null; } T value; private: int red; Node link[2]; } // root node of our tree Node root; // number of items in the tree int _size; } //============================================================================ unittest{ { auto tree = new RedBlackTree!(int); static assert( is(int==tree.value_type) ); static assert( is(int==tree.key_type) ); alias typeof(tree) Tree; alias Tree.node_type Node; assert(tree.empty); assert(tree.search_min() is null); assert(tree.search_max() is null); for (int i = 0; i < 10; i++) { bool inserted = tree.insert(i); assert(inserted == true); assert(!tree.empty); } assert(tree.size==10); // Should ignore repeats for (int i = 0; i < 10; i++) { bool inserted = tree.insert(i); assert(inserted == false); assert(!tree.empty); } assert(tree.size==10); // Test opIn assert(!(99 in tree)); assert(5 in tree); //tree.clear(); { int i=0; foreach(Node n; tree) { assert(n.value == i); i++; } foreach_reverse(Node n; tree) { i--; assert(n.value == i); } } bool[int] aacheck; foreach(Node n; tree) aacheck[n.value]=true; foreach(k,v; aacheck) { assert(k in tree); } foreach(Node n; tree) { assert(n.value in tree); } foreach(int v; tree) { assert(v in tree); } foreach(tree.value_type v; tree) { assert(v in tree); } foreach_reverse(int v; tree) { assert(v in tree); } foreach_reverse(Node n; tree) { assert(n.value in tree); } Tree t2 = new Tree(tree); // creates a copy assert(aacheck.length == t2.size); // Make sure contents are the same foreach(k,v; aacheck) { assert(k in t2); } foreach(Node n; t2) { assert(n.value in t2); } foreach(int v; t2) { assert(v in t2); } foreach(tree.value_type v; t2) { assert(v in t2); } char[] str = tree.toString(); assert(str=="0, 1, 2, 3, 4, 5, 6, 7, 8, 9"); for (int i=-10; i<20; i++) { Node n = tree.search(i); if (i>=0 && i<10) { assert (n !is null); } else { assert (n is null); } } { Node n; n = tree.search_min(); assert(n.value == 0); n = tree.search_max(); assert(n.value == 9); } // test bogus removes { bool ret = tree.remove(99); assert(!ret); assert(tree.size == 10); Node n = tree.pop(99); assert(!ret); assert(n is null); } // test remove -- remove them all 1 by 1 int[] to_remove = [7,2,3,5,6,9,4,8,0,1]; bool[int] removed; foreach(r; to_remove) { assert(tree.is_valid); removed[r] = true; tree.remove(r); for (int i=0; i<10; i++) { assert( (i in removed) || (i in tree) ); } } assert(tree.empty); assert(tree.search_min() is null); assert(tree.search_max() is null); assert(tree.is_valid); // Put some more back in for (int i = 0; i < 10; i++) { bool inserted = tree.insert(i); assert(inserted == true); assert(!tree.empty); } t2 = tree.dup; // Pop them out one by one for (int i = 0; i < 10; i++) { Node popped = tree.pop(i); assert(tree.size == 10-i-1); assert(popped !is null); assert(popped.value == i); } assert( tree.pop(0) is null ); assert(tree.empty); tree = t2.dup; // Pop them out one by one with pop() (== pop_min) for (int i = 0; i < 10; i++) { Node popped = tree.pop(); assert(tree.size == 10-i-1); assert(popped !is null); assert(popped.value == i); } assert( tree.pop_min() is null ); assert(tree.empty); tree = t2.dup; // Pop them out one by one with pop_min for (int i = 0; i < 10; i++) { Node popped = tree.pop_min(); assert(tree.size == 10-i-1); assert(popped !is null); assert(popped.value == i); } assert( tree.pop_min() is null ); assert(tree.empty); tree = t2.dup; // Pop them out one-by-one in reverse with pop_max for (int i = 0; i < 10; i++) { Node popped = tree.pop_max(); assert(tree.size == 10-i-1); assert(popped !is null); assert(popped.value == 9-i); } assert( tree.pop_max() is null ); assert(tree.empty); } { // Test lexicographical ordered pairs with external comparator struct Pair { int L,R; static Pair opCall(int L, int R) { Pair ret; ret.L=L; ret.R=R; return ret; } char[] toString() { return string_format("(%d, %d)", L,R); } } struct PairLess { static bool opCall(ref Pair a, ref Pair b) { if (a.Lb.L) { return false; } if (a.R= 0; i--) { for (int j = 3; j >= 0; j--) { tree.insert(Pair(i,j)); tree.insert(Pair(j,i)); } } char[] str = tree.toString(); assert(str=="(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3)"); // Should be 16 distinct values, (though 32 were inserted) assert(tree.size==16); // Test opIn assert(!(Pair(1,9) in tree)); assert(!(Pair(9,1) in tree)); assert(Pair(3,2) in tree); //tree.clear(); bool[Pair] aacheck; foreach(Node n; tree) aacheck[n.value]=true; foreach(k,v; aacheck) { assert(k in tree); } foreach(Node n; tree) { assert(n.value in tree); } foreach(Pair v; tree) { assert(v in tree); } foreach(tree.value_type v; tree) { assert(v in tree); } Tree t2 = new Tree(tree); // creates a copy assert(aacheck.length == t2.size); // Make sure contents are the same foreach(k,v; aacheck) { assert(k in t2); } foreach(Node n; t2) { assert(n.value in t2); } foreach(Pair v; t2) { assert(v in t2); } foreach(tree.value_type v; t2) { assert(v in t2); } for (int i=-10; i<20; i++) { for (int j=-10; j<20; j++) { Node n = tree.search(Pair(i,j)); if (i>=0 && i<4 && j>=0 && j<4) { assert (n !is null); } else { assert (n is null); } } } // test remove -- remove them all 1 by 1 Pair[] to_remove = [ Pair(0, 0), Pair(0, 1), Pair(0, 2), Pair(0, 3), Pair(1, 0), Pair(1, 1), Pair(1, 2), Pair(1, 3), Pair(2, 0), Pair(2, 1), Pair(2, 2), Pair(2, 3), Pair(3, 0), Pair(3, 1), Pair(3, 2), Pair(3, 3), ]; bool[Pair] removed; foreach(r; to_remove) { assert(tree.is_valid); removed[r] = true; tree.remove(r); for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { assert( (Pair(i,j) in removed) || (Pair(i,j) in tree) ); } } } assert(tree.toString()==""); assert(tree.is_valid); } { // Test lexicographical ordered pairs with an opCmp defined struct CPair { int L,R; int opCmp(CPair that) { if (L < that.L) { return -1; } if (L > that.L) { return 1; } if (R < that.R) { return -1; } if (R > that.R) { return 1; } return 0; } char[] toString() { return string_format("(%d, %d)", L,R); } } auto tree = new RedBlackTree!(CPair); static assert( is(CPair==tree.value_type) ); static assert( is(CPair==tree.key_type) ); alias typeof(tree) Tree; alias Tree.node_type Node; for (int i = 3; i >= 0; i--) { for (int j = 3; j >= 0; j--) { tree.insert(CPair(i,j)); tree.insert(CPair(j,i)); } } // Should be 16 distinct values, (though 32 were inserted) assert(tree.size==16); // Test opIn assert(!(CPair(1,9) in tree)); assert(!(CPair(9,1) in tree)); assert(CPair(3,2) in tree); //tree.clear(); bool[CPair] aacheck; foreach(Node n; tree) aacheck[n.value]=true; foreach(k,v; aacheck) { assert(k in tree); } foreach(Node n; tree) { assert(n.value in tree); } foreach(CPair v; tree) { assert(v in tree); } foreach(tree.value_type v; tree) { assert(v in tree); } assert(tree.is_valid()); } { // Test lexicographical ordered pairs on CLASS with an opCmp defined class KPair { this(int i, int j) { L=i; R=j; } int L,R; int opCmp(KPair that) { if (L < that.L) { return -1; } if (L > that.L) { return 1; } if (R < that.R) { return -1; } if (R > that.R) { return 1; } return 0; } char[] toString() { return string_format("(%d, %d)", L,R); } } auto tree = new RedBlackTree!(KPair); static assert( is(KPair==tree.value_type) ); static assert( is(KPair==tree.key_type) ); alias typeof(tree) Tree; alias Tree.node_type Node; for (int i = 3; i >= 0; i--) { for (int j = 3; j >= 0; j--) { tree.insert(new KPair(i,j)); tree.insert(new KPair(j,i)); } } // Should be 16 distinct values, (though 32 were inserted) assert(tree.size==16); // Test opIn assert(!(new KPair(1,9) in tree)); assert(!(new KPair(9,1) in tree)); assert(new KPair(3,2) in tree); //tree.clear(); bool[KPair] aacheck; foreach(Node n; tree) aacheck[n.value]=true; foreach(k,v; aacheck) { assert(k in tree); } foreach(Node n; tree) { assert(n.value in tree); } foreach(KPair v; tree) { assert(v in tree); } foreach(tree.value_type v; tree) { assert(v in tree); } assert(tree.is_valid()); } { // Test lexicographical ordered pairs on CLASS with external comparator defined class CKPair { this(int i, int j) { L=i; R=j; } int L,R; char[] toString() { return string_format("(%d, %d)", L,R); } } struct CKPairLess { static bool opCall(ref CKPair a, ref CKPair b) { if (a.Lb.L) { return false; } if (a.R= 0; i--) { for (int j = 3; j >= 0; j--) { tree.insert(new CKPair(i,j)); tree.insert(new CKPair(j,i)); } } // Should be 16 distinct values, (though 32 were inserted) assert(tree.size==16); // Test opIn assert(!(new CKPair(1,9) in tree)); assert(!(new CKPair(9,1) in tree)); assert(new CKPair(3,2) in tree); //tree.clear(); bool[CKPair] aacheck; foreach(Node n; tree) aacheck[n.value]=true; foreach(k,v; aacheck) { assert(k in tree); } foreach(Node n; tree) { assert(n.value in tree); } foreach(CKPair v; tree) { assert(v in tree); } foreach(tree.value_type v; tree) { assert(v in tree); } assert(tree.is_valid()); } { // Test multi-set functionality auto tree = new RedBlackTree!(int); alias typeof(tree) Tree; alias Tree.node_type Node; for (int i = 0; i < 10; i++) { assert(tree.size==i); tree.insert_unconditional(i); } assert(tree.size==10); // Should not ignore repeats for (int i = 0; i < 10; i++) { tree.insert_unconditional(i); } assert(tree.size==20); for (int i=0; i<10; i++) { tree.remove(i); } assert(tree.size==10); for (int i=0; i<10; i++) { tree.remove(i); } assert(tree.size==0); } }