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