1 /*******************************************************************************
2 
3         Based upon Doug Lea's Java collection package
4 
5         Copyright:
6             Copyright (c) 2008 Kris Bell.
7             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
8             All rights reserved.
9 
10         License:
11             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
12             See LICENSE_TANGO.txt for details.
13 
14         Version: Apr 2008: Initial release
15 
16         Authors: Kris
17 
18 *******************************************************************************/
19 
20 module ocean.util.container.Slink;
21 
22 import ocean.core.Enforce;
23 
24 import ocean.meta.types.Qualifiers;
25 import ocean.meta.types.Typedef;
26 
27 import ocean.util.container.model.IContainer;
28 
29 /*******************************************************************************
30 
31         Slink instances provide standard linked list next-fields, and
32         support standard operations upon them. Slink structures are pure
33         implementation tools, and perform no argument checking, no result
34         screening, and no synchronization. They rely on user-level classes
35         (see HashSet, for example) to do such things.
36 
37         Still, Slink is made `public' so that you can use it to build other
38         kinds of containers
39 
40         Note that when K is specified, support for keys are enabled. When
41         Identity is stipulated as 'true', those keys are compared using an
42         identity-comparison instead of equality (using 'is'). Similarly, if
43         HashCache is set true, an additional attribute is create in order to
44         retain the hash of K
45 
46 *******************************************************************************/
47 
48 private
49 {
50     mixin(Typedef!(int, "KeyDummy"));
51 }
52 
53 struct Slink (V, K=KeyDummy, bool Identity = false, bool HashCache = false)
54 {
55         alias Slink!(V, K, Identity, HashCache) Type;
56         alias Type                              *Ref;
57         alias Compare!(V)                       Comparator;
58 
59         Ref             next;           // pointer to next
60         V               value;          // element value
61 
62         static if (HashCache == true)
63         {
64         hash_t       cache;             // retain hash value?
65         }
66 
67         /***********************************************************************
68 
69                 add support for keys also?
70 
71         ***********************************************************************/
72 
73         static if (!is(typeof(K) == KeyDummy))
74         {
75                 K key;
76 
77                 final Ref set (K k, V v, Ref n)
78                 {
79                         key = k;
80                         return set (v, n);
81                 }
82 
83                 final hash_t hash()
84                 {
85                         return typeid(K).getHash(&key);
86                 }
87 
88                 final Ref findKey (K key)
89                 {
90                         static if (Identity == true)
91                         {
92                            for (auto p= &this; p; p = p.next)
93                                 if (key is p.key)
94                                     return p;
95                         }
96                         else
97                         {
98                            for (auto p= &this; p; p = p.next)
99                                 if (key == p.key)
100                                     return p;
101                         }
102                         return null;
103                 }
104 
105                 final Ref findPair (K key, V value)
106                 {
107                         static if (Identity == true)
108                         {
109                            for (auto p= &this; p; p = p.next)
110                                 if (key is p.key && value == p.value)
111                                     return p;
112                         }
113                         else
114                         {
115                            for (auto p= &this; p; p = p.next)
116                                 if (key == p.key && value == p.value)
117                                     return p;
118                         }
119                         return null;
120                 }
121 
122                 final int indexKey (K key)
123                 {
124                         int i = 0;
125                         static if (Identity == true)
126                         {
127                            for (auto p= &this; p; p = p.next, ++i)
128                                 if (key is p.key)
129                                     return i;
130                         }
131                         else
132                         {
133                            for (auto p= &this; p; p = p.next, ++i)
134                                 if (key == p.key)
135                                     return i;
136                         }
137                         return -1;
138                 }
139 
140                 final int indexPair (K key, V value)
141                 {
142                         int i = 0;
143                         static if (Identity == true)
144                         {
145                            for (auto p= &this; p; p = p.next, ++i)
146                                 if (key is p.key && value == p.value)
147                                     return i;
148                         }
149                         else
150                         {
151                            for (auto p= &this; p; p = p.next, ++i)
152                                 if (key == p.key && value == p.value)
153                                     return i;
154                         }
155                         return -1;
156                 }
157 
158                 final int countKey (K key)
159                 {
160                         int c = 0;
161                         static if (Identity == true)
162                         {
163                            for (auto p= &this; p; p = p.next)
164                                 if (key is p.key)
165                                     ++c;
166                         }
167                         else
168                         {
169                            for (auto p= &this; p; p = p.next)
170                                 if (key == p.key)
171                                     ++c;
172                         }
173                         return c;
174                 }
175 
176                 final int countPair (K key, V value)
177                 {
178                         int c = 0;
179                         static if (Identity == true)
180                         {
181                            for (auto p= &this; p; p = p.next)
182                                 if (key is p.key && value == p.value)
183                                     ++c;
184                         }
185                         else
186                         {
187                            for (auto p= &this; p; p = p.next)
188                                 if (key == p.key && value == p.value)
189                                     ++c;
190                         }
191                         return c;
192                 }
193         }
194 
195         /***********************************************************************
196 
197                  Set to point to n as next cell
198 
199                  param: n, the new next cell
200 
201         ***********************************************************************/
202 
203         final Ref set (V v, Ref n)
204         {
205                 next = n;
206                 value = v;
207                 return &this;
208         }
209 
210         /***********************************************************************
211 
212                  Splice in p between current cell and whatever it was
213                  previously pointing to
214 
215                  param: p, the cell to splice
216 
217         ***********************************************************************/
218 
219         final void attach (Ref p)
220         {
221                 if (p)
222                     p.next = next;
223                 next = p;
224         }
225 
226         /***********************************************************************
227 
228                 Cause current cell to skip over the current next() one,
229                 effectively removing the next element from the list
230 
231         ***********************************************************************/
232 
233         final void detachNext()
234         {
235                 if (next)
236                     next = next.next;
237         }
238 
239         /***********************************************************************
240 
241                  Linear search down the list looking for element
242 
243                  param: element to look for
244                  Returns: the cell containing element, or null if no such
245 
246         ***********************************************************************/
247 
248         final Ref find (V element)
249         {
250                 for (auto p = &this; p; p = p.next)
251                      if (element == p.value)
252                          return p;
253                 return null;
254         }
255 
256         /***********************************************************************
257 
258                 Return the number of cells traversed to find first occurrence
259                 of a cell with element() element, or -1 if not present
260 
261         ***********************************************************************/
262 
263         final int index (V element)
264         {
265                 int i;
266                 for (auto p = &this; p; p = p.next, ++i)
267                      if (element == p.value)
268                          return i;
269 
270                 return -1;
271         }
272 
273         /***********************************************************************
274 
275                 Count the number of occurrences of element in list
276 
277         ***********************************************************************/
278 
279         final int count (V element)
280         {
281                 int c;
282                 for (auto p = &this; p; p = p.next)
283                      if (element == p.value)
284                          ++c;
285                 return c;
286         }
287 
288         /***********************************************************************
289 
290                  Return the number of cells in the list
291 
292         ***********************************************************************/
293 
294         final int count ()
295         {
296                 int c;
297                 for (auto p = &this; p; p = p.next)
298                      ++c;
299                 return c;
300         }
301 
302         /***********************************************************************
303 
304                 Return the cell representing the last element of the list
305                 (i.e., the one whose next() is null
306 
307         ***********************************************************************/
308 
309         final Ref tail ()
310         {
311                 auto p = &this;
312                 while (p.next)
313                        p = p.next;
314                 return p;
315         }
316 
317         /***********************************************************************
318 
319                 Return the nth cell of the list, or null if no such
320 
321         ***********************************************************************/
322 
323         final Ref nth (long n)
324         {
325                 enforce(n >= 0);
326 
327                 auto p = &this;
328                 for (long i; i < n && p; ++i)
329                      p = p.next;
330                 return p;
331         }
332 
333         /***********************************************************************
334 
335                 Make a copy of the list; i.e., a new list containing new cells
336                 but including the same elements in the same order
337 
338         ***********************************************************************/
339 
340         final Ref copy (scope Ref delegate() alloc)
341         {
342                 auto newlist = dup (alloc);
343                 auto current = newlist;
344 
345                 for (auto p = next; p; p = p.next)
346                     {
347                     current.next = p.dup (alloc);
348                     current = current.next;
349                     }
350                 current.next = null;
351                 return newlist;
352         }
353 
354         /***********************************************************************
355 
356                 dup is shallow; i.e., just makes a copy of the current cell
357 
358         ***********************************************************************/
359 
360         private Ref dup (scope Ref delegate() alloc)
361         {
362                 auto ret = alloc();
363                 static if (is(typeof(K) == KeyDummy))
364                            ret.set (value, next);
365                        else
366                           ret.set (key, value, next);
367                 return ret;
368         }
369 
370         /***********************************************************************
371 
372                 Basic linkedlist merge algorithm.
373                 Merges the lists head by fst and snd with respect to cmp
374 
375                 param: fst head of the first list
376                 param: snd head of the second list
377                 param: cmp a Comparator used to compare elements
378                 Returns: the merged ordered list
379 
380         ***********************************************************************/
381 
382         static Ref merge (Ref fst, Ref snd, Comparator cmp)
383         {
384                 auto a = fst;
385                 auto b = snd;
386                 Ref hd = null;
387                 Ref current = null;
388 
389                 for (;;)
390                     {
391                     if (a is null)
392                        {
393                        if (hd is null)
394                            hd = b;
395                        else
396                           current.next = b;
397                        return hd;
398                        }
399                     else
400                        if (b is null)
401                           {
402                           if (hd is null)
403                               hd = a;
404                           else
405                              current.next = a;
406                           return hd;
407                           }
408 
409                     int diff = cmp (a.value, b.value);
410                     if (diff <= 0)
411                        {
412                        if (hd is null)
413                            hd = a;
414                        else
415                           current.next = a;
416                        current = a;
417                        a = a.next;
418                        }
419                     else
420                        {
421                        if (hd is null)
422                            hd = b;
423                        else
424                           current.next = b;
425                        current = b;
426                        b = b.next;
427                        }
428                     }
429         }
430 
431         /***********************************************************************
432 
433                 Standard list splitter, used by sort.
434                 Splits the list in half. Returns the head of the second half
435 
436                 param: s the head of the list
437                 Returns: the head of the second half
438 
439         ***********************************************************************/
440 
441         static Ref split (Ref s)
442         {
443                 auto fast = s;
444                 auto slow = s;
445 
446                 if (fast is null || fast.next is null)
447                     return null;
448 
449                 while (fast)
450                       {
451                       fast = fast.next;
452                       if (fast && fast.next)
453                          {
454                          fast = fast.next;
455                          slow = slow.next;
456                          }
457                       }
458 
459                 auto r = slow.next;
460                 slow.next = null;
461                 return r;
462 
463         }
464 
465         /***********************************************************************
466 
467                  Standard merge sort algorithm
468 
469                  param: s the list to sort
470                  param: cmp, the comparator to use for ordering
471                  Returns: the head of the sorted list
472 
473         ***********************************************************************/
474 
475         static Ref sort (Ref s, Comparator cmp)
476         {
477                 if (s is null || s.next is null)
478                     return s;
479                 else
480                    {
481                    auto right = split (s);
482                    auto left = s;
483                    left = sort (left, cmp);
484                    right = sort (right, cmp);
485                    return merge (left, right, cmp);
486                    }
487         }
488 
489 }
490 
491 version (unittest)
492 {
493     import ocean.core.Test : NamedTest;
494 }
495 
496 unittest
497 {
498     auto t = new NamedTest("Test nth() with an index out of bounds");
499 
500     static immutable total_items = 11;
501     static immutable index_out_of_bounds = total_items * 2;
502 
503     auto slink = new Slink!(int);
504     t.test!("==")(slink.nth(0).value, 0);
505     t.test!("==")(slink.count(), 1);
506 
507     auto tail = slink;
508     for (int i = 1; i < total_items; ++i)
509     {
510         auto item = new Slink!(int);
511         item.value = tail.value + 1;
512         tail.set(tail.value, item);
513         tail = item;
514     }
515 
516     // Self-verification
517     t.test!("==")(slink.count(), total_items);
518     for (int i = 0; i < total_items; ++i)
519     {
520         t.test!("==")(slink.nth(i).value, i);
521     }
522 
523     t.test!("==")(slink.nth(total_items), null);
524     t.test!("==")(slink.nth(index_out_of_bounds), null);
525 }