1 /**
2   *
3   * Copyright:
4   *     Copyright (C) 2008 Chris Wright.
5   *     Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6   *     All rights reserved.
7   *
8   * License:
9   *     Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10   *     See LICENSE_TANGO.txt for details.
11   *
12   * Version: Oct 2008: Initial release
13   *
14   * Authors: Chris Wright, aka dhasenan
15   *
16   */
17 
18 module ocean.util.container.more.Heap;
19 
20 import ocean.core.ExceptionDefinitions;
21 
22 version (unittest) import ocean.core.Test;
23 
24 bool minHeapCompare(T)(T a, T b) {return a <= b;}
25 bool maxHeapCompare(T)(T a, T b) {return a >= b;}
26 void defaultHeapSwap(T)(T t, size_t index) {}
27 
28 /** A heap is a data structure where you can insert items in random order and extract them in sorted order.
29   * Pushing an element into the heap takes O(lg n) and popping the top of the heap takes O(lg n). Heaps are
30   * thus popular for sorting, among other things.
31   *
32   * No opApply is provided, since most people would expect this to return the contents in sorted order,
33   * not do significant heap allocation, not modify the collection, and complete in linear time. This
34   * combination is not possible with a heap.
35   *
36   * Note: always pass by reference when modifying a heap.
37   *
38   * The template arguments to the heap are:
39   *     T       = the element type
40   *     Compare = a function called when ordering elements. Its signature should be bool(T, T).
41   *               see minHeapCompare() and maxHeapCompare() for examples.
42   *     Move    = a function called when swapping elements. Its signature should be void(T, uint).
43   *               The default does nothing, and should suffice for most users. You
44   *               probably want to keep this function small; it's called O(log N)
45   *               times per insertion or removal.
46 */
47 
48 struct Heap (T, alias Compare = minHeapCompare!(T), alias Move = defaultHeapSwap!(T))
49 {
50         import ocean.core.Verify;
51 
52         alias pop       remove;
53 
54         public template opOpAssign ( string op : "~" )
55         {
56             alias opOpAssign = push;
57         }
58 
59         // The actual data.
60         private T[]     heap;
61 
62         // The index of the cell into which the next element will go.
63         private uint    next;
64 
65         /** Inserts the given element into the heap. */
66         void push (T t)
67         {
68                 auto index = next++;
69                 while (heap.length <= index)
70                        heap.length = 2 * heap.length + 32;
71 
72                 heap [index] = t;
73                 Move (t, index);
74                 fixup (index);
75         }
76 
77         /** Inserts all elements in the given array into the heap. */
78         void push (T[] array)
79         {
80                 if (heap.length < next + array.length)
81                         heap.length = next + array.length + 32;
82 
83                 foreach (t; array) push (t);
84         }
85 
86         /** Removes the top of this heap and returns it. */
87         T pop ()
88         {
89                 return removeAt (0);
90         }
91 
92         /** Remove the every instance that matches the given item. */
93         void removeAll (T t)
94         {
95                 // TODO: this is slower than it could be.
96                 // I am reasonably certain we can do the O(n) scan, but I want to
97                 // look at it a bit more.
98                 while (remove (t)) {}
99         }
100 
101         /** Remove the first instance that matches the given item.
102           * Returns: true iff the item was found, otherwise false. */
103         bool remove (T t)
104         {
105                 foreach (i, a; heap)
106                 {
107                         if (a is t || a == t)
108                         {
109                                 removeAt (i);
110                                 return true;
111                         }
112                 }
113                 return false;
114         }
115 
116         /** Remove the element at the given index from the heap.
117           * The index is according to the heap's internal layout; you are
118           * responsible for making sure the index is correct.
119           * The heap invariant is maintained. */
120         T removeAt (size_t index)
121         {
122                 if (next <= index)
123                 {
124                         throw new NoSuchElementException ("Heap :: tried to remove an"
125                                 ~ " element with index greater than the size of the heap "
126                                 ~ "(did you call pop() from an empty heap?)");
127                 }
128                 next--;
129                 auto t = heap[index];
130                 // if next == index, then we have nothing valid on the heap
131                 // so popping does nothing but change the length
132                 // the other calls are irrelevant, but we surely don't want to
133                 // call Move with invalid data
134                 if (next > index)
135                 {
136                         heap[index] = heap[next];
137                         Move(heap[index], index);
138                         fixdown(index);
139 
140                         // added via ticket 1885 (kudos to wolfwood)
141                         if (heap[index] is heap[next])
142                             fixup(index);
143                 }
144                 return t;
145         }
146 
147         /** Gets the value at the top of the heap without removing it. */
148         T peek ()
149         {
150                 verify (next > 0);
151                 return heap[0];
152         }
153 
154         /** Returns the number of elements in this heap. */
155         size_t size ()
156         {
157                 return next;
158         }
159 
160         /** Reset this heap. */
161         void clear ()
162         {
163                 next = 0;
164         }
165 
166         /** reset this heap, and use the provided host for value elements */
167         void clear (T[] host)
168         {
169                 this.heap = host;
170                 clear;
171         }
172 
173         /** Get the reserved capacity of this heap. */
174         size_t capacity ()
175         {
176                 return heap.length;
177         }
178 
179         /** Reserve enough space in this heap for value elements. The reserved space is truncated or extended as necessary. If the value is less than the number of elements already in the heap, throw an exception. */
180         uint capacity (uint value)
181         {
182                 if (value < next)
183                 {
184                         throw new IllegalArgumentException ("Heap :: illegal truncation");
185                 }
186                 heap.length = value;
187                 return value;
188         }
189 
190         /** Return a shallow copy of this heap. */
191         Heap clone ()
192         {
193                 Heap other;
194                 other.heap = this.heap.dup;
195                 other.next = this.next;
196                 return other;
197         }
198 
199         // Get the index of the parent for the element at the given index.
200         private size_t parent (size_t index)
201         {
202                 verify (index > 0);
203                 return (index - 1) / 2;
204         }
205 
206         // Having just inserted, restore the heap invariant (that a node's value is greater than its children)
207         private void fixup (size_t index)
208         {
209                 if (index == 0) return;
210                 auto par = parent (index);
211                 if (!Compare(heap[par], heap[index]))
212                 {
213                         swap (par, index);
214                         fixup (par);
215                 }
216         }
217 
218         // Having just removed and replaced the top of the heap with the last inserted element,
219         // restore the heap invariant.
220         private void fixdown (size_t index)
221         {
222                 size_t left = 2 * index + 1;
223                 size_t down;
224                 if (left >= next)
225                 {
226                         return;
227                 }
228 
229                 if (left == next - 1)
230                 {
231                         down = left;
232                 }
233                 else if (Compare (heap[left], heap[left + 1]))
234                 {
235                         down = left;
236                 }
237                 else
238                 {
239                         down = left + 1;
240                 }
241 
242                 if (!Compare(heap[index], heap[down]))
243                 {
244                         swap (index, down);
245                         fixdown (down);
246                 }
247         }
248 
249         // Swap two elements in the array.
250         private void swap (size_t a, size_t b)
251         {
252                 auto t1 = heap[a];
253                 auto t2 = heap[b];
254                 heap[a] = t2;
255                 Move(t2, a);
256                 heap[b] = t1;
257                 Move(t1, b);
258         }
259 }
260 
261 
262 /** A minheap implementation. This will have the smallest item as the top of the heap.
263   *
264   * Note: always pass by reference when modifying a heap.
265   *
266 */
267 
268 template MinHeap(T)
269 {
270         alias Heap!(T, minHeapCompare) MinHeap;
271 }
272 
273 /** A maxheap implementation. This will have the largest item as the top of the heap.
274   *
275   * Note: always pass by reference when modifying a heap.
276   *
277 */
278 
279 template MaxHeap(T)
280 {
281         alias Heap!(T, maxHeapCompare) MaxHeap;
282 }
283 
284 
285 
286 unittest
287 {
288         MinHeap!(size_t) h;
289         test (h.size is 0);
290         h ~= 1;
291         h ~= 3;
292         h ~= 2;
293         h ~= 4;
294         test (h.size is 4);
295 
296         test (h.peek is 1);
297         test (h.peek is 1);
298         test (h.size is 4);
299         h.pop;
300         test (h.peek is 2);
301         test (h.size is 3);
302 }
303 
304 unittest
305 {
306         MinHeap!(size_t) h;
307         test (h.size is 0);
308         h ~= 1;
309         h ~= 3;
310         h ~= 2;
311         h ~= 4;
312         test (h.size is 4);
313 
314         test (h.pop is 1);
315         test (h.size is 3);
316         test (h.pop is 2);
317         test (h.size is 2);
318         test (h.pop is 3);
319         test (h.size is 1);
320         test (h.pop is 4);
321         test (h.size is 0);
322 }
323 
324 unittest
325 {
326         MaxHeap!(size_t) h;
327         h ~= 1;
328         h ~= 3;
329         h ~= 2;
330         h ~= 4;
331 
332         test (h.pop is 4);
333         test (h.pop is 3);
334         test (h.pop is 2);
335         test (h.pop is 1);
336 }
337 
338 unittest
339 {
340         MaxHeap!(size_t) h;
341         h ~= 1;
342         h ~= 3;
343         h ~= 2;
344         h ~= 4;
345         h.remove(3);
346         test (h.pop is 4);
347         test (h.pop is 2);
348         test (h.pop is 1);
349         test (h.size == 0);
350 }
351 
352 version (unittest)
353 {
354     long[] swapped;
355     size_t[] indices;
356     void onMove(long a, size_t b)
357     {
358             swapped ~= a;
359             indices ~= b;
360     }
361 }
362 
363 unittest
364 {
365         // this tests that onMove is called with fixdown
366         swapped = null;
367         indices = null;
368         Heap!(long, minHeapCompare, onMove) h;
369         // no swap
370         h ~= 1;
371         // no swap
372         h ~= 3;
373 
374         // onMove() is called for all insertions
375         swapped = null;
376         indices = null;
377         // pop: you replace the top with the last and
378         // percolate down. So you have to swap once when
379         // popping at a minimum, and that's if you have only two
380         // items in the heap.
381         test (h.pop is 1);
382         test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
383         test (swapped[0] == 3);
384         test (indices[0] == 0);
385         test (h.pop is 3);
386         test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
387 }
388 unittest
389 {
390         // this tests that onMove is called with fixup
391         swapped = null;
392         indices = null;
393         Heap!(long, minHeapCompare, onMove) h;
394         // no swap
395         h ~= 1;
396         // no swap
397         h ~= 3;
398         // swap: move 0 to position 0, 1 to position 2
399         h ~= 0;
400         int n=3; // onMove() called for insertions too
401         if (swapped[n] == 0)
402         {
403                 test (swapped[n+1] == 1);
404                 test (indices[n+0] == 0);
405                 test (indices[n+1] == 2);
406         }
407         else
408         {
409                 test (swapped[n+1] == 0);
410                 test (swapped[n+0] == 1);
411                 test (indices[n+0] == 2);
412                 test (indices[n+1] == 0);
413         }
414 }
415 
416 unittest
417 {
418         MaxHeap!(uint) h;
419         h ~= 1;
420         h ~= 3;
421         h ~= 2;
422         h ~= 4;
423         auto other = h.clone;
424 
425         test (other.pop is 4);
426         test (other.pop is 3);
427         test (other.pop is 2);
428         test (other.pop is 1);
429         test (h.size is 4, "cloned heap shares data with original heap");
430         test (h.pop is 4, "cloned heap shares data with original heap");
431         test (h.pop is 3, "cloned heap shares data with original heap");
432         test (h.pop is 2, "cloned heap shares data with original heap");
433         test (h.pop is 1, "cloned heap shares data with original heap");
434 }