a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
38cbfe2006-04-24Robert Hinn //! This class implements a (min-)heap. The value of a child node will //! always be greater than or equal to the value of its parent node. //! Thus, the top node of the heap will always hold the smallest value.
811dec2015-09-10Henrik Grubbström (Grubba) //! Heap element. class Element (mixed value) {
fed76a2015-11-17Henrik Grubbström (Grubba)  int pos = -1;
811dec2015-09-10Henrik Grubbström (Grubba)  constant is_adt_heap_element = 1; protected int `<(mixed other) { return value < other; } protected int `>(mixed other) { return value > other; }
a2ca3c2016-02-09Henrik Grubbström (Grubba)  protected string _sprintf(int c) { if (c != 'O') return "ADT.Heap()->Element()"; return sprintf("ADT.Heap()->Element(%O)[%d]", value, pos); }
811dec2015-09-10Henrik Grubbström (Grubba) } #define SWAP(X,Y) do{ mixed tmp=values[X]; (values[X]=values[Y])->pos = X; (values[Y]=tmp)->pos = Y; }while(0)
b78e3a1999-12-30Fredrik Hübinette (Hubbe) 
811dec2015-09-10Henrik Grubbström (Grubba) protected array(Element) values=allocate(10);
7ee5de2009-10-08Henrik Grubbström (Grubba) protected int num_values;
b78e3a1999-12-30Fredrik Hübinette (Hubbe) 
09c8a02009-09-17Henrik Grubbström (Grubba) #ifdef ADT_HEAP_DEBUG
b78e3a1999-12-30Fredrik Hübinette (Hubbe) void verify_heap() {
811dec2015-09-10Henrik Grubbström (Grubba)  for(int e=0; e<num_values; e++) { if (!values[e] || !values[e]->is_adt_heap_element) error("Error in HEAP: Position %d has no element.\n", e); if (values[e]->pos != e) error("Error in HEAP: Element %d has invalid position: %d.\n", e, values[e]->pos);
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(values[(e-1)/2] > values[e])
ab3b6d2003-05-03Martin Nilsson  error("Error in HEAP (%d, %d) num_values=%d\n", (e-1)/2, e, num_values);
811dec2015-09-10Henrik Grubbström (Grubba)  }
b78e3a1999-12-30Fredrik Hübinette (Hubbe) } #else #define verify_heap() #endif
9eaf1d2008-06-28Martin Nilsson protected void adjust_down(int elem)
b78e3a1999-12-30Fredrik Hübinette (Hubbe) { while(1) { int child=elem*2+1; if(child >= num_values) break;
82e7902004-06-21Martin Nilsson 
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(child+1==num_values || values[child] < values[child+1]) { if(values[child] < values[elem]) { SWAP(child, elem); elem=child; continue; } } else { if(child+1 >= num_values) break;
82e7902004-06-21Martin Nilsson 
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(values[child+1] < values[elem]) { SWAP(elem, child+1); elem=child+1; continue; } } break; } }
9eaf1d2008-06-28Martin Nilsson protected int adjust_up(int elem)
b78e3a1999-12-30Fredrik Hübinette (Hubbe) { int parent=(elem-1)/2;
4720a61999-12-30Fredrik Hübinette (Hubbe) 
af00351999-12-31Fredrik Hübinette (Hubbe)  if(elem && values[elem] < values[parent])
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  { SWAP(elem, parent); elem=parent;
af00351999-12-31Fredrik Hübinette (Hubbe)  while(elem && (values[elem] < values[parent=(elem -1)/2]))
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  { SWAP(elem, parent); elem=parent; } adjust_down(elem); return 1; } return 0; }
38cbfe2006-04-24Robert Hinn //! Push an element onto the heap. The heap will automatically sort itself //! so that the smallest value will be at the top.
9955792015-08-13Henrik Grubbström (Grubba) //!
811dec2015-09-10Henrik Grubbström (Grubba) //! @returns //! Returns an element handle, which can be used with //! @[adjust()] and @[remove()]. //!
9955792015-08-13Henrik Grubbström (Grubba) //! @seealso //! @[pop()], @[remove()]
811dec2015-09-10Henrik Grubbström (Grubba) Element push(mixed value)
b78e3a1999-12-30Fredrik Hübinette (Hubbe) {
811dec2015-09-10Henrik Grubbström (Grubba)  Element ret; if (objectp(value) && value->is_adt_heap_element) { ret = value; } else { ret = Element(value); }
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(num_values >= sizeof(values))
4720a61999-12-30Fredrik Hübinette (Hubbe)  values+=allocate(10+sizeof(values)/4);
82e7902004-06-21Martin Nilsson 
811dec2015-09-10Henrik Grubbström (Grubba)  (values[num_values] = ret)->pos = num_values++;
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  adjust_up(num_values-1); verify_heap();
811dec2015-09-10Henrik Grubbström (Grubba)  return ret;
b78e3a1999-12-30Fredrik Hübinette (Hubbe) }
38cbfe2006-04-24Robert Hinn //! Takes a value in the heap and sorts it through the heap to maintain //! its sort criteria (increasing order).
811dec2015-09-10Henrik Grubbström (Grubba) //! //! @param value //! Either the element handle returned by @[push()], or the pushed //! value itself. //! //! @returns //! Returns the element handle for the value (if present in the heap), //! and @expr{0@} (zero) otherwise. Element adjust(mixed value)
b78e3a1999-12-30Fredrik Hübinette (Hubbe) {
811dec2015-09-10Henrik Grubbström (Grubba)  int pos; if (objectp(value) && value->is_adt_heap_element) { pos = value->pos; } else {
6804ca2015-09-17Henrik Grubbström (Grubba)  pos = search(map(values, lambda(Element x) { return x?->value; }), value);
811dec2015-09-10Henrik Grubbström (Grubba)  } Element ret; if(pos>=0) { ret = values[pos];
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(!adjust_up(pos)) adjust_down(pos);
811dec2015-09-10Henrik Grubbström (Grubba)  }
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  verify_heap();
811dec2015-09-10Henrik Grubbström (Grubba)  return ret;
b78e3a1999-12-30Fredrik Hübinette (Hubbe) }
ab3b6d2003-05-03Martin Nilsson //! Removes and returns the item on top of the heap, //! which also is the smallest value in the heap.
9955792015-08-13Henrik Grubbström (Grubba) //! //! @throws //! Throws an error if the heap is empty. //! //! @seealso //! @[peek()], @[push()], @[remove()]
ab3b6d2003-05-03Martin Nilsson mixed pop()
b78e3a1999-12-30Fredrik Hübinette (Hubbe) { if(!num_values) error("Heap underflow!\n");
82e7902004-06-21Martin Nilsson 
fed76a2015-11-17Henrik Grubbström (Grubba)  Element value = values[0]; value->pos = -1;
811dec2015-09-10Henrik Grubbström (Grubba)  num_values--; if(num_values)
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  {
811dec2015-09-10Henrik Grubbström (Grubba)  (values[0] = values[num_values])->pos = 0;
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  adjust_down(0);
82e7902004-06-21Martin Nilsson 
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  if(num_values * 3 + 10 < sizeof(values)) values=values[..num_values+10]; }
811dec2015-09-10Henrik Grubbström (Grubba)  values[num_values]=0;
b78e3a1999-12-30Fredrik Hübinette (Hubbe)  verify_heap();
fed76a2015-11-17Henrik Grubbström (Grubba)  return value->value;
b78e3a1999-12-30Fredrik Hübinette (Hubbe) }
ab3b6d2003-05-03Martin Nilsson //! Returns the number of elements in the heap. int _sizeof() { return num_values; }
b78e3a1999-12-30Fredrik Hübinette (Hubbe) 
38cbfe2006-04-24Robert Hinn //! Removes and returns the item on top of the heap, //! which also is the smallest value in the heap.
3847152009-08-20Martin Nilsson //! @deprecated pop
fcf6b02009-08-11Henrik Grubbström (Grubba) __deprecated__ mixed top() { return pop(); }
38cbfe2006-04-24Robert Hinn  //! Returns the number of elements in the heap.
3847152009-08-20Martin Nilsson //! @deprecated lfun::_sizeof
fcf6b02009-08-11Henrik Grubbström (Grubba) __deprecated__ int size() { return _sizeof(); }
38cbfe2006-04-24Robert Hinn 
8dd5fe2016-03-02Henrik Grubbström (Grubba) //! Returns the @[Element] on top of the heap (which is also the one with //! the smallest value in the heap) without removing it. //! //! @returns //! Returns the smallest @[Element] on the heap if any, and //! @expr{UNDEFINED@} otherwise. //! //! @seealso //! @[peek()], @[pop()] Element low_peek() { if (!num_values) return UNDEFINED; return values[0]; }
38cbfe2006-04-24Robert Hinn //! Returns the item on top of the heap (which is also the smallest value //! in the heap) without removing it.
9955792015-08-13Henrik Grubbström (Grubba) //! //! @returns //! Returns the smallest value on the heap if any, and //! @expr{UNDEFINED@} otherwise. //! //! @seealso
8dd5fe2016-03-02Henrik Grubbström (Grubba) //! @[low_peek()], @[pop()]
38cbfe2006-04-24Robert Hinn mixed peek() { if (!num_values) return UNDEFINED;
811dec2015-09-10Henrik Grubbström (Grubba)  return values[0]->value;
38cbfe2006-04-24Robert Hinn }
a6f1022015-08-13Henrik Grubbström (Grubba)  //! Remove a value from the heap. //! //! @param value //! Value to remove. //! //! @seealso //! @[push()], @[pop()] void remove(mixed value) {
811dec2015-09-10Henrik Grubbström (Grubba)  int pos; if (objectp(value) && value->is_adt_heap_element) { pos = value->pos; } else {
6804ca2015-09-17Henrik Grubbström (Grubba)  pos = search(map(values, lambda(Element x) { return x?->value; }), value);
811dec2015-09-10Henrik Grubbström (Grubba)  }
a6f1022015-08-13Henrik Grubbström (Grubba)  if ((pos < 0) || (pos >= num_values)) return;
fed76a2015-11-17Henrik Grubbström (Grubba)  value = values[pos];
a6f1022015-08-13Henrik Grubbström (Grubba)  values[pos] = values[--num_values];
2caef82016-02-26Martin Karlgren  values[pos]->pos = pos;
a6f1022015-08-13Henrik Grubbström (Grubba)  values[num_values] = 0;
fed76a2015-11-17Henrik Grubbström (Grubba)  value->pos = -1;
a6f1022015-08-13Henrik Grubbström (Grubba)  if (pos < num_values) { if (!adjust_up(pos)) adjust_down(pos); }
6732372015-08-13Henrik Grubbström (Grubba)  if(num_values * 3 + 10 < sizeof(values)) values=values[..num_values+10];
a6f1022015-08-13Henrik Grubbström (Grubba)  verify_heap(); }
a2ca3c2016-02-09Henrik Grubbström (Grubba)  protected string _sprintf(int c) { if (c != 'O') return "ADT.Heap()"; int cnt = num_values; Element e = sizeof(values) && values[0]; if (cnt) { return sprintf("ADT.Heap(%d elements, top: %O)", cnt, e); } return "ADT.Heap(0 elements)"; }