1 /**
  2  * @fileOverview An utility of collections
  3  */
  4 
  5 /**
  6  * Abstract Map
  7  * 
  8  * @class
  9  * @author Sijie Guo <sijie0413@gmail.com>
 10  */
 11 var Map = function(){};
 12 
 13 /**
 14  * Entry for map
 15  * 
 16  * @class
 17  * @author Sijie Guo <sijie0413@gmail.com>
 18  * @constructor
 19  *
 20  * @param {Object} key key
 21  * @param {Object} value value
 22  */
 23 Map.Entry = function(key, value) {
 24   this.key = key;
 25   this.value = value;
 26 };
 27 
 28 exports.Map = Map;
 29 
 30 ///
 31 /// Tree Map implementation
 32 ///
 33 
 34 /**
 35  * Tree Map
 36  * 
 37  * @class
 38  * @author Sijie Guo <sijie0413@gmail.com>
 39  * @constructor
 40  *
 41  * @param {Function} comparator comparasion function
 42  */
 43 var TreeMap = function(comparator) {
 44   
 45   var that = this;
 46 
 47   /**@field comparator used to maintain order in tree map*/
 48   var _compareFunc = comparator;
 49   /**@field root entry*/
 50   var _root = null;
 51   /**@filed number of entries in the tree*/
 52   var _size = 0;
 53   var _modCnt = 0;
 54 
 55   /**@constant COLOR*/
 56   var COLOR = {};
 57   /**@constant RED COLOR*/
 58   COLOR.RED = 0;
 59   /**@constant BLACK COLOR*/
 60   COLOR.BLACK = 1;
 61   ///
 62   /// Private Class
 63   ///
 64   var Entry = function(key, value, parent) {
 65     this.key = key;
 66     this.value = value;
 67     this.left = null;
 68     this.right = null;
 69     this.parent = parent;
 70     this.color = COLOR.BLACK;
 71   };
 72 
 73   ///
 74   /// Private methods
 75   ///
 76 
 77   function size() {
 78     return _size;
 79   }
 80 
 81   /**
 82    * Return the map entry of the given key, or null if the map does not contain an entry for the key.
 83    * 
 84    * @param {Object} key given query key
 85    *
 86    * @return the map entry of the given key, or null if the map does not contain an entry for the key
 87    *
 88    * @public
 89    */
 90   function getEntry(key) {
 91     var p = _root;
 92     while (null !== p) {
 93       var cmp = _compareFunc(key, p.key);
 94       if (cmp < 0) {
 95         p = p.left;
 96       } else if (cmp > 0) {
 97         p = p.right;
 98       } else {
 99         return p;
100       }
101     }
102     return null;
103   }
104 
105   function getCeilingEntry(key) {
106     var p = _root;
107     while (null !== p) {
108       var cmp = _compareFunc(key, p.key);
109       if (cmp < 0) {
110         if (null !== p.left) {
111           p = p.left;
112         } else {
113           return p;
114         }
115       } else if (cmp > 0) {
116         if (null !== p.right) {
117           p = p.right;
118         } else {
119           var parent = p.parent;
120           var ch = p;
121           while (null !== parent && ch === parent.right) {
122             ch = parent;
123             parent = parent.parent;
124           }
125           return parent;
126         }
127       } else {
128         return p;
129       }
130     }
131     return null;
132   }
133 
134   function getFloorEntry(key) {
135     var p = _root;
136     while (null !== p) {
137       var cmp = _compareFunc(key, p.key);
138       if (cmp > 0) {
139         if (null !== p.right) {
140           p = p.right;
141         } else {
142           return p;
143         }
144       } else if (cmp < 0) {
145         if (null !== p.left) {
146           p = p.left;
147         } else {
148           var parent = p.parent;
149           var ch = p;
150           while (null !== parent && ch === parent.left) {
151             ch = parent;
152             parent = parent.parent;
153           }
154           return parent;
155         }
156       } else {
157         return p;
158       }
159     }
160     return null;
161   }
162 
163   function getHigherEntry(key) {
164     var p = _root;
165     while (null !== p) {
166       var cmp = _compareFunc(key, p.key);
167       if (cmp < 0) {
168         if (null !== p.left) {
169           p = p.left;
170         } else {
171           return p;
172         }
173       } else {
174         if (null !== p.right) {
175           p = p.right;
176         } else {
177           var parent = p.parent;
178           var ch = p;
179           while (null !== parent && ch === parent.right) {
180             ch = parent;
181             parent = parent.parent;
182           }
183           return parent;
184         }
185       }
186     }
187     return null;
188   }
189 
190   function getLowerEntry(key) {
191     var p = _root;
192     while (null !== p) {
193       var cmp = _compareFunc(key, p.key);
194       if (cmp > 0) {
195         if (null !== p.right) {
196           p = p.right;
197         } else {
198           return p;
199         }
200       } else {
201         if (null !== p.left) {
202           p = p.left;
203         } else {
204           var parent = p.parent;
205           var ch = p;
206           while (null !== parent && ch === parent.left) {
207             ch = parent;
208             parent = parent.parent;
209           }
210           return parent;
211         }
212       }
213     }
214     return null;
215   }
216 
217   function put(key, value) {
218     if (!key) {
219       console.log("TreeMap doesn't support null key now.");
220       return;
221     }
222 
223     var t = _root;
224     if (null === t) {
225       _root = new Entry(key, value, null);
226       _size = 1;
227       ++_modCnt;
228       return null;
229     }
230 
231     var cmp, parent;
232     do {
233       parent = t;
234       cmp = _compareFunc(key, t.key);
235       if (cmp < 0) {
236         t = t.left;
237       } else if (cmp > 0) {
238         t = t.right;
239       } else {
240         var oldValue = t.value;
241         t.value = value;
242         return oldValue;
243       }
244     } while (null !== t);
245 
246     var e = new Entry(key, value, parent);
247     if (cmp < 0) {
248       parent.left = e;
249     } else {
250       parent.right = e;
251     }
252     fixAfterInsertion(e);
253     ++_size;
254     ++_modCnt;
255     return null;
256   }
257 
258 
259   function deleteEntry(p) {
260     ++_modCnt;
261     --_size;
262 
263     // copy successor's element to p and then make p point to successor
264     if (null !== p.left && null !== p.right) {
265       var s = successor(p);
266       p.key = s.key;
267       p.value = s.value;
268       p = s;
269     }
270 
271     var replacement = null !== p.left ? p.left : p.right;
272 
273     if (null !== replacement) {
274       replacement.parent = p.parent;
275       if (null === p.parent) {
276         _root = replacment;
277       } else if (p === p.parent.left) {
278         p.parent.left = replacement;
279       } else {
280         p.parent.right = replacement;
281       }
282 
283       p.left = p.right = p.parent = null;
284 
285       if (COLOR.BLACK === p.color) {
286         fixAfterDeletion(replacement);
287       }
288     } else if (null === p.parent) {
289       _root = null;
290     } else {
291       if (COLOR.BLACK === p.color) {
292         fixAfterDeletion(p);
293       }
294 
295       if (null !== p.parent) {
296         if (p === p.parent.left) {
297           p.parent.left = null;
298         } else if (p === p.parent.right) {
299           p.parent.right = null;
300         }
301         p.parent = null;
302       }
303     }
304   }
305 
306   function clear() {
307     ++_modCnt;
308     _size = 0;
309     _root = null;
310   }
311 
312   ///
313   /// Red-Black-Tree Blance Operations
314   ///
315   
316   function colorOf(p) {
317     return null === p ? COLOR.BLACK : p.color;
318   }
319 
320   function setColor(p, color) {
321     if (null !== p) {
322       p.color = color;
323     }
324   }
325 
326   function parentOf(p) {
327     return null === p ? null : p.parent;
328   }
329 
330   function leftOf(p) {
331     return null === p ? null : p.left;
332   }
333 
334   function rightOf(p) {
335     return null === p ? null : p.right;
336   }
337 
338   function rotateLeft(p) {
339     if (null === p) {
340       return;
341     }
342     var r = p.right;
343     p.right = r.left;
344     if (null !== r.left) {
345       r.left.parent = p;
346     }
347     r.parent = p.parent;
348     if (null === p.parent) {
349       _root = r;
350     } else if (p.parent.left === p) {
351       p.parent.left = r;
352     } else {
353       p.parent.right = r;
354     }
355     r.left = p;
356     p.parent = r;
357   }
358 
359   function rotateRight(p) {
360     if (null === p) {
361       return;
362     }
363     var l = p.left;
364     p.left = l.right;
365     if (null !== l.right) {
366       l.right.parent = p;
367     }
368     l.parent = p.parent;
369     if (null === p.parent) {
370       _root = l;
371     } else if (p.parent.left === p) {
372       p.parent.left = l;
373     } else {
374       p.parent.right = l;
375     }
376     l.right = p;
377     p.parent = l;
378   }
379 
380   function fixAfterInsertion(x) {
381     x.color = COLOR.RED;
382 
383     var y;
384     while (null !== x && _root !== x && COLOR.RED == x.parent.color) {
385       if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
386         y = rightOf(parentOf(parentOf(x)));
387         if (COLOR.RED === colorOf(y)) {
388           setColor(parentOf(x), COLOR.BLACK);
389           setColor(y, COLOR.BLACK);
390           setColor(parentOf(parentOf(x)), COLOR.RED);
391           x = parentOf(parentOf(x));
392         } else {
393           if (rightOf(parent(x)) === x) {
394             x = parentOf(x);
395             rotateLeft(x);
396           }
397           setColor(parentOf(x), COLOR.BLACK);
398           setColor(parentOf(parentOf(x)), COLOR.RED);
399           rotateRight(parentOf(parentOf(x)));
400         }
401       } else {
402         y = leftOf(parentOf(parentOf(x)));
403         if (COLOR.RED === colorOf(y)) {
404           setColor(parentOf(x), COLOR.BLACK);
405           setColor(y, COLOR.BLACK);
406           setColor(parentOf(parentOf(x)), COLOR.RED);
407           x = parentOf(parentOf(x));
408         } else {
409           if (leftOf(parentOf(x)) === x) {
410             x = parentOf(x);
411             rotateRight(x);
412           }
413           setColor(parentOf(x), COLOR.BLACK);
414           setColor(parentOf(parentOf(x)), COLOR.RED);
415           rotateLeft(parentOf(parentOf(x)));
416         }
417       }
418     }
419     _root.color = COLOR.BLACK;
420   }
421 
422   function fixAfterDeletion(x) {
423     var sib;
424     while (_root !== x && COLOR.BLACK === colorOf(x)) {
425       if (leftOf(parentOf(x)) === x) {
426         sib = rightOf(parentOf(x));
427 
428         if (COLOR.RED === colorOf(sib)) {
429           setColor(sib, COLOR.BLACK);
430           setColor(parentOf(x), COLOR.RED);
431           rotateLeft(parentOf(x));
432           sib = rightOf(parentOf(x));
433         }
434 
435         if (COLOR.BLACK === colorOf(rightOf(sib)) &&
436             COLOR.BLACK === colorOf(leftOf(sib))) {
437           setColor(sib, COLOR.RED);
438           x = parentOf(x);
439         } else {
440           if (COLOR.BLACK === colorOf(rightOf(sib))) {
441             setColor(leftOf(sib), BLACK);
442             setColor(sib, COLOR.RED);
443             rotateRight(sib);
444             sib = rightOf(parentOf(x));
445           }
446 
447           setColor(sib, colorOf(parentOf(x)));
448           setColor(parentOf(x), COLOR.BLACK);
449           setColor(rightOf(sib), COLOR.BLACK);
450           rotateLeft(parentOf(x));
451           x = _root;
452         }
453       } else {
454         sib = leftOf(parentOf(x));
455 
456         if (COLOR.RED === colorOf(sib)) {
457           setColor(sib, COLOR.BLACK);
458           setColor(parentOf(x), COLOR.RED);
459           rotateRight(parentOf(x));
460           sib = leftOf(parentOf(x));
461         }
462 
463         if (COLOR.BLACK === colorOf(rightOf(sib)) &&
464             COLOR.BLACK === colorOf(leftOf(sib))) {
465           setColor(sib, COLOR.RED);
466           x = parentOf(x);
467         } else {
468           if (COLOR.BLACK === colorOf(leftOf(sib))) {
469             setColor(rightOf(sib), BLACK);
470             setColor(sib, COLOR.RED);
471             rotateLeft(sib);
472             sib = leftOf(parentOf(x));
473           }
474 
475           setColor(sib, colorOf(parentOf(x)));
476           setColor(parentOf(x), COLOR.BLACK);
477           setColor(leftOf(sib), COLOR.BLACK);
478           rotateRight(parentOf(x));
479           x = _root;
480         }
481       }
482     }
483 
484     setColor(x, COLOR.BLACK);
485   }
486 
487   ///
488   /// Navigation methods
489   ///
490 
491   function getFirstEntry() {
492     var p = _root;
493     if (null !== p) {
494       while (null !== p.left) {
495         p = p.left;
496       }
497     }
498     return p;
499   }
500 
501   function getLastEntry() {
502     var p = _root;
503     if (null !== p) {
504       while (null !== p.right) {
505         p = p.right;
506       }
507     }
508     return p;
509   }
510 
511   function successor(p) {
512     var t, ch;
513     if (null === p) {
514       return null;
515     } else if (null !== p.right) {
516       t = p.right;
517       while (null !== t.left) {
518         t = t.left;
519       }
520       return t;
521     } else {
522       t = p.parent;
523       ch = p;
524       while (null !== t && ch === t.right) {
525         ch = t;
526         t = t.parent;
527       }
528       return t;
529     }
530   }
531 
532   function predecessor(p) {
533     var t, ch;
534     if (null === p) {
535       return null;
536     } else if (null !== p.left) {
537       t = p.right;
538       while (null !== t.right) {
539         t = t.right;
540       }
541       return t;
542     } else {
543       t = p.parent;
544       ch = p;
545       while (null !== t && ch === t.left) {
546         ch = t;
547         t = t.parent;
548       }
549       return t;
550     }
551   }
552 
553   function exportEntry(p) {
554     if (null !== p) {
555       return new Map.Entry(p.key, p.value);
556     } else {
557       return null;
558     }
559   }
560 
561   ///
562   /// Privileged methods
563   ///
564 
565   /**
566    * Return the number of key-value mapping in this map
567    * 
568    * @return number of key-value mapping
569    */
570   this.size = size;
571 
572   /**
573    * Associate the specified value with the specified key in this map.
574    * If the map previously contained a mapping entry for the key, the old value is replaced.
575    * 
576    * @param {object} key key with which the specified value is to be associated
577    * @param {object} value value to be associated with the specified key
578    *
579    * @return the previous value associated with key
580    */
581   this.put = put;
582 
583   /**
584    * Removes the mapping entry for this key from this map if present.
585    * 
586    * @param {Object} key key for which mapping should be removed
587    *
588    * @return the previous value associated with key, or null if there was no mapping entry for this key.
589    */
590   this.remove = function(key) {
591     var p = getEntry(key);
592     if (null === p) {
593       return p;
594     }
595 
596     var oldValue = p.value;
597     deleteEntry(p);
598     return oldValue;
599   };
600 
601   /**
602    * Remove all the mapping entries from this map.
603    * @return void
604    */
605   this.clear = clear;
606 
607   /**
608    * Returns true if this map contains a mapping entry for the given key
609    * 
610    * @param {Object} key whose presence in this map is to be tested
611    *
612    * @return true if this map contains a mapping entry for the given key
613    */
614   this.containsKey = function(key) {
615     return getEntry(key) !== null;
616   };
617 
618   /**
619    * Get the value to which the specified key is mapped,
620    * or null if this map contains no mapping for the key
621    * 
622    * @param {Object} key the query key
623    *
624    * @return the value or null
625    */
626   this.get = function(key) {
627     var p = getEntry(key);
628     return null === p ? null : p.value;
629   };
630 
631   /**
632    * Get the first key in this map
633    * @return the first key
634    */
635   this.firstKey = function() {
636     var e = getFirstEntry();
637     return null === e ? null : e.key;
638   };
639 
640   /**
641    * Get the last key in this map
642    * @return the last key
643    */
644   this.lastKey = function() {
645     var e = getLastEntry();
646     return null === e ? null : e.key;
647   };
648 
649   /**
650    * Get the first entry in this map
651    * @return the first entry
652    */
653   this.firstEntry = function() {
654     return exportEntry(getFirstEntry());
655   };
656 
657   /**
658    * Get the last entry in this map
659    * @return the last entry
660    */
661   this.lastEntry = function() {
662     return exportEntry(getLastEntry());
663   };
664  
665   /**
666    * Get the first entry and remove it
667    * @return the first entry
668    */
669   this.pollFirstEntry = function() {
670     var p = getFirstEntry();
671     var res = exportEntry(p);
672     if (null !== p) {
673       deleteEntry(p);
674     }
675     return res;
676   };
677 
678   /**
679    * Get the last entry and remove it
680    * @return the last entry
681    */
682   this.pollLastEntry = function() {
683     var p = getLastEntry();
684     var res = exportEntry(p);
685     if (null !== p) {
686       deleteEntry(p);
687     }
688     return res;
689   };
690 
691   /**
692    * Get the largest entry whose key is lower than the specified key.
693    * 
694    * @param {Object} key the specified key
695    *
696    * @return lower entry
697    */
698   this.lowerEntry = function(key) {
699     return exportEntry(getLowerEntry(key));
700   };
701 
702   /**
703    * Get the largest entry's whose key is lower than the specified key.
704    * 
705    * @param {Object} key the specfied key
706    *
707    * @return lower key
708    */
709   this.lowerKey = function(key) {
710     var e = getLowerEntry(key);
711     return null === e ? null : e.key;
712   };
713 
714 };
715 
716 exports.TreeMap = TreeMap;
717