      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