heap.js
  1  // Generated by CoffeeScript 1.8.0
  2  (function() {
  3    var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
  4  
  5    floor = Math.floor, min = Math.min;
  6  
  7  
  8    /*
  9    Default comparison function to be used
 10     */
 11  
 12    defaultCmp = function(x, y) {
 13      if (x < y) {
 14        return -1;
 15      }
 16      if (x > y) {
 17        return 1;
 18      }
 19      return 0;
 20    };
 21  
 22  
 23    /*
 24    Insert item x in list a, and keep it sorted assuming a is sorted.
 25    
 26    If x is already in a, insert it to the right of the rightmost x.
 27    
 28    Optional args lo (default 0) and hi (default a.length) bound the slice
 29    of a to be searched.
 30     */
 31  
 32    insort = function(a, x, lo, hi, cmp) {
 33      var mid;
 34      if (lo == null) {
 35        lo = 0;
 36      }
 37      if (cmp == null) {
 38        cmp = defaultCmp;
 39      }
 40      if (lo < 0) {
 41        throw new Error('lo must be non-negative');
 42      }
 43      if (hi == null) {
 44        hi = a.length;
 45      }
 46      while (lo < hi) {
 47        mid = floor((lo + hi) / 2);
 48        if (cmp(x, a[mid]) < 0) {
 49          hi = mid;
 50        } else {
 51          lo = mid + 1;
 52        }
 53      }
 54      return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
 55    };
 56  
 57  
 58    /*
 59    Push item onto heap, maintaining the heap invariant.
 60     */
 61  
 62    heappush = function(array, item, cmp) {
 63      if (cmp == null) {
 64        cmp = defaultCmp;
 65      }
 66      array.push(item);
 67      return _siftdown(array, 0, array.length - 1, cmp);
 68    };
 69  
 70  
 71    /*
 72    Pop the smallest item off the heap, maintaining the heap invariant.
 73     */
 74  
 75    heappop = function(array, cmp) {
 76      var lastelt, returnitem;
 77      if (cmp == null) {
 78        cmp = defaultCmp;
 79      }
 80      lastelt = array.pop();
 81      if (array.length) {
 82        returnitem = array[0];
 83        array[0] = lastelt;
 84        _siftup(array, 0, cmp);
 85      } else {
 86        returnitem = lastelt;
 87      }
 88      return returnitem;
 89    };
 90  
 91  
 92    /*
 93    Pop and return the current smallest value, and add the new item.
 94    
 95    This is more efficient than heappop() followed by heappush(), and can be
 96    more appropriate when using a fixed size heap. Note that the value
 97    returned may be larger than item! That constrains reasonable use of
 98    this routine unless written as part of a conditional replacement:
 99        if item > array[0]
100          item = heapreplace(array, item)
101     */
102  
103    heapreplace = function(array, item, cmp) {
104      var returnitem;
105      if (cmp == null) {
106        cmp = defaultCmp;
107      }
108      returnitem = array[0];
109      array[0] = item;
110      _siftup(array, 0, cmp);
111      return returnitem;
112    };
113  
114  
115    /*
116    Fast version of a heappush followed by a heappop.
117     */
118  
119    heappushpop = function(array, item, cmp) {
120      var _ref;
121      if (cmp == null) {
122        cmp = defaultCmp;
123      }
124      if (array.length && cmp(array[0], item) < 0) {
125        _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
126        _siftup(array, 0, cmp);
127      }
128      return item;
129    };
130  
131  
132    /*
133    Transform list into a heap, in-place, in O(array.length) time.
134     */
135  
136    heapify = function(array, cmp) {
137      var i, _i, _j, _len, _ref, _ref1, _results, _results1;
138      if (cmp == null) {
139        cmp = defaultCmp;
140      }
141      _ref1 = (function() {
142        _results1 = [];
143        for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
144        return _results1;
145      }).apply(this).reverse();
146      _results = [];
147      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
148        i = _ref1[_i];
149        _results.push(_siftup(array, i, cmp));
150      }
151      return _results;
152    };
153  
154  
155    /*
156    Update the position of the given item in the heap.
157    This function should be called every time the item is being modified.
158     */
159  
160    updateItem = function(array, item, cmp) {
161      var pos;
162      if (cmp == null) {
163        cmp = defaultCmp;
164      }
165      pos = array.indexOf(item);
166      if (pos === -1) {
167        return;
168      }
169      _siftdown(array, 0, pos, cmp);
170      return _siftup(array, pos, cmp);
171    };
172  
173  
174    /*
175    Find the n largest elements in a dataset.
176     */
177  
178    nlargest = function(array, n, cmp) {
179      var elem, result, _i, _len, _ref;
180      if (cmp == null) {
181        cmp = defaultCmp;
182      }
183      result = array.slice(0, n);
184      if (!result.length) {
185        return result;
186      }
187      heapify(result, cmp);
188      _ref = array.slice(n);
189      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
190        elem = _ref[_i];
191        heappushpop(result, elem, cmp);
192      }
193      return result.sort(cmp).reverse();
194    };
195  
196  
197    /*
198    Find the n smallest elements in a dataset.
199     */
200  
201    nsmallest = function(array, n, cmp) {
202      var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
203      if (cmp == null) {
204        cmp = defaultCmp;
205      }
206      if (n * 10 <= array.length) {
207        result = array.slice(0, n).sort(cmp);
208        if (!result.length) {
209          return result;
210        }
211        los = result[result.length - 1];
212        _ref = array.slice(n);
213        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
214          elem = _ref[_i];
215          if (cmp(elem, los) < 0) {
216            insort(result, elem, 0, null, cmp);
217            result.pop();
218            los = result[result.length - 1];
219          }
220        }
221        return result;
222      }
223      heapify(array, cmp);
224      _results = [];
225      for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
226        _results.push(heappop(array, cmp));
227      }
228      return _results;
229    };
230  
231    _siftdown = function(array, startpos, pos, cmp) {
232      var newitem, parent, parentpos;
233      if (cmp == null) {
234        cmp = defaultCmp;
235      }
236      newitem = array[pos];
237      while (pos > startpos) {
238        parentpos = (pos - 1) >> 1;
239        parent = array[parentpos];
240        if (cmp(newitem, parent) < 0) {
241          array[pos] = parent;
242          pos = parentpos;
243          continue;
244        }
245        break;
246      }
247      return array[pos] = newitem;
248    };
249  
250    _siftup = function(array, pos, cmp) {
251      var childpos, endpos, newitem, rightpos, startpos;
252      if (cmp == null) {
253        cmp = defaultCmp;
254      }
255      endpos = array.length;
256      startpos = pos;
257      newitem = array[pos];
258      childpos = 2 * pos + 1;
259      while (childpos < endpos) {
260        rightpos = childpos + 1;
261        if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
262          childpos = rightpos;
263        }
264        array[pos] = array[childpos];
265        pos = childpos;
266        childpos = 2 * pos + 1;
267      }
268      array[pos] = newitem;
269      return _siftdown(array, startpos, pos, cmp);
270    };
271  
272    Heap = (function() {
273      Heap.push = heappush;
274  
275      Heap.pop = heappop;
276  
277      Heap.replace = heapreplace;
278  
279      Heap.pushpop = heappushpop;
280  
281      Heap.heapify = heapify;
282  
283      Heap.updateItem = updateItem;
284  
285      Heap.nlargest = nlargest;
286  
287      Heap.nsmallest = nsmallest;
288  
289      function Heap(cmp) {
290        this.cmp = cmp != null ? cmp : defaultCmp;
291        this.nodes = [];
292      }
293  
294      Heap.prototype.push = function(x) {
295        return heappush(this.nodes, x, this.cmp);
296      };
297  
298      Heap.prototype.pop = function() {
299        return heappop(this.nodes, this.cmp);
300      };
301  
302      Heap.prototype.peek = function() {
303        return this.nodes[0];
304      };
305  
306      Heap.prototype.contains = function(x) {
307        return this.nodes.indexOf(x) !== -1;
308      };
309  
310      Heap.prototype.replace = function(x) {
311        return heapreplace(this.nodes, x, this.cmp);
312      };
313  
314      Heap.prototype.pushpop = function(x) {
315        return heappushpop(this.nodes, x, this.cmp);
316      };
317  
318      Heap.prototype.heapify = function() {
319        return heapify(this.nodes, this.cmp);
320      };
321  
322      Heap.prototype.updateItem = function(x) {
323        return updateItem(this.nodes, x, this.cmp);
324      };
325  
326      Heap.prototype.clear = function() {
327        return this.nodes = [];
328      };
329  
330      Heap.prototype.empty = function() {
331        return this.nodes.length === 0;
332      };
333  
334      Heap.prototype.size = function() {
335        return this.nodes.length;
336      };
337  
338      Heap.prototype.clone = function() {
339        var heap;
340        heap = new Heap();
341        heap.nodes = this.nodes.slice(0);
342        return heap;
343      };
344  
345      Heap.prototype.toArray = function() {
346        return this.nodes.slice(0);
347      };
348  
349      Heap.prototype.insert = Heap.prototype.push;
350  
351      Heap.prototype.top = Heap.prototype.peek;
352  
353      Heap.prototype.front = Heap.prototype.peek;
354  
355      Heap.prototype.has = Heap.prototype.contains;
356  
357      Heap.prototype.copy = Heap.prototype.clone;
358  
359      return Heap;
360  
361    })();
362  
363    (function(root, factory) {
364      if (typeof define === 'function' && define.amd) {
365        return define([], factory);
366      } else if (typeof exports === 'object') {
367        return module.exports = factory();
368      } else {
369        return root.Heap = factory();
370      }
371    })(this, function() {
372      return Heap;
373    });
374  
375  }).call(this);