1 /**
2  * Container types
3  *
4  * Authors: Tristan Brice Velloza Kildaire (deavmi)
5  */
6 module niknaks.containers;
7 
8 import core.sync.mutex : Mutex;
9 import std.datetime : Duration, dur;
10 import std.datetime.stopwatch : StopWatch, AutoStart;
11 import core.thread : Thread;
12 import core.sync.condition : Condition;
13 import std.functional : toDelegate;
14 import core.exception : ArrayIndexError;
15 import core.exception : RangeError;
16 import std.string : format;
17 import niknaks.arrays : removeResize;
18 import std.traits : hasMember, hasStaticMember, Parameters, arity, ReturnType, TemplateArgsOf;
19 import std.meta : AliasSeq, staticIndexOf;
20 import niknaks.functional : Optional;
21 
22 private version(unittest)
23 {
24     import std.stdio : writeln;
25 }
26 
27 private version(unittest)
28 {
29     import std.functional : toDelegate;
30 
31     private void DebugTouch(T)(Graph!(T) node)
32     {
33         writeln("Touching graph node ", node);
34     }
35 }
36 
37 private version(unittest)
38 {
39     class DNode
40     {
41         private int x;
42         this(int x)
43         {
44             this.x = x;
45         }
46 
47         public int getTestVal()
48         {
49             return this.x;
50         }
51     }
52 
53     class ClassWithoutSimpleThis
54     {
55         this(int, int)
56         {
57 
58         }
59     }
60 }
61 
62 private version(unittest)
63 {
64     class Thing
65     {
66         this(int)
67         {
68             
69         }
70     }
71 
72     class ArglessThing
73     {
74         this()
75         {
76             
77         }
78     }
79 }
80 
81 private version(unittest)
82 {   
83     struct Person
84     {
85         private static size_t _g = 0;
86         size_t uniq;
87         this(int)
88         {
89             this.uniq = ++_g;
90         }
91     }
92 }
93 
94 /** 
95  * Represents an entry of
96  * some value of type `V`
97  *
98  * Associated with this
99  * is a timer used to
100  * check against for
101  * expiration
102  */
103 private template Entry(V)
104 {
105     /**
106      * The entry type
107      */
108     public struct Entry
109     {
110         private V value;
111         private StopWatch timer;
112 
113         @disable
114         private this();
115 
116         /** 
117          * Creates a new entry
118          * with the given value
119          *
120          * Params:
121          *   value = the value
122          */
123         public this(V value)
124         {
125             setValue(value);
126             timer = StopWatch(AutoStart.yes);
127         }
128 
129         /** 
130          * Sets the value of this
131          * entry
132          *
133          * Params:
134          *   value = the value
135          */
136         public void setValue(V value)
137         {
138             this.value = value;
139         }
140 
141         /** 
142          * Returns the value associated
143          * with this entry
144          *
145          * Returns: the value
146          */
147         public V getValue()
148         {
149             return this.value;
150         }
151 
152         /** 
153          * Resets the timer back
154          * to zero
155          */
156         public void bump()
157         {
158             timer.reset();
159         }
160 
161         /** 
162          * Gets the time elapsed
163          * since this entry was
164          * instantiated
165          *
166          * Returns: the elapsed
167          * time
168          */
169         public Duration getElapsedTime()
170         {
171             return timer.peek();
172         }
173     }
174 }
175 
176 /** 
177  * A `CacheMap` with a key type of `K`
178  * and value type of `V`
179  */
180 public template CacheMap(K, V)
181 {
182     /** 
183      * A replacement function which takes
184      * in the key of type `K` and returns
185      * a value of type `V`
186      *
187      * This is the delegate-based variant
188      */
189     public alias ReplacementDelegate = V delegate(K);
190 
191     /** 
192      * A replacement function which takes
193      * in the key of type `K` and returns
194      * a value of type `V`
195      *
196      * This is the function-based variant
197      */
198     public alias ReplacementFunction = V function(K);
199 
200     /** 
201      * A caching map which when queried
202      * for a key which does not exist yet
203      * will call a so-called replacement
204      * function which produces a result
205      * which will be stored at that key's
206      * location
207      *
208      * After this process a timer is started,
209      * and periodically entries are checked
210      * for timeouts, if they have timed out
211      * then they are removed and the process
212      * begins again.
213      *
214      * Accessing an entry will reset its
215      * timer ONLY if it has not yet expired
216      * however accessing an entry which
217      * has expired causing an on-demand
218      * replacement function call, just not
219      * a removal in between
220      */
221     public class CacheMap
222     {
223         private Entry!(V)[K] map;
224         private Mutex lock;
225         private Duration expirationTime;
226         private ReplacementDelegate replFunc;
227 
228         private Thread checker;
229         private bool isRunning;
230         private Condition condVar;
231         private Duration sweepInterval;
232         
233         /** 
234          * Constructs a new cache map with the
235          * given replacement delegate and the
236          * expiration deadline.
237          *
238          * Params:
239          *   replFunc = the replacement delegate
240          *   expirationTime = the expiration
241          * deadline
242          *   sweepInterval = the interval at
243          * which the sweeper thread should
244          * run at to check for expired entries
245          */
246         this(ReplacementDelegate replFunc, Duration expirationTime = dur!("seconds")(10), Duration sweepInterval = dur!("seconds")(10))
247         {
248             this.replFunc = replFunc;
249             this.lock = new Mutex();
250             this.expirationTime = expirationTime;
251 
252             this.sweepInterval = sweepInterval;
253             this.condVar = new Condition(this.lock);
254             this.checker = new Thread(&checkerFunc);
255             this.isRunning = true;
256             this.checker.start();
257           
258         }
259 
260         /** 
261          * Constructs a new cache map with the
262          * given replacement function and the
263          * expiration deadline.
264          *
265          * Params:
266          *   replFunc = the replacement function
267          *   expirationTime = the expiration
268          * deadline
269          *   sweepInterval = the interval at
270          * which the sweeper thread should
271          * run at to check for expired entries
272          */
273         this(ReplacementFunction replFunc, Duration expirationTime = dur!("seconds")(10), Duration sweepInterval = dur!("seconds")(10))
274         {
275             this(toDelegate(replFunc), expirationTime, sweepInterval);
276         }
277 
278         /** 
279          * Creates an entry for the given
280          * key by creating the `Entry`
281          * at the key and then setting
282          * that entry's value with the
283          * replacement function
284          *
285          * Params:
286          *   key = the key
287          * Returns: the value set
288          */
289         private V makeKey(K key)
290         {
291             // Lock the mutex
292             this.lock.lock();
293 
294             // On exit
295             scope(exit)
296             {
297                 // Unlock the mutex
298                 this.lock.unlock();
299             }
300 
301             // Run the replacement function for this key
302             V newValue = replFunc(key);
303 
304             // Create a new entry with this value
305             Entry!(V) newEntry = Entry!(V)(newValue);
306 
307             // Save this entry into the hashmap
308             this.map[key] = newEntry;
309             
310             return newValue;
311         }
312 
313         /** 
314          * Called to update an existing
315          * `Entry` (already present) in
316          * the map. This will run the 
317          * replacement function and update
318          * the value present.
319          *
320          * Params:
321          *   key = the key
322          * Returns: the value set
323          */
324         private V updateKey(K key)
325         {
326             // Lock the mutex
327             this.lock.lock();
328 
329             // On exit
330             scope(exit)
331             {
332                 // Unlock the mutex
333                 this.lock.unlock();
334             }
335 
336             // Run the replacement function for this key
337             V newValue = replFunc(key);
338 
339             // Update the value saved at this key's entry
340             this.map[key].setValue(newValue);
341 
342             return newValue;
343         }
344 
345         /** 
346          * Check's a specific key for expiration,
347          * and if expired then refreshes it if
348          * not it leaves it alone.
349          *
350          * Returns the key's value
351          *
352          * Params:
353          *   key = the key to check
354          * Returns: the key's value
355          */
356         private V expirationCheck(K key)
357         {
358             // Lock the mutex
359             this.lock.lock();
360 
361             // On exit
362             scope(exit)
363             {
364                 // Unlock the mutex
365                 this.lock.unlock();
366             }
367 
368             // Obtain the entry at this key
369             Entry!(V)* entry = key in this.map;
370 
371             // If the key exists
372             if(entry != null)
373             {
374                 // If this entry expired, run the refresher
375                 if(entry.getElapsedTime() >= this.expirationTime)
376                 {
377                     version(unittest) { writeln("Expired entry for key '", key, "', refreshing"); }
378                     
379                     updateKey(key);
380                 }
381                 // Else, if not, then bump the entry
382                 else
383                 {
384                     entry.bump();
385                 }
386             }
387             // If it does not exist (then make it)
388             else
389             {
390                 version(unittest) { writeln("Hello there, we must MAKE key as it does not exist"); }
391                 makeKey(key);
392                 version(unittest) { writeln("fic"); }
393             }
394 
395             return this.map[key].getValue();
396         }
397 
398         /** 
399          * Gets the value of
400          * the entry at the
401          * provided key
402          *
403          * This may or may not
404          * call the replication
405          * function
406          *
407          * Params:
408          *   key = the key to
409          * lookup by
410          *
411          * Returns: the value
412          */
413         public V get(K key)
414         {
415             // Lock the mutex
416             this.lock.lock();
417 
418             // On exit
419             scope(exit)
420             {
421                 // Unlock the mutex
422                 this.lock.unlock();
423             }
424 
425             // The key's value
426             V keyValue;
427 
428             // On access expiration check
429             keyValue = expirationCheck(key);
430 
431             return keyValue;
432         }
433 
434         /** 
435          * See_Also: get 
436          */
437         public V opIndex(K key)
438         {
439             return get(key);
440         }
441 
442         /** 
443          * Removes the given key
444          * returning whether or
445          * not it was a success
446          *
447          * Params:
448          *   key = the key to
449          * remove
450          * Returns: `true` if the
451          * key existed, `false`
452          * otherwise
453          */
454         public bool removeKey(K key)
455         {
456             // Lock the mutex
457             this.lock.lock();
458 
459             // On exit
460             scope(exit)
461             {
462                 // Unlock the mutex
463                 this.lock.unlock();
464             }
465 
466             // Remove the key
467             return this.map.remove(key);
468         }
469 
470         /** 
471          * Runs at the latest every
472          * `expirationTime` ticks
473          * and checks the entire
474          * map for expired
475          * entries
476          */
477         private void checkerFunc()
478         {
479             while(this.isRunning)
480             {
481                 // Lock the mutex
482                 this.lock.lock();
483 
484                 // On loop exit
485                 scope(exit)
486                 {
487                     // Unlock the mutex
488                     this.lock.unlock();
489                 }
490 
491                 // Sleep until sweep interval
492                 this.condVar.wait(this.sweepInterval);
493 
494                 // Run the expiration check
495                 K[] marked;
496                 foreach(K curKey; this.map.keys())
497                 {
498                     Entry!(V) curEntry = this.map[curKey];
499 
500                     // If entry has expired mark it for removal
501                     if(curEntry.getElapsedTime() >= this.expirationTime)
502                     {
503                         version(unittest) { writeln("Marked entry '", curEntry, "' for removal"); }
504                         marked ~= curKey;
505                     }
506                 }
507 
508                 foreach(K curKey; marked)
509                 {
510                     Entry!(V) curEntry = this.map[curKey];
511 
512                     version(unittest) { writeln("Removing entry '", curEntry, "'..."); }
513                     this.map.remove(curKey);
514                 }
515             }
516         }
517 
518         /** 
519          * Wakes up the checker
520          * immediately such that
521          * it can perform a cycle
522          * over the map and check
523          * for expired entries
524          */
525         private void doLiveCheck()
526         {
527             // Lock the mutex
528             this.lock.lock();
529 
530             // Signal wake up
531             this.condVar.notify();
532 
533             // Unlock the mutex
534             this.lock.unlock();
535         }
536 
537         /** 
538          * On destruction, set
539          * the running status
540          * to `false`, then
541          * wake up the checker
542          * and wait for it to
543          * exit
544          */
545         ~this()
546         {
547             version(unittest)
548             {
549                 writeln("Dtor running");
550 
551                 scope(exit)
552                 {
553                     writeln("Dtor running [done]");
554                 }
555             }
556 
557             // Set run state to false
558             this.isRunning = false;
559 
560             // Signal to stop
561             doLiveCheck();
562 
563             // Wait for it to stop
564             this.checker.join();
565         }
566     }
567 }
568 
569 /**
570  * Tests the usage of the `CacheMap` type
571  * along with the expiration of entries
572  * mechanism
573  */
574 unittest
575 {
576     int i = 0;
577     int getVal(string)
578     {
579         i++;
580         return i;
581     }
582 
583     // Create a CacheMap with 10 second expiration and 10 second sweeping interval
584     CacheMap!(string, int) map = new CacheMap!(string, int)(&getVal, dur!("seconds")(10));
585 
586     // Get the value
587     int tValue = map["Tristan"];
588     assert(tValue == 1);
589 
590     // Get the value (should still be cached)
591     tValue = map["Tristan"];
592     assert(tValue == 1);
593 
594     // Wait for expiry (by sweeping thread)
595     Thread.sleep(dur!("seconds")(11));
596 
597     // Should call replacement function
598     tValue = map["Tristan"];
599     assert(tValue == 2);
600 
601     // Wait for expiry (by sweeping thread)
602     writeln("Sleeping now 11 secs");
603     Thread.sleep(dur!("seconds")(11));
604 
605     // Destroy the map (such that it ends the sweeper)
606     destroy(map);
607 }
608 
609 /**
610  * Creates a `CacheMap` which tests out
611  * the on-access expiration checking of
612  * entries by accessing an entry faster
613  * then the sweep interval and by
614  * having an expiration interval below
615  * the aforementioned interval
616  */
617 unittest
618 {
619     int i = 0;
620     int getVal(string)
621     {
622         i++;
623         return i;
624     }
625 
626     // Create a CacheMap with 5 second expiration and 10 second sweeping interval
627     CacheMap!(string, int) map = new CacheMap!(string, int)(&getVal, dur!("seconds")(5), dur!("seconds")(10));
628 
629     // Get the value
630     int tValue = map["Tristan"];
631     assert(tValue == 1);
632 
633     // Wait for 5 seconds (the entry should then be expired by then for on-access check)
634     Thread.sleep(dur!("seconds")(5));
635 
636     // Get the value (should have replacement function run)
637     tValue = map["Tristan"];
638     assert(tValue == 2);
639 
640     // Destroy the map (such that it ends the sweeper
641     destroy(map);
642 }
643 
644 /**
645  * Tests the usage of the `CacheMap`,
646  * specifically the explicit key
647  * removal method
648  */
649 unittest
650 {
651     int i = 0;
652     int getVal(string)
653     {
654         i++;
655         return i;
656     }
657 
658     // Create a CacheMap with 10 second expiration and 10 second sweeping interval
659     CacheMap!(string, int) map = new CacheMap!(string, int)(&getVal, dur!("seconds")(10), dur!("seconds")(10));
660 
661     // Get the value
662     int tValue = map["Tristan"];
663     assert(tValue == 1);
664 
665     // Remove the key
666     assert(map.removeKey("Tristan"));
667 
668     // Get the value
669     tValue = map["Tristan"];
670     assert(tValue == 2);
671 
672     // Destroy the map (such that it ends the sweeper
673     destroy(map);
674 }
675 
676 /** 
677  * A visitation stratergy
678  * which always returns
679  * `true`
680  */
681 public template Always(T)
682 {
683     /** 
684      * Whatever graph node is
685      * provided always accept
686      * a visitation to it
687      *
688      * Params:
689      *   treeNode = the node
690      * Returns: `true` always
691      */
692     public bool Always(Graph!(T) treeNode)
693     {
694         version(unittest)
695         {
696             import std.stdio : writeln;
697             writeln("Strat for: ", treeNode);
698         }
699         return true;
700     }
701 }
702 
703 /** 
704  * A touching stratergy
705  * that does nothing
706  */
707 public template Nothing(T)
708 {
709     /** 
710      * Consumes a graph node
711      * and does zilch with it
712      *
713      * Params:
714      *   treeNode = the node
715      */
716     public void Nothing(Graph!(T)) {}
717 }
718 
719 /** 
720  * The inclusion stratergy which
721  * will be called upon the graph
722  * node prior to it being visited
723  * during a dfs operation.
724  *
725  * It is a predicate to determine
726  * whether or not the graph node
727  * in concern should be recursed
728  * upon.
729  */
730 public template InclusionStratergy(T)
731 {
732     public alias InclusionStratergy = bool delegate(Graph!(T) item);
733 }
734 
735 /** 
736  * This is called on a graph node
737  * as part of the first action
738  * that takes place during the
739  * visitation of said node during
740  * a dfs operation.
741  */
742 public template TouchStratergy(T)
743 {
744     public alias TouchStratergy = void delegate(Graph!(T) item);
745 }
746 
747 /** 
748  * A graph of nodes.
749  *
750  * These nodes are comprised of
751  * two components. The first of
752  * which is their associated value
753  * of type `T`, then the second
754  * are their children nodes
755  * (if any). The latter are of
756  * type `Graph!(T)` and therefore
757  * when constructing one such node
758  * it can also be added as a child
759  * of another node, therefore
760  * allowing you to build your
761  * graph as you see fit.
762  *
763  * Some notable functionality,
764  * other than the obvious,
765  * is the pluggable dfs method
766  * which let's you perform 
767  * a recursive search on
768  * the graph, parameterized
769  * by two stratergies. The first
770  * is the so-called `TouchStratergy`
771  * which specifies the function
772  * to be called on the current node
773  * when `dfs` is called on it -
774  * this is the first thing that
775  * is done. The other parameter
776  * is the `VisitationStratergy`
777  * which is a predicate that
778  * will be called BEFORE
779  * entering the dfs (recursing)
780  * of a candidate child node.
781  * With this things like trees
782  * can be built or rather
783  * _derived_ from a graph.
784  * This is infact what the visitation
785  * tree type does.
786  *
787  * See_Also: `VisitationTree`
788  */
789 public class Graph(T)
790 {
791     private T value;
792     private Graph!(T)[] children;
793 
794     /** 
795      * Constructs a new graph with
796      * the given value to set
797      *
798      * Params:
799      *   value = the value of
800      * this graph node
801      */
802     this(T value)
803     {
804         this.value = value;
805     }
806 
807     /** 
808      * Creates a new graph without
809      * associating any value with
810      * itself
811      */
812     this()
813     {
814 
815     }
816 
817     /** 
818      * Sets the graph node's
819      * associated value
820      *
821      * Params:
822      *   value = the valye
823      */
824     public void setValue(T value)
825     {
826         this.value = value;
827     }
828 
829     /** 
830      * Obtains the value associated with
831      * this graph node
832      *
833      * Returns: the value `T`
834      */
835     public T getValue()
836     {
837         return this.value;
838     }
839 
840     /** 
841      * Appends another graph node
842      * to the array of children
843      * of this node's
844      *
845      * Params:
846      *   node = the tree node
847      * to append
848      */
849     public void appendNode(Graph!(T) node)
850     {
851         this.children ~= node;
852     }
853 
854     /** 
855      * Removes a given graph node
856      * from th array of children
857      * of thie node's
858      *
859      * Params:
860      *   node = the graph node to
861      * remove
862      * Returns: `true` if the node
863      * was found and then removed,
864      * otherwise `false`
865      */
866     public bool removeNode(Graph!(T) node)
867     {
868         bool found = false;
869         size_t idx;
870         for(size_t i = 0; i < this.children.length; i++)
871         {
872             found = this.children[i] == node;
873             if(found)
874             {
875                 idx = i;
876                 break;
877             }
878         }
879 
880         if(found)
881         {
882             this.children = this.children.removeResize(idx);
883             return true;
884         }
885 
886         return false;
887     }
888 
889     /** 
890      * Checks if the given type is
891      * that of a graph node
892      *
893      * Returns: `true` if so, `false`
894      * otherwise
895      */
896     private static bool isGraphNodeType(E)()
897     {
898         return __traits(isSame, E, Graph!(T));
899     }
900 
901     /** 
902      * Checks if the given type is
903      * that of a graph node's value
904      * type
905      *
906      * Returns: `true` if so, `false`
907      * otherwise
908      */
909     private static bool isGraphValueType(E)()
910     {
911         return __traits(isSame, E, T);
912     }
913 
914     /** 
915      * Returns a slice of the requested
916      * type. This is either `Graph!(T)`
917      * or `T` itself, therefore returning
918      * an array of either
919      *
920      * Returns: an array of the requested
921      * type of children
922      */
923     public E[] opSlice(E)()
924     if(isGraphNodeType!(E) || isGraphValueType!(E))
925     {
926         // If the children as graph nodes is requested
927         static if(isGraphNodeType!(E))
928         {
929             return this.children;
930         }
931         // If the children as values themselves is requested
932         else static if(isGraphValueType!(E))
933         {
934             T[] slice;
935             foreach(Graph!(T) tnode; this.children)
936             {
937                 slice ~= tnode.value;
938             }
939             return slice;
940         }
941     }
942 
943     /** 
944      * Returns an array of all the childrens'
945      * associated values
946      *
947      * Returns: a `T[]`
948      */
949     public T[] opSlice()
950     {
951         return opSlice!(T)();
952     }
953 
954     /** 
955      * Returns the element of the child
956      * at the given index.
957      *
958      * The type `E` can be specified
959      * as either `Graph!(T)` or `T`
960      * which will hence return a node
961      * from the children array at the 
962      * given index of that type (either
963      * the child node or the child node's
964      * value).
965      *
966      * Params:
967      *   idx = the index 
968      * Returns: the type `E`
969      */
970     public E opIndex(E)(size_t idx)
971     if(isGraphNodeType!(E) || isGraphValueType!(E))
972     {
973         // If the child as a graph node is requested
974         static if(isGraphNodeType!(E))
975         {
976             return this.children[idx];
977         }
978         // If the child as a value itself is requested
979         else static if(isGraphValueType!(E))
980         {
981             return this.children[idx].value;
982         }
983     }
984 
985     /** 
986      * Returns the value of
987      * the child node at
988      * the provided index
989      *
990      * Params:
991      *   idx = the index
992      * Returns: the value
993      */
994     public T opIndex(size_t idx)
995     {
996         return opIndex!(T)(idx);
997     }
998 
999     /** 
1000      * Returns the number
1001      * of children attached
1002      * to this node
1003      *
1004      * Returns: the count
1005      */
1006     @property
1007     public size_t length()
1008     {
1009         return this.children.length;
1010     }
1011 
1012     /** 
1013      * Returns the number
1014      * of children attached
1015      * to this node
1016      *
1017      * Returns: the count
1018      */
1019     public size_t opDollar()
1020     {
1021         return this.length;
1022     }
1023 
1024     /** 
1025      * Performs a depth first search
1026      * on the graph by firstly calling
1027      * the `TouchStratergy` on the current
1028      * node and then iterating over all
1029      * of its children and only recursing
1030      * on each of them if the `InclusionStratergy`
1031      * allows it.
1032      *
1033      * The touch stratergy is called
1034      * as part of the first line of code
1035      * in the call to the dfs on a
1036      * given graph node.
1037      *
1038      * Note that is you don't have a good
1039      * inclusion stratergy and touch startergy
1040      * then you may have a stack overflow
1041      * occur if your graph has cycles
1042      *
1043      * Params: 
1044      *   strat = the `InclusionStratergy` 
1045      *   touch = the `TouchStratergy`
1046      * Returns: a `T[]`
1047      */
1048     public T[] dfs
1049     (
1050         InclusionStratergy!(T) strat = toDelegate(&Always!(T)),
1051         TouchStratergy!(T) touch = toDelegate(&Nothing!(T))
1052     )
1053     {
1054         version(unittest)
1055         {
1056             writeln("dfs entry: ", this);
1057         }
1058         
1059         T[] collected;
1060         scope(exit)
1061         {
1062             version(unittest)
1063             {
1064                 writeln("leaving node ", this, " with collected ", collected);
1065             }
1066         }
1067 
1068         // Touch
1069         touch(this); // root[x]
1070 
1071         foreach(Graph!(T) child; this.children) // subtree[x], 
1072         {
1073             if(strat(child))
1074             {
1075                 version(unittest)
1076                 {
1077                     writeln("dfs, strat good for child: ", child);
1078                 }
1079 
1080                 // Visit
1081                 collected ~= child.dfs(strat, touch);
1082             }
1083             else
1084             {
1085                 version(unittest)
1086                 {
1087                     writeln("dfs, strat ignored for child: ", child);
1088                 }
1089             }
1090         }
1091 
1092         // "Visit"
1093         collected ~= this.value;
1094         
1095         
1096         return collected;
1097     }
1098 
1099     /** 
1100      * Returns a string representation
1101      * of this node and its value
1102      *
1103      * Returns: a `string`
1104      */
1105     public override string toString()
1106     {
1107         return format("GraphNode [val: %s]", this.value);
1108     }
1109 
1110     /** 
1111      * This will attempt to find the
1112      * graph node which contains the
1113      * provided value.
1114      *
1115      * Value matching is based on applying
1116      * the `==`/`opEquals()` operator
1117      * to each graph nodes' `getValue()`
1118      * within the graph.
1119      *
1120      * Params:
1121      *   val = the value
1122      * Returns: an optional containing
1123      * the graph node if found, else
1124      * an empty optional
1125      */
1126     public Optional!(Graph!(T)) findByValue(T val)
1127     {
1128         if(getValue() == val)
1129         {
1130             return Optional!(Graph!(T))(this);
1131         }
1132 
1133         foreach(Graph!(T) g; this.children)
1134         {
1135             Optional!(Graph!(T)) g_opt = g.findByValue(val);
1136             if(g_opt.isPresent())
1137             {
1138                 return g_opt;
1139             }
1140         }
1141 
1142         return Optional!(Graph!(T)).empty();
1143     }
1144 }
1145 
1146 /**
1147  * Test out usage of the `Graph!(T)`
1148  */
1149 unittest
1150 {
1151     Graph!(string) treeOfStrings = new Graph!(string)("Top");
1152 
1153     Graph!(string) subtree_1 = new Graph!(string)("1");
1154     Graph!(string) subtree_2 = new Graph!(string)("2");
1155     Graph!(string) subtree_3 = new Graph!(string)("3");
1156 
1157     treeOfStrings.appendNode(subtree_1);
1158     treeOfStrings.appendNode(subtree_2);
1159     treeOfStrings.appendNode(subtree_3);
1160 
1161     assert(treeOfStrings.opIndex!(Graph!(string))(0) == subtree_1);
1162     assert(treeOfStrings.opIndex!(Graph!(string))(1) == subtree_2);
1163     assert(treeOfStrings.opIndex!(Graph!(string))(2) == subtree_3);
1164 
1165     assert(treeOfStrings[0] == subtree_1.getValue());
1166     assert(treeOfStrings[1] == subtree_2.getValue());
1167     assert(treeOfStrings[2] == subtree_3.getValue());
1168 
1169     assert(treeOfStrings.opDollar() == 3);
1170 
1171     Optional!(Graph!(string)) match_opt = treeOfStrings.findByValue("2");
1172     assert(match_opt.isPresent());
1173     assert(match_opt.get() is subtree_2);
1174 
1175     InclusionStratergy!(string) strat = toDelegate(&Always!(string));
1176     TouchStratergy!(string) touch = toDelegate(&DebugTouch!(string));
1177 
1178     string[] result = treeOfStrings.dfs(strat, touch);
1179     writeln("dfs: ", result);
1180 
1181     assert(result[0] == "1");
1182     assert(result[1] == "2");
1183     assert(result[2] == "3");
1184     assert(result[3] == "Top");
1185 
1186 
1187     auto i = treeOfStrings.opSlice!(Graph!(string))();
1188     writeln("Siblings: ", i);
1189     assert(i[0] == subtree_1);
1190     assert(i[1] == subtree_2);
1191     assert(i[2] == subtree_3);
1192 
1193     auto p = treeOfStrings.opSlice!(string)();
1194     writeln("Siblings (vals): ", p);
1195     assert(p == treeOfStrings[]);
1196 
1197 
1198     assert(treeOfStrings.removeNode(subtree_1));
1199     assert(!treeOfStrings.removeNode(subtree_1));
1200 }
1201 
1202 /** 
1203  * A kind-of a graph which has the ability
1204  * to linearize all of its nodes which
1205  * results in performing a depth first
1206  * search resulting in the collection of
1207  * all nodes into a single array with
1208  * elements on the left hand side being
1209  * the most leafiest (and left-to-right
1210  * on the same depth are in said order).
1211  *
1212  * It also marks a node as visited on
1213  * entry to it via the dfs call to it.
1214  *
1215  * When dfs is performed, a child node
1216  * is only recursed upon if it has not
1217  * yet been visited.
1218  *
1219  * With all this, it means a graph of
1220  * relations can be flattened into an
1221  * array.
1222  */
1223 public class VisitationTree(T) : Graph!(T)
1224 {
1225     private bool visisted;    
1226 
1227     /** 
1228      * Constructs a new node
1229      *
1230      * Params:
1231      *   value = the value
1232      */
1233     this(T value)
1234     {
1235         super(value);
1236     }
1237 
1238     /** 
1239      * Performs the linearization
1240      *
1241      * Returns: the linearized list
1242      */
1243     public T[] linearize()
1244     {
1245         return dfs(toDelegate(&_shouldVisit), toDelegate(&_touch));
1246     }
1247 
1248     /** 
1249      * The inclusion startergy
1250      *
1251      * Params:
1252      *   tnode = the graph node
1253      * Returns: `true` if not
1254      * yet visited or incompatible
1255      * node type
1256      */
1257     private static bool _shouldVisit(Graph!(T) tnode)
1258     {
1259         VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode;
1260         return vnode && !vnode.isVisited();
1261     }
1262 
1263     /** 
1264      * The touching stratergy
1265      *
1266      * Only works on compatible
1267      * graph nodes
1268      *
1269      * Params:
1270      *   tnode = the tree node
1271      */
1272     private static void _touch(Graph!(T) tnode)
1273     {
1274         VisitationTree!(T) vnode = cast(VisitationTree!(T))tnode;
1275         if(vnode)
1276         {
1277             vnode.mark();
1278         }
1279     }
1280 
1281     /** 
1282      * Marks this node as
1283      * visited
1284      */
1285     public void mark()
1286     {
1287         this.visisted = true;
1288     }
1289     
1290     /** 
1291      * Checks this node has been
1292      * visited
1293      *
1294      * Returns: `true` if visited,
1295      * otherwise `false`
1296      */
1297     public bool isVisited()
1298     {
1299         return this.visisted;
1300     }
1301 }
1302 
1303 /**
1304  * Tests out using the visitation tree
1305  */
1306 unittest
1307 {
1308     VisitationTree!(string) root = new VisitationTree!(string)("root");
1309 
1310     VisitationTree!(string) thing = new VisitationTree!(string)("subtree");
1311     root.appendNode(thing);
1312     thing.appendNode(root);
1313 
1314     string[] linearized = root.linearize();
1315     writeln(linearized);
1316 
1317     assert(linearized[0] == "subtree");
1318     assert(linearized[1] == "root");
1319 }
1320 
1321 public class CycleDetectionTree(T) : Graph!(T)
1322 {
1323     private size_t cnt;
1324 
1325     /** 
1326      * Constructs a new node
1327      *
1328      * Params:
1329      *   value = the value
1330      */
1331     this(T value)
1332     {
1333         super(value);
1334     }
1335 
1336     public void mark(size_t s)
1337     {
1338         this.cnt += s;
1339     }
1340 
1341     public void mark()
1342     {
1343         mark(1);
1344     }
1345 
1346     public void opUnary(string op)()
1347     if(op == "++")
1348     {
1349         mark();
1350     }
1351 
1352     public void opBinaryRight(string op, size_t)(size_t lhs)
1353     if(op == "+")
1354     {
1355         assert(lhs > 0); // FIXME: Throw rather
1356         mark(lhs);
1357     }
1358 
1359     public bool isVisited()
1360     {
1361         return this.cnt > 0;
1362     }
1363 
1364     public bool wasCycled()
1365     {
1366         return this.cnt > 1;
1367     }
1368 
1369     public size_t getCnt()
1370     {
1371         return this.cnt;
1372     }
1373 }
1374 
1375 /** 
1376  * Basic implementation of a `SectorType`
1377  */
1378 public struct BasicSector(T)
1379 {
1380     private T[] data;
1381 
1382     this(T[] data)
1383     {
1384         this.data = data;
1385     }
1386 
1387     /** 
1388      * Constructs a new `BasicSector`
1389      *
1390      * Params:
1391      *   data = the data the sector
1392      * should reference
1393      * Returns: a new `BasicSector`
1394      */
1395     public static BasicSector!(T) make(T[] data)
1396     {
1397         return BasicSector!(T)(data);
1398     }
1399 
1400     /** 
1401      * Ontains the element at the
1402      * given index
1403      *
1404      * Params:
1405      *   idx = the index
1406      * Returns: the element
1407      */
1408     public T opIndex(size_t idx)
1409     {
1410         return this.data[idx];
1411     }
1412 
1413     /** 
1414      * Set something at an index
1415      *
1416      * Params:
1417      *   value = the value to set
1418      *   index = the index to store
1419      * the value at
1420      */
1421     public void opIndexAssign(T value, size_t index)
1422     {
1423         this.data[index] = value;
1424     }
1425 
1426     /** 
1427      * Obtains this sector's length
1428      *
1429      * Returns: the length
1430      */
1431     public size_t opDollar()
1432     {
1433         return this.data.length;
1434     }
1435 
1436     /** 
1437      * Obtains this sector's length
1438      *
1439      * Returns: the length
1440      */
1441     @property
1442     public size_t length()
1443     {
1444         return opDollar();
1445     }
1446 
1447     /** 
1448      * Returns a slice of this sector
1449      * between the given bounds
1450      *
1451      * Params:
1452      *   start = the lower (inclusive)
1453      * bound
1454      *   end = the upper (exclusive)
1455      * bound
1456      * Returns: the slice
1457      */
1458     public T[] opSlice(size_t start, size_t end)
1459     {
1460         return this.data[start..end];
1461     }
1462 
1463     /** 
1464      * Ontains a slice of the
1465      * entire sector
1466      *
1467      * Returns: the slice
1468      */
1469     public T[] opSlice()
1470     {
1471         return opSlice(0, opDollar);
1472     }
1473 
1474     /** 
1475      * Sets the new size of this
1476      * sector's internal buffer
1477      *
1478      * Note that this should
1479      * only be smaller or equal
1480      * to the current internal
1481      * buffer size
1482      *
1483      * Params:
1484      *   newSize = the new size
1485      */
1486     @property
1487     public void length(size_t newSize)
1488     {
1489         assert(newSize <= this.data.length);
1490         this.data.length = newSize;
1491     }
1492 }
1493 
1494 /** 
1495  * Compile-time function to check if
1496  * a given data type is a valid
1497  * `SectorType`, meaning that it
1498  * can be used for a `View`
1499  * implementation
1500  *
1501  * Returns: `true` if so, `false`
1502  * otherwise
1503  */
1504 public bool isSector(S)()
1505 {
1506     bool s = true;
1507 
1508     alias args = TemplateArgsOf!(S);
1509     pragma(msg, args);
1510     static if(!args.length)
1511     {
1512         return false;
1513     }
1514     
1515     alias T = args[0];
1516 
1517     // Has opSlice(size_t, size_t) with T[] return
1518     s &= hasMember!(S, "opSlice") &&
1519         __traits(isSame, Parameters!(S.opSlice), AliasSeq!(size_t, size_t)) &&
1520         __traits(isSame, ReturnType!(S.opSlice), T[]);
1521 
1522     // Has opSlice() with T[] return
1523     bool foundNonParamOpSlice = false;
1524     foreach(func; __traits(getOverloads, S, "opSlice"))
1525     {
1526         if(arity!(func) == 0)
1527         {
1528             static if(__traits(isSame, ReturnType!(S.opSlice), T[]))
1529             {
1530                 foundNonParamOpSlice = true;
1531             }
1532         }
1533     }
1534     s &= foundNonParamOpSlice;
1535 
1536     // Has length method
1537     s &= hasMember!(S, "length") && 
1538          __traits(isSame, ReturnType!(S.length), size_t) &&
1539          staticIndexOf!("@property", __traits(getFunctionAttributes, S.length)) != -1;
1540 
1541     // Has length (setter) method
1542     bool found_len_setter = false;
1543     static foreach(lenFunc; __traits(getOverloads, S, "length"))
1544     {
1545         static if
1546         (
1547             __traits(isSame, Parameters!(lenFunc), AliasSeq!(size_t)) &&
1548             __traits(isSame, ReturnType!(lenFunc), void) &&
1549             staticIndexOf!("@property", __traits(getFunctionAttributes, lenFunc)) != -1
1550         )
1551         {
1552             found_len_setter = true;
1553         }
1554     }
1555     s &= found_len_setter;
1556 
1557     // Has opDollar with size_t return
1558     s &= hasMember!(S, "opDollar") &&
1559         __traits(isSame, Parameters!(S.opDollar), AliasSeq!()) &&
1560         __traits(isSame, ReturnType!(S.opDollar), size_t);
1561 
1562     // Has opIndex(size_t) with T return
1563     s &= hasMember!(S, "opIndex") &&
1564         __traits(isSame, Parameters!(S.opIndex), AliasSeq!(size_t)) &&
1565         __traits(isSame, ReturnType!(S.opIndex), T);
1566 
1567     // Has opIndexAssign(size_t) with T return
1568     s &= hasMember!(S, "opIndexAssign") &&
1569         __traits(isSame, Parameters!(S.opIndexAssign), AliasSeq!(T, size_t)) &&
1570         __traits(isSame, ReturnType!(S.opIndexAssign), void);
1571 
1572 
1573     // Has make(T[] data) returning S (implied S!(T) due to template arg check earlier)
1574     s &= hasStaticMember!(S, "make") &&
1575          __traits(isSame, Parameters!(S.make), AliasSeq!(T[])) &&
1576          __traits(isSame, ReturnType!(S.make), S);
1577 
1578     return s;
1579 }
1580 
1581 /** 
1582  * A view represents a collection of
1583  * arrays which can be accessed
1584  * in an array like manner and have their
1585  * elements changed too. Therefore this
1586  * provides access to these originally
1587  * non-contiguous data sets as if they
1588  * were one contiguous array.
1589  *
1590  * Updating of elements is allowed,
1591  * fetching of elements (and slices)
1592  * and lastly sizing down but NOT
1593  * updwards. This last constraint
1594  * is why this is considered a "view".
1595  */
1596 public struct View(T, SectorType = BasicSector!(T))
1597 if(isSector!(SectorType)())
1598 {
1599     private SectorType[] sectors;
1600 
1601     // Maybe current size should be here as we
1602     // are a view, we should allow modification
1603     // but not make any NEW arrays
1604     private size_t curSize;
1605 
1606     /** 
1607      * Computes the sum of the
1608      * length of all sectors
1609      * attached to us
1610      *
1611      * Returns: the total
1612      */
1613     private size_t computeTotalLen()
1614     {
1615         size_t l;
1616         foreach(SectorType sector; this.sectors)
1617         {
1618             l += sector.opDollar();
1619         }
1620         return l;
1621     }
1622 
1623     /** 
1624      * Returns the total length
1625      * of the data in the view
1626      *
1627      * Returns: the length
1628      */
1629     public size_t opDollar()
1630     {
1631         return this.length;
1632     }
1633 
1634     /** 
1635      * Retrieves the value of
1636      * the element at the
1637      * given position
1638      *
1639      * Params:
1640      *   idx = the position
1641      * of the element
1642      * Returns: the value
1643      */
1644     public T opIndex(size_t idx)
1645     {
1646         // Within range of "fake" size
1647         if(!(idx < this.length))
1648         {
1649             throw new ArrayIndexError(idx, this.length);
1650         }
1651 
1652         size_t thunk;
1653         foreach(SectorType sector; this.sectors)
1654         {
1655             if(idx-thunk < sector.opDollar())
1656             {
1657                 return sector[idx-thunk];
1658             }
1659             else
1660             {
1661                 thunk += sector.opDollar();
1662             }
1663         }
1664 
1665         // NOTE: This should be unreachable but
1666         // compiler moans and groans
1667         assert(false);
1668     }
1669 
1670     /** 
1671      * Updates the element at
1672      * the given index with
1673      * a new value
1674      *
1675      * Params:
1676      *   value = the new value
1677      *   idx = the element
1678      * to update's position
1679      */
1680     public void opIndexAssign(T value, size_t idx)
1681     {
1682         // Within range of "fake" size
1683         if(!(idx < this.length))
1684         {
1685             throw new ArrayIndexError(idx, this.length);
1686         }
1687 
1688         size_t thunk;
1689         foreach(ref SectorType sector; this.sectors)
1690         {
1691             version(unittest)
1692             {
1693                 writeln(sector);
1694                 writeln("idx: ", idx);
1695                 writeln("thunk: ", thunk);
1696             }
1697             
1698             if(idx-thunk < sector.opDollar())
1699             {
1700                 sector[idx-thunk] = value;
1701                 return;
1702             }
1703             else
1704             {
1705                 thunk += sector.opDollar();
1706             }
1707         }
1708     }
1709 
1710     /** 
1711      * Returns a copy of the entire
1712      * view
1713      *
1714      * Returns: a `T[]`
1715      */
1716     public T[] opSlice()
1717     {
1718         return this[0..this.length];
1719     }
1720 
1721     /** 
1722      * Returns a copy of the view
1723      * within the provided bounds
1724      *
1725      * Params:
1726      *   start = the starting
1727      * index
1728      *   end = the ending index
1729      * (exclusive)
1730      * Returns: 
1731      */
1732     public T[] opSlice(size_t start, size_t end)
1733     {
1734         // Invariant of start <= end
1735         if(!(start <= end))
1736         {
1737             throw new RangeError("Starting index must be smaller than or equal to ending index");
1738         }
1739         // If the indices are equal, then it is empty
1740         else if(start == end)
1741         {
1742             return [];
1743         }
1744         // Within range of "fake" size
1745         else if(!((start < this.length) && (end <= this.length)))
1746         {
1747             throw new RangeError("start index or end index not under range");
1748         }
1749 
1750         T[] collected;
1751 
1752         size_t thunk;
1753         foreach(SectorType sector; this.sectors)
1754         {
1755             // If the current sector contains
1756             // both the starting AND ending
1757             // indices
1758             if(start-thunk < sector.opDollar() && end-thunk <= sector.opDollar())
1759             {
1760                 return sector[start-thunk..end-thunk];
1761             }
1762             // If the current sector's starting
1763             // index (only) is included
1764             else if(start-thunk < sector.opDollar() && !(end-thunk <= sector.opDollar()))
1765             {
1766                 collected ~= sector[start-thunk..$];
1767             }
1768             // If the current sector's ending
1769             // index (only) is included
1770             else if(!(start-thunk < sector.opDollar()) && end-thunk <= sector.opDollar())
1771             {
1772                 collected ~= sector[0..end-thunk];
1773             }
1774             // If the current sector's entirety
1775             // is to be included
1776             else
1777             {
1778                 collected ~= sector[];
1779             }
1780 
1781             thunk += sector.opDollar();
1782         }
1783 
1784         return collected;
1785     }
1786 
1787     private static bool isArrayAppend(P)()
1788     {
1789         return __traits(isSame, P, T[]);
1790     }
1791 
1792     private static bool isElementAppend(P)()
1793     {
1794         return __traits(isSame, P, T);
1795     }
1796 
1797     /** 
1798      * Appends a new value to
1799      * the end of the view
1800      *
1801      * Params:
1802      *   value = the value
1803      * to append
1804      */
1805     public void opOpAssign(string op, E)(E value)
1806     if(op == "~" && (isArrayAppend!(E) || isElementAppend!(E)))
1807     {
1808         static if(isArrayAppend!(E))
1809         {
1810             add(value);
1811         }
1812         else
1813         {
1814             add([value]);
1815         }
1816     }
1817 
1818     /** 
1819      * Takes the data with which we
1820      * constructs a kind-of `SectorType`
1821      * from. We then adjust the total
1822      * size and append the new sector
1823      *
1824      * Params:
1825      *   data = the data to append
1826      */
1827     private void add(T[] data)
1828     {
1829         // Create a new sector
1830         SectorType sec = SectorType.make(data);
1831 
1832         // Update the tracking size
1833         this.curSize += sec.length;
1834 
1835         // Concatenate it to the view
1836         this.sectors ~= sec;
1837     }
1838 
1839     /** 
1840      * Returns the total length
1841      * of the data in the view
1842      *
1843      * Returns: the length
1844      */
1845     @property
1846     public size_t length()
1847     {
1848         return this.curSize;
1849     }
1850 
1851     /** 
1852      * Resizes the total
1853      * length of the view.
1854      *
1855      * This allows the user
1856      * to either keep the
1857      * size the same or
1858      * shrink the view,
1859      * but never extend
1860      * it.
1861      *
1862      * 
1863      * Params:
1864      *   size = the new size
1865      * Throws:
1866      *   RangeError if an
1867      * attempt to extend
1868      * the length is made
1869      */
1870     @property
1871     public void length(size_t size)
1872     {
1873         // TODO: Need we continuously compute this?
1874         // ... we should have a tracking field for
1875         // ... this
1876         // Would only need to be called in length(size_t)
1877         // and add(T[])
1878         size_t actualSize = computeTotalLen();
1879 
1880         // On successful exit, update the "fake" size
1881         scope(success)
1882         {
1883             this.curSize = size;
1884         }
1885 
1886 
1887         // Don't allow sizing up (doesn't make sense for a view)
1888         if(size > actualSize)
1889         {
1890             auto r = new RangeError();
1891             r.msg = "Cannot extend the size of a view past its total size (of all attached sectors)";
1892             throw r;
1893         }
1894         // If nothing changes
1895         else if(size == actualSize)
1896         {
1897             // Nothing
1898         }
1899         // If shrinking to zero
1900         else if(size == 0)
1901         {
1902             // Just drop everything
1903             this.sectors.length = 0;
1904         }
1905         // If shrinking (arbitrary)
1906         else
1907         {
1908             // Sectors from left-to-right to keep
1909             size_t sectorCnt;
1910 
1911             // Accumulator
1912             size_t accumulator;
1913 
1914             foreach(ref SectorType sector; this.sectors)
1915             {
1916                 accumulator += sector.length;
1917                 sectorCnt++;
1918                 if(size <= accumulator)
1919                 {
1920                     // TODO: Resize on the tail-end sector?
1921 
1922                     // Bleed size (accumulation of previous sectors)
1923                     // called "x". Then we do `size-x`, this gives
1924                     // us the bleed size and we use this as the
1925                     // tail-end sector's new size
1926                     size_t tailEndTrimSize = size-(accumulator-sector.length);
1927                     version(unittest)
1928                     {
1929                         writeln("tailEndTrimSize: ", tailEndTrimSize);
1930                     }
1931                     sector.length(tailEndTrimSize);
1932 
1933                     break;
1934                 }
1935             }
1936 
1937             this.sectors.length = sectorCnt;
1938         }
1939     }
1940 }
1941 
1942 unittest
1943 {
1944     View!(int) view;
1945     assert(view.opDollar() == 0);
1946 
1947     try
1948     {
1949         view[1];
1950         assert(false);
1951     }
1952     catch(ArrayIndexError e)
1953     {
1954         assert(e.index == 1);
1955         assert(e.length == 0);
1956     }
1957 
1958     view ~= [1,3,45];
1959     assert(view.opDollar() == 3);
1960     assert(view.length == 3);
1961 
1962     view ~= 2;
1963     assert(view.opDollar() == 4);
1964     assert(view.length == 4);
1965 
1966     assert(view[0] == 1);
1967     assert(view[1] == 3);
1968     assert(view[2] == 45);
1969     assert(view[3] == 2);
1970     assert(view[0..2] == [1,3]);
1971     assert(view[0..4] == [1,3,45,2]);
1972 
1973     // Update elements
1974     view[0] = 71;
1975     view[3] = 50;
1976 
1977     // Set size to same size
1978     view.length = view.length;
1979 
1980     // Check that update is present
1981     // and size unchanged
1982     int[] all = view[];
1983     assert(all == [71,3,45,50]);
1984 
1985     // Truncate by 1 element
1986     view.length = view.length-1;
1987     all = view[];
1988     assert(all == [71,3,45]);
1989 
1990     // This should fail
1991     try
1992     {
1993         view[3] = 3;
1994         assert(false);
1995     }
1996     catch(RangeError e)
1997     {
1998     }
1999 
2000     // This should fail
2001     try
2002     {
2003         int j = view[3];
2004         assert(false);
2005     }
2006     catch(RangeError e)
2007     {
2008     }
2009 
2010     // Up-sizing past real size should not be allowed
2011     try
2012     {
2013         view.length =  view.length+1;
2014         assert(false);
2015     }
2016     catch(RangeError e)
2017     {
2018     }
2019 
2020     // Size to zero
2021     view.length = 0;
2022     assert(view.length == 0);
2023     assert(view[] == []);
2024 }
2025 
2026 unittest
2027 {
2028     View!(int) view;
2029     view ~= 1;
2030     view ~= [2,3,4];
2031     view ~= 5;
2032 
2033     assert(view[0..5] == [1,2,3,4,5]);
2034 
2035     // test: start <= end invariant broken
2036     try
2037     {
2038         auto j = view[1..0];
2039         assert(false);
2040     }
2041     catch(RangeError e)
2042     {
2043 
2044     }
2045 
2046     // test: end out of bounds
2047     try
2048     {
2049         auto j = view[1..view.length+1];
2050         assert(false);
2051     }
2052     catch(RangeError e)
2053     {
2054 
2055     }
2056 
2057     int[] d = [1,2,3];
2058     writeln("according to dlang: ", d[1..2]);
2059 
2060     writeln("test lekker: ", view[1..2]);
2061     assert(view[1..2] == [2]);
2062 
2063     writeln("test lekker: ", view[1..1]);
2064     assert(view[1..1] == []);
2065 }
2066 
2067 import niknaks.meta : isClassType, isStructType;
2068 
2069 private mixin template Methods(EntityType)
2070 {
2071     // Class-based types
2072     static if(isClassType!(EntityType))
2073     {
2074         /** 
2075          * Pools a new entry by the given
2076          * value. If no such pool entry 
2077          * exists then it is constructed,
2078          * stored and returned, else the
2079          * existing entry is returned.
2080          *
2081          * Params:
2082          *   v = the key to pool by
2083          * Returns: the pooled entry
2084          * (as an `EntryType`)
2085          */
2086         public EntryType pool(ValueType v)
2087         {
2088 
2089             EntryType* ent = v in _p;
2090             if(ent is null)
2091             {
2092                 static if(entryTakesValue_onCtor)
2093                 {
2094                     _p[v] = new EntryType(v);
2095                 }
2096                 else
2097                 {
2098                     pragma(msg, "Class: Argless ctor less");
2099                     _p[v] = new EntryType();
2100                 }
2101                 
2102                 return pool(v);
2103             }
2104             return *ent;
2105         }
2106     }
2107     // Struct-based types
2108     else static if(isStructType!(EntryType))
2109     {
2110         /** 
2111          * Pools a new entry by the given
2112          * value. If no such pool entry 
2113          * exists then it is constructed,
2114          * stored and returned, else the
2115          * existing entry is returned.
2116          *
2117          * Params:
2118          *   v = the key to pool by
2119          * Returns: the pooled entry
2120          * (as an `EntryType*`)
2121          */
2122         public EntryType* pool(ValueType v)
2123         {
2124             EntryType* ent = v in _p;
2125             if(ent is null)
2126             {
2127                 static if(entryTakesValue_onCtor)
2128                 {
2129                     _p[v] = EntryType(v);
2130                 }
2131                 else
2132                 {
2133                     pragma(msg, "Struct: Argless ctor less");
2134                     _p[v] = EntryType();
2135                 }
2136                 
2137                 return pool(v);
2138             }
2139             return ent;
2140         }
2141     }
2142 }
2143 
2144 /** 
2145  * Represents a pool which takes
2146  * in a key of type `ValueType`.
2147  * If a corresponding `EntryType`
2148  * is mapped-to via said key then
2149  * it is returned, else it is
2150  * constructed on the spot, stored
2151  * and then finally returned
2152  *
2153  * If your `EntryType` is to
2154  * have its constructor called
2155  * _without_ taking in a single
2156  * argument of type `ValueType`
2157  * then set the `entryTakesValue_onCtor`
2158  * to `false`. It is `true` by
2159  * default.
2160  *
2161  * The result of pooling an `EntryType`
2162  * which is a struct-type is that
2163  * it will have a pointer returned,
2164  * i.e. an `EntryType*`
2165  */
2166 public struct Pool(EntryType, ValueType, bool entryTakesValue_onCtor = true)
2167 if
2168 (
2169     isClassType!(EntryType) ||
2170     isStructType!(EntryType)
2171 )
2172 {
2173     private EntryType[ValueType] _p;
2174 
2175     /** 
2176      * Clears the entry that would
2177      * be mapped to by the given
2178      * key
2179      *
2180      * Params:
2181      *   v = the key
2182      * Returns: `true` if it existed,
2183      * `false` otheriwse
2184      */
2185     public bool clear(ValueType v)
2186     {
2187         EntryType* ent = v in _p;
2188         if(ent is null)
2189         {
2190             return false;
2191         }
2192 
2193         _p.remove(v);
2194 
2195         return true;
2196     }
2197 
2198     mixin Methods!(EntryType);
2199 }
2200 
2201 /**
2202  * General usage of the pool
2203  */
2204 unittest
2205 {
2206     Pool!(DNode, int) d;
2207     static assert(__traits(compiles, Pool!(DNode, int)()));
2208     
2209 
2210     // Initial pool creates the node
2211     DNode p1 = d.pool(1);
2212     assert(p1 !is null);
2213 
2214     // Pooling again by said value
2215     // should now return the same
2216     // exact object
2217     DNode p2 = d.pool(1);
2218     assert(p2 is p1);
2219 
2220     // Any other value makes an entirely
2221     // new node
2222     DNode p3 = d.pool(2);
2223     assert(p3 !is p2);
2224 
2225     // Class-based type without a `this(ValueType)` ctor
2226     static assert(__traits(compiles, Pool!(ClassWithoutSimpleThis, int)()) == false);
2227 
2228     // Only class types and struct types are supported
2229     static assert(__traits(compiles, Pool!(int, int)()) == false);
2230 }
2231 
2232 /**
2233  * Tests out the clearing of
2234  * an entry by its key
2235  */
2236 unittest
2237 {
2238     Pool!(Thing, int) p;
2239     Thing t1 = p.pool(1);
2240     assert(t1);
2241 
2242     Thing t2 = p.pool(1);
2243     assert(t2 is t1);
2244 
2245     // Clear, now pooling should give different address
2246     p.clear(1);
2247 
2248     Thing t3 = p.pool(1);
2249     assert(t3 !is t2);
2250 }
2251 
2252 /**
2253  * Tests usage of a type that
2254  * consumes no `ValueType`
2255  * on construction of itself
2256  * (`EntryType` has a zero-arity
2257  * constructor)
2258  */
2259 unittest
2260 {
2261     static assert(__traits(compiles,  Pool!(ArglessThing, int, false)));
2262 
2263     Pool!(ArglessThing, int, false) p;
2264     ArglessThing t1 = p.pool(1);
2265     assert(t1);
2266     
2267     ArglessThing t2 = p.pool(1);
2268     assert(t2 is t1);
2269 }
2270 
2271 /**
2272  * Tests pooling with an `EntryType`
2273  * which is a struct type
2274  */
2275 unittest
2276 {
2277     // Struct-based types are supported
2278     struct P {}
2279     static assert(__traits(compiles, Pool!(P, int, false)()) == true);
2280 
2281     Pool!(Person, int) p;
2282     Person* t1 = p.pool(1);
2283     assert(t1);
2284     
2285     Person* t2 = p.pool(1);
2286     assert(t2 is t1);
2287     assert(t2.uniq == 1);
2288 
2289     Person* t3 = p.pool(2);
2290     assert(t3);
2291     assert(t3.uniq == 2);
2292     assert(t3 !is t2);
2293 }