The OpenD Programming Language

1 module mir.ndslice.internal;
2 
3 import mir.internal.utility : isFloatingPoint, Iota;
4 import mir.math.common: fmamath;
5 import mir.ndslice.iterator: IotaIterator;
6 import mir.ndslice.slice;
7 import mir.primitives;
8 import std.meta;
9 import std.traits;
10 
11 @fmamath:
12 
13 template ConstIfPointer(T)
14 {
15     static if (isPointer!T)
16         alias ConstIfPointer = const(PointerTarget!T)*;
17     else
18         alias ConstIfPointer = T;
19 }
20 
21 ///
22 public import mir.utility: _expect;
23 
24 struct RightOp(string op, T)
25 {
26     T value;
27 
28     auto lightConst()() const @property
29     {
30         import mir.qualifier;
31         return RightOp!(op, LightConstOf!T)(value.lightConst);
32     }
33 
34     auto lightImmutable()() immutable @property
35     {
36         import mir.qualifier;
37         return RightOp!(op, LightImmutableOf!T)(value.lightImmutable);
38     }
39 
40     this()(ref T v) { value = v; }
41     this()(T v) { value = v; }
42     auto ref opCall(F)(auto ref F right)
43     {
44         static if (op == "^^" && isNumeric!T && isFloatingPoint!F)
45         {
46             import mir.math.common: pow;
47             return pow(value, right);
48         }
49         else
50         {
51             return mixin("value " ~ op ~ " right");
52         }
53     }
54 }
55 
56 struct LeftOp(string op, T)
57 {
58     T value;
59 
60     auto lightConst()() const @property
61     {
62         import mir.qualifier;
63         return LeftOp!(op, LightConstOf!T)(value.lightConst);
64     }
65 
66     auto lightImmutable()() immutable @property
67     {
68         import mir.qualifier;
69         return LeftOp!(op, LightImmutableOf!T)(value.lightImmutable);
70     }
71 
72     this()(ref T v) { value = v; }
73     this()(T v) { value = v; }
74     auto ref opCall(F)(auto ref F left)
75     {
76         static if (op == "^^" && isFloatingPoint!T && isNumeric!F)
77         {
78             import mir.math.common: pow;
79             return pow(left, value);
80         }
81         else
82         {
83             return mixin("left " ~ op ~ " value");
84         }
85     }
86 }
87 
88 private template _prod(size_t len)
89     if (len)
90 {
91     static if (len == 1)
92         enum _prod = "elems[0]";
93     else
94     {
95         enum i = len - 1;
96         enum _prod = ._prod!i ~ " * elems[" ~ i.stringof ~ "]";
97     }
98 }
99 
100 auto product(Elems...)(auto ref Elems elems)
101 {   
102     return mixin(_prod!(Elems.length));
103 }
104 
105 
106 template _iotaArgs(size_t length, string prefix, string suffix)
107 {
108     static if (length)
109     {
110         enum i = length - 1;
111         enum _iotaArgs = _iotaArgs!(i, prefix, suffix) ~ prefix ~ i.stringof ~ suffix;
112     }
113     else
114         enum _iotaArgs = "";
115 }
116 
117 alias _IteratorOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = Iterator;
118 
119 E maxElem(E)(E[] arr...)
120 {
121     auto ret = Unqual!E.min;
122     foreach(e; arr)
123         if (e > ret)
124             ret = e;
125     return ret;
126 }
127 
128 E minElem(E)(E[] arr...)
129 {
130     auto ret = Unqual!E.max;
131     foreach(e; arr)
132         if (e < ret)
133             ret = e;
134     return ret;
135 }
136 
137 size_t sum()(size_t[] packs)
138 {
139     size_t s;
140     foreach(pack; packs)
141         s += pack;
142     return s;
143 }
144 
145 
146 size_t[] reverse()(size_t[] ar)
147 {
148     foreach(i, e; ar[0..$/2])
149     {
150         ar[i] = ar[$ - i - 1];
151         ar[$ - i - 1] = e;
152     }
153     return ar;
154 }
155 
156 enum indexError(DeepElement, int pos, int N) =
157     N.stringof ~ "D slice of " ~ DeepElement.stringof ~ ": bounds check failed at " ~ (pos + 1).stringof ~ " dimension";
158 
159 enum string tailErrorMessage(
160     string fun = __FUNCTION__,
161     string pfun = __PRETTY_FUNCTION__) =
162 "
163 - - -
164 Error in function
165 " ~ fun ~ "
166 - - -
167 Function prototype
168 " ~ pfun ~ "
169 _____";
170 
171 mixin template DimensionsCountCTError()
172 {
173     static assert(Dimensions.length <= N,
174         "Dimensions list length = " ~ Dimensions.length.stringof
175         ~ " should be less than or equal to N = " ~ N.stringof
176         ~ tailErrorMessage!());
177 }
178 
179 enum DimensionsCountRTError = q{
180     assert(dimensions.length <= N,
181         "Dimensions list length should be less than or equal to N = " ~ N.stringof
182         ~ tailErrorMessage!());
183 };
184 
185 mixin template DimensionCTError()
186 {
187     static assert(dimension >= 0,
188         "dimension = " ~ dimension.stringof ~ " at position "
189         ~ i.stringof ~ " should be greater than or equal to 0"
190         ~ tailErrorMessage!());
191     static assert(dimension < N,
192         "dimension = " ~ dimension.stringof ~ " at position "
193         ~ i.stringof ~ " should be less than N = " ~ N.stringof
194         ~ tailErrorMessage!());
195     static assert(dimension < slice.S,
196         "dimension = " ~ dimension.stringof ~ " at position "
197         ~ i.stringof ~ " should be less than " ~ (slice.S).stringof ~ ". "
198         ~ "`universal` and `canonical` from `mir.ndslice.topology` can be used to relax slice kind."
199         ~ tailErrorMessage!());
200 }
201 
202 enum DimensionRTError = q{
203     static if (isSigned!(typeof(dimension)))
204     assert(dimension >= 0, "dimension should be greater than or equal to 0"
205         ~ tailErrorMessage!());
206     assert(dimension < N, "dimension should be less than N = " ~ N.stringof
207         ~ tailErrorMessage!());
208     assert(dimension < slice.S,
209         "dimension should be less than " ~ slice.S.stringof ~ ". "
210         ~ "`universal` and `canonical` from `mir.ndslice.topology` can be used to relax slice kind."
211         ~ tailErrorMessage!());
212 };
213 
214 private alias IncFront(Seq...) = AliasSeq!(Seq[0] + 1, Seq[1 .. $]);
215 
216 private alias DecFront(Seq...) = AliasSeq!(Seq[0] - 1, Seq[1 .. $]);
217 
218 private enum bool isNotZero(alias t) = t != 0;
219 
220 alias NSeqEvert(Seq...) = Filter!(isNotZero, DecFront!(Reverse!(IncFront!Seq)));
221 
222 //alias Parts(Seq...) = DecAll!(IncFront!Seq);
223 
224 alias Snowball(Seq...) = AliasSeq!(size_t.init, SnowballImpl!(size_t.init, Seq));
225 
226 private template SnowballImpl(size_t val, Seq...)
227 {
228     static if (Seq.length == 0)
229         alias SnowballImpl = AliasSeq!();
230     else
231         alias SnowballImpl = AliasSeq!(Seq[0] + val, SnowballImpl!(Seq[0] +  val, Seq[1 .. $]));
232 }
233 
234 private template DecAll(Seq...)
235 {
236     static if (Seq.length == 0)
237         alias DecAll = AliasSeq!();
238     else
239         alias DecAll = AliasSeq!(Seq[0] - 1, DecAll!(Seq[1 .. $]));
240 }
241 
242 //template SliceFromSeq(Range, Seq...)
243 //{
244 //    static if (Seq.length == 0)
245 //        alias SliceFromSeq = Range;
246 //    else
247 //    {
248 //        import mir.ndslice.slice : Slice;
249 //        alias SliceFromSeq = SliceFromSeq!(Slice!(Seq[$ - 1], Range), Seq[0 .. $ - 1]);
250 //    }
251 //}
252 
253 template DynamicArrayDimensionsCount(T)
254 {
255     static if (isDynamicArray!T)
256         enum size_t DynamicArrayDimensionsCount = 1 + DynamicArrayDimensionsCount!(typeof(T.init[0]));
257     else
258         enum size_t DynamicArrayDimensionsCount = 0;
259 }
260 
261 bool isPermutation(size_t N)(auto ref const scope size_t[N] perm)
262 {
263     int[N] mask;
264     return isValidPartialPermutationImpl(perm, mask);
265 }
266 
267 version(mir_ndslice_test) unittest
268 {
269     assert(isPermutation([0, 1]));
270     // all numbers 0..N-1 need to be part of the permutation
271     assert(!isPermutation([1, 2]));
272     assert(!isPermutation([0, 2]));
273     // duplicates are not allowed
274     assert(!isPermutation([0, 1, 1]));
275 
276     size_t[0] emptyArr;
277     // empty permutations are not allowed either
278     assert(!isPermutation(emptyArr));
279 }
280 
281 bool isValidPartialPermutation(size_t N)(in size_t[] perm)
282 {
283     int[N] mask;
284     return isValidPartialPermutationImpl(perm, mask);
285 }
286 
287 private bool isValidPartialPermutationImpl(size_t N)(in size_t[] perm, ref int[N] mask)
288 {
289     if (perm.length == 0)
290         return false;
291     foreach (j; perm)
292     {
293         if (j >= N)
294             return false;
295         if (mask[j]) //duplicate
296             return false;
297         mask[j] = true;
298     }
299     return true;
300 }
301 
302 template ShiftNegativeWith(size_t N)
303 {
304     enum ShiftNegativeWith(sizediff_t i) = i < 0 ? i + N : i;
305 }
306 
307 enum toSize_t(size_t i) = i;
308 enum toSizediff_t(sizediff_t i) = i;
309 enum isSize_t(alias i) = is(typeof(i) == size_t);
310 enum isSizediff_t(alias i) = is(typeof(i) == sizediff_t);
311 enum isIndex(I) = is(I : size_t);
312 template is_Slice(S)
313 {
314     static if (is(S : Slice!(IotaIterator!I), I))
315         enum is_Slice = __traits(isIntegral, I);
316     else
317         enum is_Slice = false;
318 }
319 
320 alias Repeat(size_t N : 0, T...) = AliasSeq!();
321 
322 private enum isReference(P) =
323     hasIndirections!P
324     || isFunctionPointer!P
325     || is(P == interface);
326 
327 alias ImplicitlyUnqual(T) = Select!(isImplicitlyConvertible!(T, Unqual!T), Unqual!T, T);
328 alias ImplicitlyUnqual(T : T*) = T*;
329 
330 size_t lengthsProduct(size_t N)(auto ref const scope size_t[N] lengths)
331 {
332     size_t length = lengths[0];
333     foreach (i; Iota!(1, N))
334             length *= lengths[i];
335     return length;
336 }
337 
338 pure nothrow version(mir_ndslice_test) unittest
339 {
340     const size_t[3] lengths = [3, 4, 5];
341     assert(lengthsProduct(lengths) == 60);
342     assert(lengthsProduct([3, 4, 5]) == 60);
343 }
344 
345 package(mir) template strideOf(args...)
346 {
347     static if (args.length == 0)
348         enum strideOf = args;
349     else
350     {
351         @fmamath @property auto ref ls()()
352         {
353             import mir.ndslice.topology: stride;
354             return stride(args[0]);
355         }
356         alias strideOf = AliasSeq!(ls, strideOf!(args[1..$]));
357     }
358 }
359 
360 package(mir) template frontOf(size_t n)
361 {
362     enum frontOf = () {
363         string ret;
364         static foreach (i; 0 .. n)
365         {
366             if (i)
367                 ret ~= `, `;
368             ret ~= "slices[" ~ i.stringof ~ `].front`;
369         }
370         return ret;
371     } ();
372 }
373 
374 package(mir) template frontOf2(args...)
375 {
376     static if (args.length == 0)
377         enum frontOf2 = args;
378     else
379     {
380         @fmamath @property auto frontOf2Mod()()
381         {
382             return args[0].front;
383         }
384         alias frontOf2 = AliasSeq!(frontOf2Mod, frontOf2!(args[1..$]));
385     }
386 }
387 
388 package(mir) template backOf(args...)
389 {
390     static if (args.length == 0)
391         enum backOf = args;
392     else
393     {
394         @fmamath @property auto ref backOfMod()()
395         {
396             return args[0].back;
397         }
398         alias backOf = AliasSeq!(backOfMod, backOf!(args[1..$]));
399     }
400 }
401 
402 package(mir) template frontOfD(size_t dimension, args...)
403 {
404     static if (args.length == 0)
405         enum frontOfD = args;
406     else
407     {
408         @fmamath @property auto ref frontOfDMod()()
409         {
410             return args[0].front!dimension;
411         }
412         alias frontOfD = AliasSeq!(frontOfDMod, frontOfD!(dimension, args[1..$]));
413     }
414 }
415 
416 package(mir) template backOfD(size_t dimension, args...)
417 {
418     static if (args.length == 0)
419         enum backOfD = args;
420     else
421     {
422         @fmamath @property auto ref backOfDMod()()
423         {
424             return args[0].back!dimension;
425         }
426         alias backOfD = AliasSeq!(backOfDMod, backOfD!(dimension, args[1..$]));
427     }
428 }
429 
430 package(mir) template frontOfDim(size_t dim, args...)
431 {
432     static if (args.length == 0)
433         enum frontOfDim = args;
434     else
435     {
436         alias arg = args[0];
437         @fmamath @property auto ref frontOfDimMod()
438         {
439             return arg.front!dim;
440         }
441         alias frontOfDim = AliasSeq!(frontOfDimMod, frontOfDim!(dim, args[1..$]));
442     }
443 }
444 
445 package(mir) template selectFrontOf(alias input, args...)
446 {
447     static if (args.length == 0)
448         enum selectFrontOf = args;
449     else
450     {
451         alias arg = args[0];
452         @fmamath @property auto ref selectFrontOfMod()()
453         {
454             return arg.lightScope.selectFront!0(input);
455         }
456         alias selectFrontOf = AliasSeq!(selectFrontOfMod, selectFrontOf!(input, args[1..$]));
457     }
458 }
459 
460 package(mir) template selectBackOf(alias input, args...)
461 {
462     static if (args.length == 0)
463         enum selectBackOf = args;
464     else
465     {
466         alias arg = args[0];
467         @fmamath @property auto ref selectBackOfMod()()
468         {
469             return arg.selectBack!0(input);
470         }
471         alias selectBackOf = AliasSeq!(selectBackOfMod, selectBackOf!(input, args[1..$]));
472     }
473 }
474 
475 package(mir) template frontSelectFrontOf(alias input, args...)
476 {
477     static if (args.length == 0)
478         enum frontSelectFrontOf = args;
479     else
480     {
481         alias arg = args[0];
482         @fmamath @property auto ref frontSelectFrontOfMod()()
483         {
484             return arg.lightScope.front.selectFront!0(input);
485         }
486         alias frontSelectFrontOf = AliasSeq!(frontSelectFrontOfMod, frontSelectFrontOf!(input, args[1..$]));
487     }
488 }
489 
490 package(mir) template frontSelectBackOf(alias input, args...)
491 {
492     static if (args.length == 0)
493         enum frontSelectBackOf = args;
494     else
495     {
496         alias arg = args[0];
497         @fmamath @property auto ref frontSelectBackOfMod
498         ()()
499         {
500             return arg.lightScope.front.selectBack!0(input);
501         }
502         alias frontSelectBackOf = AliasSeq!(frontSelectBackOfMod
503         , frontSelectBackOf!(input, args[1..$]));
504     }
505 }