      1 var define = require("../define").define,
      2         Collection = require("./Collection"),
      3 		Iterable = require("./Iterable"),
      4         base = require("../base"),
      5         multiply = base.string.multiply;
      6
      7 var compare = function(a, b) {
      8     var ret = 0;
      9     if (a > b) {
     10         return 1;
     11     } else if (a < b) {
     12         return -1;
     13     }
     14     return ret;
     15
     16 };
     17
     18 var Tree = define([Collection, Iterable], {
     19
     20     instance : {
     21
     22         /**@lends comb.collections.Tree.prototype*/
     23
     24         /**
     25          * Prints a node
     26          * @param node node to print
     27          * @param level the current level the node is at, Used for formatting
     28          */
     29         __printNode : function(node, level) {
     30             //console.log(level);
     31             var str = [];
     32             if (node == null || node == undefined) {
     33                 str.push(multiply('\t', level));
     34                 str.push("~");
     35                 console.log(str.join(""));
     36             } else {
     37                 this.__printNode(node.right, level + 1);
     38                 str.push(multiply('\t', level));
     39                 str.push(node.data + "\n");
     40                 console.log(str.join(""));
     41                 this.__printNode(node.left, level + 1);
     42             }
     43         },
     44
     45         /**
     46          * Base Class for all tree implementations
     47          * @constructs
     48          * @augments comb.collections.Collection
     49          * @augments comb.collections.Iterable
     50          * @memberOf comb.collections
     51          *
     52          * @param {Object} options options to initialize the tree
     53          * @param {Function} options.compare function used to compare items in a tree must return an integer
     54          *  <ul>
     55          *      </li>-1 for less than</li>
     56          *      </li>0 for equal</li>
     57          *      </li>1 for greater than</li>
     58          *  </ul>
     59          *
     60          */
     61         constructor : function(options) {
     62             options = options || {};
     63             this.compare = options.compare || compare;
     64             this.__root = null;
     65         },
     66
     67         /**
     68          * Inserts an item into the tree
     69          * @param {Anything} data the item to insert
     70          */
     71         insert : function(data) {
     72             throw new Error("Not Implemented");
     73         },
     74
     75         /**
     76          * Removes an item from the tree
     77          * @param {Anything} data the item to insert
     78          */
     79         remove : function(data) {
     80             throw new Error("Not Implemented");
     81         },
     82
     83         /**
     84          * Clear all items from a tree
     85          */
     86         clear : function() {
     87             this.__root = null;
     88         },
     89
     90         /**
     91          * Test if a tree is empty
     92          *
     93          * @return {Boolean} true if empty false otherwise
     94          */
     95         isEmpty : function() {
     96             return this.__root == null;
     97         },
     98
     99         /**
    100          * Traverse a tree until the callback function returns false
    101          *
    102          * <p><b>Not typically used directly</b></p>
    103          *
    104          * @param {Object} node the node to start at
    105          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    106          * @param {Function} callback called for each item, traversal continues until the function returns false
    107          *
    108          */
    109         traverseWithCondition : function(node, order, callback) {
    110             var cont = true;
    111             if (node) {
    112                 order = order || Tree.PRE_ORDER;
    113                 if (order === Tree.PRE_ORDER) {
    114                     cont = callback(node.data);
    115                     if (cont) {
    116                         cont = this.traverseWithCondition(node.left, order, callback);
    117                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
    118
    119                     }
    120                 } else if (order === Tree.IN_ORDER) {
    121                     cont = this.traverseWithCondition(node.left, order, callback);
    122                     if (cont) {
    123                         cont = callback(node.data);
    124                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
    125                     }
    126                 } else if (order === Tree.POST_ORDER) {
    127                     cont = this.traverseWithCondition(node.left, order, callback);
    128                     if (cont) {
    129                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
    130                         cont && (cont = callback(node.data));
    131                     }
    132                 } else if (order === Tree.REVERSE_ORDER) {
    133                     cont = this.traverseWithCondition(node.right, order, callback);
    134                     if (cont) {
    135                         cont = callback(node.data);
    136                         cont && (cont = this.traverseWithCondition(node.left, order, callback));
    137                     }
    138                 }
    139             }
    140             return cont;
    141         },
    142
    143         /**
    144          * Traverse a tree
    145          *
    146          * <p><b>Not typically used directly</b></p>
    147          *
    148          * @param {Object} node the node to start at
    149          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    150          * @param {Function} callback called for each item
    151          *
    152          */
    153         traverse : function(node, order, callback) {
    154             if (node) {
    155                 order = order || Tree.PRE_ORDER;
    156                 if (order === Tree.PRE_ORDER) {
    157                     callback(node.data);
    158                     this.traverse(node.left, order, callback);
    159                     this.traverse(node.right, order, callback);
    160                 } else if (order === Tree.IN_ORDER) {
    161                     this.traverse(node.left, order, callback);
    162                     callback(node.data);
    163                     this.traverse(node.right, order, callback);
    164                 } else if (order === Tree.POST_ORDER) {
    165                     this.traverse(node.left, order, callback);
    166                     this.traverse(node.right, order, callback);
    167                     callback(node.data);
    168                 } else if (order === Tree.REVERSE_ORDER) {
    169                     this.traverseWithCondition(node.right, order, callback);
    170                     callback(node.data);
    171                     this.traverseWithCondition(node.left, order, callback);
    172
    173                 }
    174             }
    175         },
    176
    177         /**
    178          * Loop through each item in the tree
    179          * @param {Function} cb called for each item in the tree
    180          * @param {Object} [scope=this] scope to call the function in
    181          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    182          */
    183         forEach : function(cb, scope, order) {
    184             if (typeof cb !== "function")
    185                 throw new TypeError();
    186             order = order || Tree.IN_ORDER;
    187             scope = scope || this;
    188             this.traverse(this.__root, order, function(node) {
    189                 cb.call(scope, node, this);
    190             });
    191         },
    192
    193         /**
    194          * Loop through each item in the tree, collecting the value returned by the callback funciton.
    195          * @param {Function} cb called for each item in the tree.
    196          *                   Whatever the function returns is inserted into the return tree
    197          * @param {Object} [scope=this] scope to call the function in
    198          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    199          *
    200          * @return {comb.collections.Tree} the tree with the mapped items
    201          */
    202         map : function(cb, scope, order) {
    203             if (typeof cb !== "function")
    204                 throw new TypeError();
    205
    206             order = order || Tree.IN_ORDER;
    207             scope = scope || this;
    208             var construct = this.constructor;
    209             var ret = new construct();
    210             this.traverse(this.__root, order, function(node) {
    211                 ret.insert(cb.call(scope, node, this));
    212             });
    213             return ret;
    214         },
    215
    216         /**
    217          * Filters a tree, only returning items that result in true being returned from the callback
    218          *
    219          * @param {Function} cb called for each item in the tree
    220          * @param {Object} [scope=this] scope to call the function in
    221          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    222          *
    223          * @return {comb.collections.Tree} the tree with items that resulted in true being returned from the callback
    224          */
    225         filter : function(cb, scope, order) {
    226             if (typeof cb !== "function")
    227                 throw new TypeError();
    228
    229             order = order || Tree.IN_ORDER;
    230             scope = scope || this;
    231             var construct = this.constructor;
    232             var ret = new construct();
    233             this.traverse(this.__root, order, function(node) {
    234                 var include = cb.call(scope, node, this);
    235                 include && ret.insert(node);
    236             });
    237             return ret;
    238         },
    239
    240         /**
    241          * Reduces a tree
    242          *
    243          * @param {Function} fun called for each item in the tree
    244          * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
    245          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    246          *
    247          * @return the result of the reduce function
    248          */
    249         reduce : function(fun, accumulator, order) {
    250             var arr = this.toArray(order);
    251             var args = [fun];
    252             if (!base.isUndefined(accumulator) && !base.isNull(accumulator)) {
    253                 args.push(accumulator);
    254             }
    255             return arr.reduce.apply(arr, args);
    256         },
    257
    258         /**
    259          * Reduces from right to left
    260          *
    261          * @param {Function} fun called for each item in the tree
    262          * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
    263          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    264          *
    265          * @return the result of the reduce function
    266          */
    267         reduceRight : function(fun, accumulator, order) {
    268             var arr = this.toArray(order);
    269             var args = [fun];
    270             if (!base.isUndefined(accumulator) && !base.isNull(accumulator)) {
    271                 args.push(accumulator);
    272             }
    273             return arr.reduceRight.apply(arr, args);
    274         },
    275
    276         /**
    277          * Determines if every item meets the condition returned by the callback.
    278          *
    279          * @param {Function} cb called for each item in the tree
    280          * @param {Object} [scope=this] scope to call the function in
    281          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    282          *
    283          * @return {Boolean} True if every item passed false otherwise
    284          */
    285         every : function(cb, scope, order) {
    286             if (typeof cb !== "function")
    287                 throw new TypeError();
    288
    289             order = order || Tree.IN_ORDER;
    290             scope = scope || this;
    291             var ret = false;
    292             this.traverseWithCondition(this.__root, order, function(node) {
    293                 return (ret = cb.call(scope, node, this));
    294             });
    295             return ret;
    296         },
    297
    298         /**
    299          * Determines if some item meet the condition returned by the callback. Traversal ends the first time true is found.
    300          *
    301          * @param {Function} cb called for each item in the tree
    302          * @param {Object} [scope=this] scope to call the function in
    303          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    304          *
    305          * @return {Boolean} True if every item passed false otherwise
    306          */
    307         some : function(cb, scope, order) {
    308             if (typeof cb !== "function")
    309                 throw new TypeError();
    310
    311             order = order || Tree.IN_ORDER;
    312             scope = scope || this;
    313             var ret;
    314             this.traverseWithCondition(this.__root, order, function(node) {
    315                 ret = cb.call(scope, node, this);
    316                 return !ret;
    317             });
    318             return ret;
    319         },
    320
    321         /**
    322          * Converts a tree into an array based on the specified order
    323          *
    324          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
    325          *
    326          * @return {Array} array of all items in the order specified.
    327          */
    328         toArray : function(order) {
    329             order = order || Tree.IN_ORDER;
    330             var arr = [];
    331             this.traverse(this.__root, order, function(node) {
    332                 arr.push(node);
    333             });
    334             return arr;
    335         },
    336
    337
    338
    339         /**
    340          * Determines if a value is contained in the tree
    341          * @param {*} value the value to find
    342          *
    343          * @return {Boolean} true if the tree contains the item false otherwise.
    344          */
    345         contains : function(value) {
    346             var ret = false;
    347             var root = this.__root;
    348             while (root != null) {
    349                 var cmp = this.compare(value, root.data);
    350                 if (cmp) {
    351                     root = root[(cmp == -1) ? "left" : "right"];
    352                 } else {
    353                     ret = true;
    354                     root = null;
    355                 }
    356             }
    357             return ret;
    358         },
    359
    360         /**
    361          * Finds a value in the tree
    362          * @param {*} value the value to find
    363          *
    364          * @return the value of the node that matched
    365          */
    366         find : function(value) {
    367             var ret;
    368             var root = this.__root;
    369             while (root != null) {
    370                 var cmp = this.compare(value, root.data);
    371                 if (cmp) {
    372                     root = root[(cmp == -1) ? "left" : "right"];
    373                 } else {
    374                     ret = root.data;
    375                     break;
    376                 }
    377             }
    378             return ret;
    379         },
    380
    381         /**
    382          * Find all values less than a value
    383          * @param {*} value the value to find nodes less than
    384          * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
    385          *
    386          * @return {Array} the array containing all values less than
    387          */
    388         findLessThan : function(value, exclusive) {
    389             //find a better way!!!!
    390             var ret = [], compare = this.compare;
    391             this.traverseWithCondition(this.__root, exports.IN_ORDER, function(v) {
    392                 var cmp = compare(value, v);
    393                 if ((!exclusive && cmp == 0) || cmp == 1) {
    394                     ret.push(v);
    395                     return true;
    396                 } else {
    397                     return false;
    398                 }
    399             });
    400             return ret;
    401         },
    402
    403         /**
    404          * Find all greater than a value
    405          * @param {*} value the value to find nodes greater than
    406          * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
    407          *
    408          * @return {Array} the array containing all values greater than
    409          */
    410         findGreaterThan : function(value, exclusive) {
    411             //find a better way!!!!
    412             var ret = [], compare = this.compare;
    413             this.traverse(this.__root, exports.REVERSE_ORDER, function(v) {
    414                 var cmp = compare(value, v);
    415                 if ((!exclusive && cmp == 0) || cmp == -1) {
    416                     ret.push(v);
    417                     return true;
    418                 } else {
    419                     return false;
    420                 }
    421             });
    422             return ret;
    423         },
    424
    425         /**
    426          * Prints a tree to the console.
    427          */
    428         print : function() {
    429             this.__printNode(this.__root, 0);
    430         }
    431     },
    432
    433     static : {
    434     /** @lends comb.collections.Tree */
    435
    436         /**
    437          * Pre Order
    438          */
    439         PRE_ORDER : "pre_order",
    440
    441         /**
    442          * In Order
    443          */
    444         IN_ORDER : "in_order",
    445
    446         /**
    447          * Post Order
    448          */
    449         POST_ORDER:"post_order",
    450
    451         /**
    452
    453          * Reverse Order
    454          */
    455         REVERSE_ORDER : "reverse_order"
    456     }
    457 });
    458 /**@ignore*/
    459 module.exports = exports = Tree;
    460
    461