1 var define = require("../define").define,
  2         Tree = require("./Tree"),
  3         base = require("../base"),
  4         multiply = base.string.multiply;
  5 
  6 var RED = "red", BLACK = "black";
  7 
  8 var isRed = function(node) {
  9     return node != null && node.red;
 10 };
 11 
 12 var makeNode = function(data, parent) {
 13     return {
 14         data : data,
 15         red : true,
 16         left : null,
 17         right : null
 18     }
 19 };
 20 
 21 var insert = function(root, data, compare) {
 22     if (root == null) {
 23         return makeNode(data, null);
 24 
 25     } else {
 26         var cmp = compare(data, root.data);
 27         if (cmp) {
 28             var dir = cmp == -1 ? "left" : "right";
 29             var otherDir = dir == "left" ? "right" : "left";
 30             root[dir] = insert(root[dir], data, compare);
 31             var node = root[dir];
 32 
 33             if (isRed(node)) {
 34 
 35                 var sibling = root[otherDir];
 36                 if (isRed(sibling)) {
 37                     /* Case 1 */
 38                     root.red = true;
 39                     node.red = false;
 40                     sibling.red = false;
 41                 } else {
 42 
 43                     if (isRed(node[dir])) {
 44 
 45                         root = rotateSingle(root, otherDir);
 46                     } else if (isRed(node[otherDir])) {
 47 
 48                         root = rotateDouble(root, otherDir);
 49                     }
 50                 }
 51 
 52             }
 53         }
 54     }
 55     return root;
 56 };
 57 
 58 var rotateSingle = function(root, dir) {
 59     var otherDir = dir == "left" ? "right" : "left";
 60     var save = root[otherDir];
 61     root[otherDir] = save[dir];
 62     save[dir] = root;
 63     root.red = true;
 64     save.red = false;
 65     return save;
 66 };
 67 
 68 var rotateDouble = function(root, dir) {
 69     var otherDir = dir == "left" ? "right" : "left";
 70     root[otherDir] = rotateSingle(root[otherDir], otherDir);
 71     return rotateSingle(root, dir);
 72 };
 73 
 74 
 75 var remove = function (root, data, done, compare) {
 76     if (root == null) {
 77         done.done = true;
 78     } else {
 79         var dir;
 80         if (compare(data, root.data) == 0) {
 81             if (root.left == null || root.right == null) {
 82                 var save = root[root.left == null ? "right" : "left"];
 83                 /* Case 0 */
 84                 if (isRed(root)) {
 85                     done.done = true;
 86                 } else if (isRed(save)) {
 87                     save.red = false;
 88                     done.done = true;
 89                 }
 90                 return save;
 91             }
 92             else {
 93                 var heir = root.right, p;
 94                 while (heir.left != null) {
 95                     p = heir;
 96                     heir = heir.left;
 97                 }
 98                 p && (p.left = null);
 99                 root.data = heir.data;
100                 data = heir.data;
101             }
102         }
103         dir = compare(data, root.data) == -1 ? "left" : "right";
104         root[dir] = remove(root[dir], data, done, compare);
105         !done.done && (root = removeBalance(root, dir, done));
106     }
107     return root;
108 };
109 
110 var removeBalance = function(root, dir, done) {
111     var notDir = dir == "left" ? "right" : "left";
112     var p = root, s = p[notDir];
113     if (isRed(s)) {
114         root = rotateSingle(root, dir);
115         s = p[notDir];
116     }
117     if (s != null) {
118         if (!isRed(s.left) && !isRed(s.right)) {
119             isRed(p) && (done.done = true);
120             p.red = 0;
121             s.red = 1;
122         } else {
123             var save = p.red, newRoot = ( root === p );
124             p = (isRed(s[notDir]) ? rotateSingle : rotateDouble)(p, dir);
125             p.red = save;
126             p.left.red = p.right.red = 0;
127             if (newRoot) {
128                 root = p;
129             } else {
130                 root[dir] = p;
131             }
132             done.done = true;
133         }
134     }
135     return root;
136 };
137 var RedBlackTree;
138 /**
139  * @class <p>A RedBlack tree is a form of a self balancing binary tree.</p>
140  *
141  * <b>Performance</b>
142  * <table>
143  *     <tr><td></td><td>Best</td><td>Worst</td></tr>
144  *     <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
145  *     <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
146  *     <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
147  *     <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
148  * <table>
149  *     @name RedBlackTree
150  * @augments comb.collections.Tree
151  * @memberOf comb.collections
152  */
153 module.exports = exports  = define(Tree, {
154     instance : {
155         /**@lends comb.collections.RedBlackTree.prototype*/
156         insert : function(data) {
157             this.__root = insert(this.__root, data, this.compare);
158             this.__root.red = false;
159         },
160 
161         remove : function(data) {
162             var done = {done : false};
163             var root = remove(this.__root, data, done, this.compare);
164             if (root != null)
165                 root.red = 0;
166             this.__root = root;
167         },
168 
169 
170         __printNode : function(node, level) {
171             var str = [];
172             if (node == null || node == undefined) {
173                 str.push(multiply('\t', level));
174                 str.push("~");
175                 console.log(str.join(""));
176             } else {
177                 this.__printNode(node.right, level + 1);
178                 str.push(multiply('\t', level));
179                 str.push((node.red ? "RED" : "BLACK") + ":" + node.data + "\n");
180                 console.log(str.join(""));
181                 this.__printNode(node.left, level + 1);
182             }
183         }
184 
185     }
186 });
187 
188