1 /// Comptime algorithm templates for working with `AliasSeq`.
2 module tern.meta;
3 
4 // TODO: Function map try reinterpret
5 public import std.meta : Alias, AliasSeq, aliasSeqOf, Erase, EraseAll, NoDuplicates, Stride,
6     DerivedToFront, MostDerived, Repeat, Replace, ReplaceAll, Reverse, staticSort,
7     templateAnd, templateNot, templateOr, ApplyLeft, ApplyRight;
8 import tern.traits;
9 import std.string;
10 import std.conv;
11 
12 public template seqLoad(A...)
13 {
14     auto seqLoad(size_t index)
15     {
16         switch (index)
17         {
18             static foreach (j, B; A)
19             {
20                 mixin("case "~j.to!string~":
21                 return A["~j.to!string~"];");
22             }
23             default: assert(0);
24         }
25     }
26 }
27 
28 /// Checks if an `AliasSeq` contains an alias.
29 enum seqContains(A...) =
30 {
31     static if (A.length != 0)
32     static foreach (C; A[1..$])
33     {
34         static if (isSame!(C, A[0]))
35             return true;
36     }
37     return false;
38 }();
39 
40 public template seqIndexOf(A...)
41 {
42     enum seqIndexOf =
43     {
44         static if (A.length != 0)
45         static foreach (i, C; A[1..$])
46         {
47             static if (isSame!(C, A[0]))
48                 return i;
49         }
50         return -1;
51     }();
52 }
53 
54 /// Filters over an `AliasSeq` based on a predicate.
55 public template seqFilter(A...)
56 {
57     alias seqFilter = AliasSeq!();
58     alias F = A[0];
59 
60     static if (A.length != 0)
61     static foreach (B; A[1..$])
62     {
63         static if (F!B)
64             seqFilter = AliasSeq!(seqFilter, B);
65     }
66 }
67 
68 /// Filters over an `AliasSeq` based on a string predicate.
69 public template seqFilter(string F, A...)
70 {
71     alias seqFilter = AliasSeq!();
72     
73     private template filter(size_t I, alias X) 
74     { 
75         static if (mixin(F)) 
76             alias filter = X; 
77         else
78             alias filter = AliasSeq!();
79     }
80 
81     static foreach (i, B; A)
82         seqFilter = AliasSeq!(seqFilter, filter!(i, B));
83 }
84 
85 /// Maps a template over an `AliasSeq`, returning an `AliasSeq` of all of the return values.
86 public template seqMap(A...)
87 {
88     alias seqMap = AliasSeq!();
89     alias F = A[0];
90 
91     static if (A.length != 0)
92     static foreach (B; A[1..$])
93         seqMap = AliasSeq!(seqMap, F!B);
94 }
95 
96 /// Maps a string over an `AliasSeq`, returning an `AliasSeq` of all of the return values.
97 public template seqMap(string F, A...)
98 {
99     alias seqMap = AliasSeq!();
100 
101     private template map(size_t I, alias X) 
102     { 
103         static if (__traits(compiles, { alias map = mixin(F); }))
104             alias map = mixin(F);
105         else
106             enum map = mixin(F);
107     }
108 
109     static foreach (i, B; A)
110         seqMap = AliasSeq!(seqMap, map!(i, B));
111 }
112 
113 /// True if all elements in `A` meet the first given predicate.
114 public enum seqAny(A...) =
115 {
116     return seqFilter!A.length == A.length - 1;
117 }();
118 
119 /// True if any elements in `A` meet the first given predicate.
120 public enum seqAny(A...) =
121 {
122     return seqFilter!A.length != 0;
123 }();
124 
125 /// Creates a string representing `A` using the given separator.
126 enum seqStringJoin(string SEPARATOR, A...) =
127 {
128     pragma(msg, A);
129     static if (A.length == 0)
130         return "";
131 
132     string ret;
133     foreach (i, B; A)
134     {
135         static if (__traits(compiles, { enum _ = B; }))
136             ret ~= B.to!string~(i == A.length - 1 ? null : SEPARATOR);
137         else
138             ret ~= B.stringof~(i == A.length - 1 ? null : SEPARATOR);
139     }
140     return ret[0..$];
141 }();
142 
143 /// Checks if two aliases are identical.
144 // Ripped from `std.meta`.
145 public template isSame(alias A, alias B)
146 {
147     static if (!is(typeof(&A && &B)) // at least one is an rvalue
148             && __traits(compiles, { enum isSame = A == B; })) // c-t comparable
149         enum isSame = A == B;
150     else
151         enum isSame = __traits(isSame, A, B) || A.stringof == B.stringof;
152 }
153 
154 /** 
155  * Generates a random boolean with the odds `1/max`.
156  *
157  * Params:
158  *  max = Maximum odds, this is what the chance is out of.
159  */
160 public alias randomBool(uint max, uint seed = uint.max, uint R0 = __LINE__, string R1 = __TIMESTAMP__, string R2 = __FILE_FULL_PATH__, string R3 = __FUNCTION__) 
161     = Alias!(random!(uint, 0, max, seed, R0, R1, R2, R3) == 0);
162 
163 /** 
164  * Generates a random floating point value.
165  *
166  * Params:
167  *  min = Minimum value.
168  *  max = Maximum value.
169  *  seed = The seed to generate with, useful if you do multiple random generations in one line, as it causes entropy.
170  */
171 public template random(T, T min, T max, uint seed = uint.max, uint R0 = __LINE__, string R1 = __TIMESTAMP__, string R2 = __FILE_FULL_PATH__, string R3 = __FUNCTION__) 
172     if (is(T == float) || is(T == double))
173 {
174     pure T random()
175     {
176         return random!(ulong, cast(ulong)(min * cast(T)1000), cast(ulong)(max * cast(T)1000), seed, R0, R1, R2, R3) / cast(T)1000;
177     }
178 }
179 
180 /** 
181  * Generates a random integral value.
182  *
183  * Params:
184  *  min = Minimum value.
185  *  max = Maximum value.
186  *  seed = The seed to generate with, useful if you do multiple random generations in one line, as it causes entropy.
187  */
188 public template random(T, T min, T max, uint seed = uint.max, uint R0 = __LINE__, string R1 = __TIMESTAMP__, string R2 = __FILE_FULL_PATH__, string R3 = __FUNCTION__)
189     if (isIntegral!T)
190 {
191     pure T random()
192     {
193         static if (min == max)
194             return min;
195 
196         ulong s0 = (seed * R0) || 1;
197         ulong s1 = (seed * R0) || 1;
198         ulong s2 = (seed * R0) || 1;
199         
200         static foreach (c; R1)
201             s0 *= (c * (R0 ^ seed)) || 1;
202         static foreach (c; R2)
203             s1 *= (c * (R0 - seed)) || 1;
204         static foreach (c; R3)
205             s2 *= (c * (R0 ^ seed)) || 1;
206         
207         ulong o = s0 + s1 + s2;
208         return min + (cast(T)o % (max - min));
209     }
210 }