1 module nxt.fibonacci_heap;
2 
3 struct Node(V)
4 {
5     @safe pure nothrow @nogc:
6 
7     inout(Node*) getPrev() inout { return prev; }
8     inout(Node*) getNext() inout { return next; }
9     inout(Node*) getChild() inout { return child; }
10     inout(Node*) getParent() inout { return parent; }
11 
12     V getValue() const { return value; }
13 
14     bool isMarked() const { return marked; }
15 
16     bool hasChildren() const { return child !is null; }
17     bool hasParent() const { return parent !is null; }
18 private:
19     Node* prev;                 // also called left
20     Node* next;                 // also called right
21     Node* child;
22     Node* parent;
23     int degree;
24     bool marked;                // also called mark
25 
26     V value;
27 };
28 
29 /** Fibonacci Heap Container.
30     See_Also: http://www.codeproject.com/Articles/42561/Dijkstra-s-Algorithm-for-Network-Optimization-Usin
31     See_Also: http://www.sanfoundry.com/cpp-program-implement-fibonacci-heap/
32  */
33 struct FibonacciHeap(V)
34 {
35     @safe pure nothrow:
36 
37     alias N = Node!V;
38 
39 protected:
40     N* root = null;             // root node
41 
42 public:
43 
44     // this()
45     // {
46     //     heap = _empty();
47     // }
48 
49     /*virtual*/
50     ~this() @nogc
51     {
52         if (root)
53         {
54             _deleteAll(root);
55         }
56     }
57 
58     N* insert(V value)
59     {
60         N* ret = _singleton(value);
61         root = _merge(root, ret);
62         return ret;
63     }
64 
65     void merge(FibonacciHeap other)
66     {
67         root = _merge(root, other.root);
68         other.root = _empty();
69     }
70 
71     bool empty() const @nogc
72     {
73         return root is null;
74     }
75 
76     inout(V) minimum() inout @nogc
77     {
78         return root.value;
79     }
80 
81     V removeMinimum()
82     {
83         N* old = root;
84         root = _removeMinimum(root);
85         V ret = old.value;
86         delete old;
87         return ret;
88     }
89 
90     void decreaseKey(N* n, V value)
91     {
92         root = _decreaseKey(root, n, value);
93     }
94 
95     N* find(V value) @nogc
96     {
97         return _find(root, value);
98     }
99 
100 private:
101     N* _empty() @nogc
102     {
103         return null;
104     }
105 
106     N* _singleton(V value)
107     {
108         N* n = new N;           // TODO use std.allocator instead
109         n.value = value;
110         n.prev = n.next = n;
111         n.degree = 0;
112         n.marked = false;
113         n.child = null;
114         n.parent = null;
115         return n;
116     }
117 
118     N* _merge(N* a, N* b) @nogc
119     {
120         if (a is null) return b;
121         if (b is null) return a;
122         if (a.value > b.value)
123         {
124             N* temp = a;
125             a = b;
126             b = temp;
127         }
128         N* an = a.next;
129         N* bp = b.prev;
130         a.next = b;
131         b.prev = a;
132         an.prev = bp;
133         bp.next = an;
134         return a;
135     }
136 
137     void _deleteAll(N* n)
138     {
139         if (n !is null)
140         {
141             N* c = n;
142             do
143             {
144                 N* d = c;
145                 c = c.next;
146                 _deleteAll(d.child);
147                 delete d;       // TODO use std.allocator instead
148             }
149             while (c !is n);
150         }
151     }
152 
153     void _addChild(N* parent, N* child) @nogc
154     {
155         child.prev = child.next = child;
156         child.parent = parent;
157         parent.degree++;
158         parent.child = _merge(parent.child, child);
159     }
160 
161     void _unMarkAndUnParentAll(N* n) @nogc
162     {
163         if (n is null) return;
164         N* c = n;
165         do
166         {
167             c.marked = false;
168             c.parent = null;
169             c = c.next;
170         }
171         while (c !is n);
172     }
173 
174     N* _removeMinimum(N* n)
175     {
176         _unMarkAndUnParentAll(n.child);
177         if (n.next == n)
178         {
179             n = n.child;
180         }
181         else
182         {
183             n.next.prev = n.prev;
184             n.prev.next = n.next;
185             n = _merge(n.next, n.child);
186         }
187 
188         if (n is null) return n;
189         N*[64] trees; // = { null };
190 
191         while (true)
192         {
193             if (trees[n.degree] !is null)
194             {
195                 N* t = trees[n.degree];
196                 if (t == n)
197                     break;
198                 trees[n.degree] = null;
199                 if (n.value < t.value)
200                 {
201                     t.prev.next = t.next;
202                     t.next.prev = t.prev;
203                     _addChild(n, t);
204                 }
205                 else
206                 {
207                     t.prev.next = t.next;
208                     t.next.prev = t.prev;
209                     if (n.next == n)
210                     {
211                         t.next = t.prev = t;
212                         _addChild(t, n);
213                         n = t;
214                     }
215                     else
216                     {
217                         n.prev.next = t;
218                         n.next.prev = t;
219                         t.next = n.next;
220                         t.prev = n.prev;
221                         _addChild(t, n);
222                         n = t;
223                     }
224                 }
225                 continue;
226             }
227             else
228             {
229                 trees[n.degree] = n;
230             }
231             n = n.next;
232         }
233         N* min = n;
234         do
235         {
236             if (n.value < min.value) min = n;
237             n = n.next;
238         }
239         while (n !is n);
240         return min;
241     }
242 
243     N* _cut(N* heap, N* n) @nogc
244     {
245         if (n.next == n)
246         {
247             n.parent.child = null;
248         }
249         else
250         {
251             n.next.prev = n.prev;
252             n.prev.next = n.next;
253             n.parent.child = n.next;
254         }
255         n.next = n.prev = n;
256         n.marked = false;
257         return _merge(heap, n);
258     }
259 
260     N* _decreaseKey(N* heap, N* n, V value) @nogc
261     {
262         if (n.value < value) return heap;
263         n.value = value;
264         if (n.value < n.parent.value)
265         {
266             heap = _cut(heap, n);
267             N* parent = n.parent;
268             n.parent = null;
269 
270             while (parent !is null && parent.marked)
271             {
272                 heap = _cut(heap, parent);
273                 n = parent;
274                 parent = n.parent;
275                 n.parent = null;
276             }
277 
278             if (parent !is null &&
279                 parent.parent !is null)
280             {
281                 parent.marked = true;
282             }
283         }
284         return heap;
285     }
286 
287     N* _find(N* heap, V value) @nogc
288     {
289         N* n = heap;
290 
291         if (n is null)
292             return null;
293 
294         do
295         {
296             if (n.value == value) return n;
297             N* ret = _find(n.child, value);
298             if (ret) return ret;
299             n = n.next;
300         }
301         while (n !is heap);
302 
303         return null;
304     }
305 };
306 
307 void dumpDot(V)(ref FibonacciHeap!V _fh) @safe
308 {
309     import std.stdio: writeln, writefln;
310 
311     writeln(`digraph G {`);
312     if (_fh.root is null)
313     {
314         writeln(`empty;\n}`);
315         return;
316     }
317     writefln(`minimum -> "%x" [constraint = false];`, _fh.root);
318     Node!int* c = _fh.root;
319 
320     do
321     {
322         dumpDotChildren(c);
323         c = c.getNext();
324     }
325     while (c !is _fh.root);
326 
327     writeln(`}`);
328 }
329 
330 void dumpDotChildren(ref Node!int* n) @safe
331 {
332     import std.stdio: writeln, writefln;
333 
334     writefln(`"%x" -> "%x" [constraint = false, arrowhead = lnormal];`, n, n.getNext());
335     writefln(`"%x" -> "%x" [constraint = false, arrowhead = ornormal];`, n, n.getPrev());
336 
337     if (n.isMarked())
338         writefln(`"%x" [style = filled, fillcolor = grey];`, n);
339 
340     if (n.hasParent())
341     {
342         writefln(`"%x" -> "%x" [constraint = false, arrowhead = onormal];`, n, n.getParent());
343     }
344 
345     writefln(`"%x" [label = %d];`, n, n.getValue());
346 
347     if (n.hasChildren())
348     {
349         Node!int* c = n.getChild();
350         do
351         {
352             writefln(`"%x" -> "%x";`, n,c);
353             dumpDotChildren(c);
354             c = c.getNext();
355         }
356         while (c !is n.getChild());
357     }
358 }
359 
360 version(unittest)
361 void generate(ref FibonacciHeap!int h) @safe pure nothrow
362 {
363     h.insert(2);
364     h.insert(3);
365     h.insert(1);
366     h.insert(4);
367 
368     h.removeMinimum();
369     h.removeMinimum();
370 
371     h.insert(5);
372     h.insert(7);
373 
374     h.removeMinimum();
375 
376     h.insert(2);
377 
378     Node!int* nine = h.insert(90);
379 
380     h.removeMinimum();
381     h.removeMinimum();
382     h.removeMinimum();
383 
384     for (int i = 0; i < 20; i += 2)
385         h.insert(30-i);
386     for (int i = 0; i < 4; i++)
387         h.removeMinimum();
388     for (int i = 0; i < 20; i+= 2)
389         h.insert(30-i);
390 
391     h.insert(23);
392 
393     for (int i = 0; i < 7; i++)
394         h.removeMinimum();
395 
396     h.decreaseKey(nine, 1);
397     h.decreaseKey(h.find(28), 2);
398     h.decreaseKey(h.find(23), 3);
399 }
400 
401 ///
402 @safe pure nothrow unittest
403 {
404     FibonacciHeap!int h;
405 
406     h.insert(4); assert(h.minimum == 4);
407     h.insert(3); assert(h.minimum == 3);
408     h.insert(2); assert(h.minimum == 2);
409     h.insert(1); assert(h.minimum == 1);
410 
411     h.removeMinimum; assert(h.minimum == 2);
412     h.removeMinimum; assert(h.minimum == 3);
413 
414     h.insert(0); assert(h.minimum == 0);
415 
416     h.removeMinimum; assert(h.minimum == 3);
417     h.removeMinimum; assert(h.minimum == 4);
418     h.removeMinimum; assert(h.empty);
419 }
420 
421 // Write output to file X and process it with: "dot -O -Tsvg X"
422 @safe pure nothrow unittest
423 {
424     FibonacciHeap!int h;
425     generate(h);
426 }
427 
428 ///
429 @safe unittest
430 {
431     FibonacciHeap!int h;
432     generate(h);
433 
434     h.dumpDot();
435 }