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         alias push      opCatAssign;
54 
55         // The actual data.
56         private T[]     heap;
57 
58         // The index of the cell into which the next element will go.
59         private uint    next;
60 
61         /** Inserts the given element into the heap. */
62         void push (T t)
63         {
64                 auto index = next++;
65                 while (heap.length <= index)
66                        heap.length = 2 * heap.length + 32;
67 
68                 heap [index] = t;
69                 Move (t, index);
70                 fixup (index);
71         }
72 
73         /** Inserts all elements in the given array into the heap. */
74         void push (T[] array)
75         {
76                 if (heap.length < next + array.length)
77                         heap.length = next + array.length + 32;
78 
79                 foreach (t; array) push (t);
80         }
81 
82         /** Removes the top of this heap and returns it. */
83         T pop ()
84         {
85                 return removeAt (0);
86         }
87 
88         /** Remove the every instance that matches the given item. */
89         void removeAll (T t)
90         {
91                 // TODO: this is slower than it could be.
92                 // I am reasonably certain we can do the O(n) scan, but I want to
93                 // look at it a bit more.
94                 while (remove (t)) {}
95         }
96 
97         /** Remove the first instance that matches the given item.
98           * Returns: true iff the item was found, otherwise false. */
99         bool remove (T t)
100         {
101                 foreach (i, a; heap)
102                 {
103                         if (a is t || a == t)
104                         {
105                                 removeAt (i);
106                                 return true;
107                         }
108                 }
109                 return false;
110         }
111 
112         /** Remove the element at the given index from the heap.
113           * The index is according to the heap's internal layout; you are
114           * responsible for making sure the index is correct.
115           * The heap invariant is maintained. */
116         T removeAt (size_t index)
117         {
118                 if (next <= index)
119                 {
120                         throw new NoSuchElementException ("Heap :: tried to remove an"
121                                 ~ " element with index greater than the size of the heap "
122                                 ~ "(did you call pop() from an empty heap?)");
123                 }
124                 next--;
125                 auto t = heap[index];
126                 // if next == index, then we have nothing valid on the heap
127                 // so popping does nothing but change the length
128                 // the other calls are irrelevant, but we surely don't want to
129                 // call Move with invalid data
130                 if (next > index)
131                 {
132                         heap[index] = heap[next];
133                         Move(heap[index], index);
134                         fixdown(index);
135 
136                         // added via ticket 1885 (kudos to wolfwood)
137                         if (heap[index] is heap[next])
138                             fixup(index);
139                 }
140                 return t;
141         }
142 
143         /** Gets the value at the top of the heap without removing it. */
144         T peek ()
145         {
146                 verify (next > 0);
147                 return heap[0];
148         }
149 
150         /** Returns the number of elements in this heap. */
151         size_t size ()
152         {
153                 return next;
154         }
155 
156         /** Reset this heap. */
157         void clear ()
158         {
159                 next = 0;
160         }
161 
162         /** reset this heap, and use the provided host for value elements */
163         void clear (T[] host)
164         {
165                 (&this).heap = host;
166                 clear;
167         }
168 
169         /** Get the reserved capacity of this heap. */
170         size_t capacity ()
171         {
172                 return heap.length;
173         }
174 
175         /** 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. */
176         uint capacity (uint value)
177         {
178                 if (value < next)
179                 {
180                         throw new IllegalArgumentException ("Heap :: illegal truncation");
181                 }
182                 heap.length = value;
183                 return value;
184         }
185 
186         /** Return a shallow copy of this heap. */
187         Heap clone ()
188         {
189                 Heap other;
190                 other.heap = (&this).heap.dup;
191                 other.next = (&this).next;
192                 return other;
193         }
194 
195         // Get the index of the parent for the element at the given index.
196         private size_t parent (size_t index)
197         {
198                 verify (index > 0);
199                 return (index - 1) / 2;
200         }
201 
202         // Having just inserted, restore the heap invariant (that a node's value is greater than its children)
203         private void fixup (size_t index)
204         {
205                 if (index == 0) return;
206                 auto par = parent (index);
207                 if (!Compare(heap[par], heap[index]))
208                 {
209                         swap (par, index);
210                         fixup (par);
211                 }
212         }
213 
214         // Having just removed and replaced the top of the heap with the last inserted element,
215         // restore the heap invariant.
216         private void fixdown (size_t index)
217         {
218                 size_t left = 2 * index + 1;
219                 size_t down;
220                 if (left >= next)
221                 {
222                         return;
223                 }
224 
225                 if (left == next - 1)
226                 {
227                         down = left;
228                 }
229                 else if (Compare (heap[left], heap[left + 1]))
230                 {
231                         down = left;
232                 }
233                 else
234                 {
235                         down = left + 1;
236                 }
237 
238                 if (!Compare(heap[index], heap[down]))
239                 {
240                         swap (index, down);
241                         fixdown (down);
242                 }
243         }
244 
245         // Swap two elements in the array.
246         private void swap (size_t a, size_t b)
247         {
248                 auto t1 = heap[a];
249                 auto t2 = heap[b];
250                 heap[a] = t2;
251                 Move(t2, a);
252                 heap[b] = t1;
253                 Move(t1, b);
254         }
255 }
256 
257 
258 /** A minheap implementation. This will have the smallest item as the top of the heap.
259   *
260   * Note: always pass by reference when modifying a heap.
261   *
262 */
263 
264 template MinHeap(T)
265 {
266         alias Heap!(T, minHeapCompare) MinHeap;
267 }
268 
269 /** A maxheap implementation. This will have the largest item as the top of the heap.
270   *
271   * Note: always pass by reference when modifying a heap.
272   *
273 */
274 
275 template MaxHeap(T)
276 {
277         alias Heap!(T, maxHeapCompare) MaxHeap;
278 }
279 
280 
281 
282 unittest
283 {
284         MinHeap!(size_t) h;
285         test (h.size is 0);
286         h ~= 1;
287         h ~= 3;
288         h ~= 2;
289         h ~= 4;
290         test (h.size is 4);
291 
292         test (h.peek is 1);
293         test (h.peek is 1);
294         test (h.size is 4);
295         h.pop;
296         test (h.peek is 2);
297         test (h.size is 3);
298 }
299 
300 unittest
301 {
302         MinHeap!(size_t) h;
303         test (h.size is 0);
304         h ~= 1;
305         h ~= 3;
306         h ~= 2;
307         h ~= 4;
308         test (h.size is 4);
309 
310         test (h.pop is 1);
311         test (h.size is 3);
312         test (h.pop is 2);
313         test (h.size is 2);
314         test (h.pop is 3);
315         test (h.size is 1);
316         test (h.pop is 4);
317         test (h.size is 0);
318 }
319 
320 unittest
321 {
322         MaxHeap!(size_t) h;
323         h ~= 1;
324         h ~= 3;
325         h ~= 2;
326         h ~= 4;
327 
328         test (h.pop is 4);
329         test (h.pop is 3);
330         test (h.pop is 2);
331         test (h.pop is 1);
332 }
333 
334 unittest
335 {
336         MaxHeap!(size_t) h;
337         h ~= 1;
338         h ~= 3;
339         h ~= 2;
340         h ~= 4;
341         h.remove(3);
342         test (h.pop is 4);
343         test (h.pop is 2);
344         test (h.pop is 1);
345         test (h.size == 0);
346 }
347 
348 version (UnitTest)
349 {
350     long[] swapped;
351     size_t[] indices;
352     void onMove(long a, size_t b)
353     {
354             swapped ~= a;
355             indices ~= b;
356     }
357 }
358 
359 unittest
360 {
361         // this tests that onMove is called with fixdown
362         swapped = null;
363         indices = null;
364         Heap!(long, minHeapCompare, onMove) h;
365         // no swap
366         h ~= 1;
367         // no swap
368         h ~= 3;
369 
370         // onMove() is called for all insertions
371         swapped = null;
372         indices = null;
373         // pop: you replace the top with the last and
374         // percolate down. So you have to swap once when
375         // popping at a minimum, and that's if you have only two
376         // items in the heap.
377         test (h.pop is 1);
378         test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
379         test (swapped[0] == 3);
380         test (indices[0] == 0);
381         test (h.pop is 3);
382         test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
383 }
384 unittest
385 {
386         // this tests that onMove is called with fixup
387         swapped = null;
388         indices = null;
389         Heap!(long, minHeapCompare, onMove) h;
390         // no swap
391         h ~= 1;
392         // no swap
393         h ~= 3;
394         // swap: move 0 to position 0, 1 to position 2
395         h ~= 0;
396         int n=3; // onMove() called for insertions too
397         if (swapped[n] == 0)
398         {
399                 test (swapped[n+1] == 1);
400                 test (indices[n+0] == 0);
401                 test (indices[n+1] == 2);
402         }
403         else
404         {
405                 test (swapped[n+1] == 0);
406                 test (swapped[n+0] == 1);
407                 test (indices[n+0] == 2);
408                 test (indices[n+1] == 0);
409         }
410 }
411 
412 unittest
413 {
414         MaxHeap!(uint) h;
415         h ~= 1;
416         h ~= 3;
417         h ~= 2;
418         h ~= 4;
419         auto other = h.clone;
420 
421         test (other.pop is 4);
422         test (other.pop is 3);
423         test (other.pop is 2);
424         test (other.pop is 1);
425         test (h.size is 4, "cloned heap shares data with original heap");
426         test (h.pop is 4, "cloned heap shares data with original heap");
427         test (h.pop is 3, "cloned heap shares data with original heap");
428         test (h.pop is 2, "cloned heap shares data with original heap");
429         test (h.pop is 1, "cloned heap shares data with original heap");
430 }