1 // Written in the D programming language.
2 /**
3 This module contains generic _iteration algorithms.
4 $(SCRIPT inhibitQuickIndex = 1;)
5 
6 $(BOOKTABLE $(H2 Function),
7 $(TR $(TH Function Name) $(TH Description))
8 $(T2 all, Checks if all elements satisfy to a predicate.)
9 $(T2 any, Checks if at least one element satisfy to a predicate.)
10 $(T2 cmp, Compares two slices.)
11 $(T2 count, Counts elements in a slices according to a predicate.)
12 $(T2 each, Iterates elements.)
13 $(T2 eachLower, Iterates lower triangle of matrix.)
14 $(T2 eachOnBorder, Iterates elementes on tensors borders and corners.)
15 $(T2 eachUploPair, Iterates upper and lower pairs of elements in square matrix.)
16 $(T2 eachUpper, Iterates upper triangle of matrix.)
17 $(T2 equal, Compares two slices for equality.)
18 $(T2 filter, Filters elements in a range or an ndslice.)
19 $(T2 find, Finds backward index.)
20 $(T2 findIndex, Finds index.)
21 $(T2 fold, Accumulates all elements (different parameter order than `reduce`).)
22 $(T2 isSymmetric, Checks if the matrix is symmetric.)
23 $(T2 maxIndex, Finds index of the maximum.)
24 $(T2 maxPos, Finds backward index of the maximum.)
25 $(T2 minIndex, Finds index of the minimum.)
26 $(T2 minmaxIndex, Finds indices of the minimum and the maximum.)
27 $(T2 minmaxPos, Finds backward indices of the minimum and the maximum.)
28 $(T2 minPos, Finds backward index of the minimum.)
29 $(T2 nBitsToCount, Сount bits until set bit count is reached.)
30 $(T2 reduce, Accumulates all elements.)
31 $(T2 Chequer, Chequer color selector to work with $(LREF each) .)
32 $(T2 uniq, Iterates over the unique elements in a range or an ndslice, which is assumed sorted.)
33 )
34 
35 Transform function is represented by $(NDSLICEREF topology, map).
36 
37 All operators are suitable to change slices using `ref` argument qualification in a function declaration.
38 Note, that string lambdas in Mir are `auto ref` functions.
39 
40 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
41 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
42 Authors: Ilya Yaroshenko, John Michael Hall, Andrei Alexandrescu (original Phobos code)
43 
44 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
45 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
46 
47 Authors: , Ilya Yaroshenko (Mir & BetterC rework).
48 Source: $(PHOBOSSRC std/algorithm/_iteration.d)
49 Macros:
50     NDSLICEREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
51     T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
52  */
53 module mir.algorithm.iteration;
54 
55 import mir.functional: naryFun;
56 import mir.internal.utility;
57 import mir.math.common: optmath;
58 import mir.ndslice.field: BitField;
59 import mir.ndslice.internal;
60 import mir.ndslice.iterator: FieldIterator, RetroIterator;
61 import mir.ndslice.slice;
62 import mir.primitives;
63 import mir.qualifier;
64 import std.meta;
65 import std.traits;
66 
67 /++
68 Chequer color selector to work with $(LREF each)
69 +/
70 enum Chequer : bool
71 {
72     /// Main diagonal color
73     black,
74     /// First sub-diagonal color
75     red,
76 }
77 
78 ///
79 version(mir_test) unittest
80 {
81     import mir.ndslice.allocation: slice;
82     auto s = [5, 4].slice!int;
83 
84     Chequer.black.each!"a = 1"(s);
85     assert(s == [
86         [1, 0, 1, 0],
87         [0, 1, 0, 1],
88         [1, 0, 1, 0],
89         [0, 1, 0, 1],
90         [1, 0, 1, 0],
91     ]);
92 
93     Chequer.red.each!((ref b) => b = 2)(s);
94     assert(s == [
95         [1, 2, 1, 2],
96         [2, 1, 2, 1],
97         [1, 2, 1, 2],
98         [2, 1, 2, 1],
99         [1, 2, 1, 2],
100     ]);
101 
102 }
103 
104 @optmath:
105 
106 /+
107 Bitslice representation for accelerated bitwise algorithm.
108 1-dimensional contiguousitslice can be split into three chunks: head bits, body chunks, and tail bits.
109 
110 Bitslice can have head bits because it has slicing and the zero bit may not be aligned to the zero of a body chunk.
111 +/
112 private struct BitSliceAccelerator(Field, I = typeof(Field.init[size_t.init]))
113     if (__traits(isUnsigned, I))
114 {
115     import mir.bitop;
116     import mir.qualifier: lightConst;
117     import mir.ndslice.traits: isIterator;
118     import mir.ndslice.iterator: FieldIterator;
119     import mir.ndslice.field: BitField;
120 
121     ///
122     alias U = typeof(I + 1u);
123     /// body bits chunks
124     static if (isIterator!Field)
125         Slice!Field bodyChunks;
126     else
127         Slice!(FieldIterator!Field) bodyChunks;
128     /// head length
129     int headLength;
130     /// tail length
131     int tailLength;
132 
133 @optmath:
134 
135     this(Slice!(FieldIterator!(BitField!(Field, I))) slice)
136     {
137         enum mask = bitShiftMask!I;
138         enum shift = bitElemShift!I;
139         size_t length = slice.length;
140         size_t index = slice._iterator._index;
141         if (auto hlen = index & mask)
142         {
143             auto l = I.sizeof * 8 - hlen;
144             if (l > length)
145             {
146                 // central problem
147                 headLength = -cast(int) length;
148                 tailLength = cast(int) hlen;
149                 goto F;
150             }
151             else
152             {
153                 headLength = cast(uint) l;
154                 length -= l;
155                 index += l;
156             }
157         }
158         tailLength = cast(int) (length & mask);
159     F:
160         length >>= shift;
161         index >>= shift;
162         bodyChunks._lengths[0] = length;
163         static if (isIterator!Field)
164         {
165             bodyChunks._iterator = slice._iterator._field._field;
166             bodyChunks._iterator += index;
167         }
168         else
169         {
170             bodyChunks._iterator._index = index;
171             bodyChunks._iterator._field = slice._iterator._field._field;
172         }
173     }
174 
175 scope const:
176 
177     bool isCentralProblem()
178     {
179         return headLength < 0;
180     }
181 
182     U centralBits()
183     {
184         assert(isCentralProblem);
185         return *bodyChunks._iterator.lightConst >>> tailLength;
186     }
187 
188     uint centralLength()
189     {
190         assert(isCentralProblem);
191         return -headLength;
192     }
193 
194     /// head bits (last `headLength` bits are valid).
195     U headBits()
196     {
197         assert(!isCentralProblem);
198         if (headLength == 0)
199             return U.init;
200         static if (isIterator!Field)
201             return bodyChunks._iterator.lightConst[-1];
202         else
203             return bodyChunks._iterator._field.lightConst[bodyChunks._iterator._index - 1];
204     }
205 
206     /// tail bits (first `tailLength` bits are valid).
207     U tailBits()
208     {
209         assert(!isCentralProblem);
210         if (tailLength == 0)
211             return U.init;
212         static if (isIterator!Field)
213             return bodyChunks._iterator.lightConst[bodyChunks.length];
214         else
215             return bodyChunks._iterator._field.lightConst[bodyChunks._iterator._index + bodyChunks.length];
216     }
217 
218     U negCentralMask()
219     {
220         return U.max << centralLength;
221     }
222 
223     U negHeadMask()
224     {
225         return U.max << headLength;
226     }
227 
228     U negTailMask()
229     {
230         return U.max << tailLength;
231     }
232 
233     U negCentralMaskS()
234     {
235         return U.max >> centralLength;
236     }
237 
238     U negHeadMaskS()
239     {
240         return U.max >> headLength;
241     }
242 
243     U negTailMaskS()
244     {
245         return U.max >> tailLength;
246     }
247 
248     U centralBitsWithRemainingZeros()
249     {
250         return centralBits & ~negCentralMask;
251     }
252 
253     U centralBitsWithRemainingZerosS()
254     {
255         return centralBits << (U.sizeof * 8 - centralLength);
256     }
257 
258     U headBitsWithRemainingZeros()
259     {
260         return headBits >>> (I.sizeof * 8 - headLength);
261     }
262 
263     U headBitsWithRemainingZerosS()
264     {
265         static if (U.sizeof > I.sizeof)
266             return (headBits << (U.sizeof - I.sizeof) * 8) & ~negTailMaskS;
267         else
268             return headBits & ~negTailMaskS;
269     }
270 
271     U tailBitsWithRemainingZeros()
272     {
273         return tailBits & ~negTailMask;
274     }
275 
276     U tailBitsWithRemainingZerosS()
277     {
278         return tailBits << (U.sizeof * 8 - tailLength);
279     }
280 
281     U centralBitsWithRemainingOnes()
282     {
283         return centralBits | negCentralMask;
284     }
285 
286     U centralBitsWithRemainingOnesS()
287     {
288         return centralBitsWithRemainingZerosS | negCentralMaskS;
289     }
290 
291     U headBitsWithRemainingOnes()
292     {
293         return headBitsWithRemainingZeros | negHeadMask;
294     }
295 
296     U headBitsWithRemainingOnesS()
297     {
298         return headBitsWithRemainingZerosS | negHeadMaskS;
299     }
300 
301     U tailBitsWithRemainingOnes()
302     {
303         return tailBits | negTailMask;
304     }
305 
306     U tailBitsWithRemainingOnesS()
307     {
308         return tailBitsWithRemainingZerosS | negTailMaskS;
309     }
310 
311     size_t ctpop()
312     {
313         import mir.bitop: ctpop;
314         if (isCentralProblem)
315             return centralBitsWithRemainingZeros.ctpop;
316         size_t ret;
317         if (headLength)
318             ret = cast(size_t) headBitsWithRemainingZeros.ctpop;
319         if (bodyChunks.length)
320         {
321             auto bc = bodyChunks.lightConst;
322             do
323             {
324                 ret += cast(size_t) bc.front.ctpop;
325                 bc.popFront;
326             }
327             while(bc.length);
328         }
329         if (tailBits)
330             ret += cast(size_t) tailBitsWithRemainingZeros.ctpop;
331         return ret;
332     }
333 
334     bool any()
335     {
336         if (isCentralProblem)
337             return centralBitsWithRemainingZeros != 0;
338         if (headBitsWithRemainingZeros != 0)
339             return true;
340         if (bodyChunks.length)
341         {
342             auto bc = bodyChunks.lightConst;
343             do
344             {
345                 if (bc.front != 0)
346                     return true;
347                 bc.popFront;
348             }
349             while(bc.length);
350         }
351         if (tailBitsWithRemainingZeros != 0)
352             return true;
353         return false;
354     }
355 
356     bool all()
357     {
358         if (isCentralProblem)
359             return centralBitsWithRemainingOnes != U.max;
360         size_t ret;
361         if (headBitsWithRemainingOnes != U.max)
362             return false;
363         if (bodyChunks.length)
364         {
365             auto bc = bodyChunks.lightConst;
366             do
367             {
368                 if (bc.front != I.max)
369                     return false;
370                 bc.popFront;
371             }
372             while(bc.length);
373         }
374         if (tailBitsWithRemainingOnes != U.max)
375             return false;
376         return true;
377     }
378 
379     size_t cttz()
380     {
381         U v;
382         size_t ret;
383         if (isCentralProblem)
384         {
385             v = centralBitsWithRemainingOnes;
386             if (v)
387                 goto R;
388             ret = centralLength;
389             goto L;
390         }
391         v = headBitsWithRemainingOnes;
392         if (v)
393             goto R;
394         ret = headLength;
395         if (bodyChunks.length)
396         {
397             auto bc = bodyChunks.lightConst;
398             do
399             {
400                 v = bc.front;
401                 if (v)
402                     goto R;
403                 ret += I.sizeof * 8;
404                 bc.popFront;
405             }
406             while(bc.length);
407         }
408         v = tailBitsWithRemainingOnes;
409         if (v)
410             goto R;
411         ret += tailLength;
412         goto L;
413     R:
414         ret += v.cttz;
415     L:
416         return ret;
417     }
418 
419     size_t ctlz()
420     {
421         U v;
422         size_t ret;
423         if (isCentralProblem)
424         {
425             v = centralBitsWithRemainingOnes;
426             if (v)
427                 goto R;
428             ret = centralLength;
429             goto L;
430         }
431         v = tailBitsWithRemainingOnesS;
432         if (v)
433             goto R;
434         ret = tailLength;
435         if (bodyChunks.length)
436         {
437             auto bc = bodyChunks.lightConst;
438             do
439             {
440                 v = bc.back;
441                 if (v)
442                     goto R;
443                 ret += I.sizeof * 8;
444                 bc.popBack;
445             }
446             while(bc.length);
447         }
448         v = headBitsWithRemainingOnesS;
449         if (v)
450             goto R;
451         ret += headLength;
452         goto L;
453     R:
454         ret += v.ctlz;
455     L:
456         return ret;
457     }
458 
459     sizediff_t nBitsToCount(size_t count)
460     {
461         size_t ret;
462         if (count == 0)
463             return count;
464         U v, cnt;
465         if (isCentralProblem)
466         {
467             v = centralBitsWithRemainingZeros;
468             goto E;
469         }
470         v = headBitsWithRemainingZeros;
471         cnt = v.ctpop;
472         if (cnt >= count)
473             goto R;
474         ret += headLength;
475         count -= cast(size_t) cnt;
476         if (bodyChunks.length)
477         {
478             auto bc = bodyChunks.lightConst;
479             do
480             {
481                 v = bc.front;
482                 cnt = v.ctpop;
483                 if (cnt >= count)
484                     goto R;
485                 ret += I.sizeof * 8;
486                 count -= cast(size_t) cnt;
487                 bc.popFront;
488             }
489             while(bc.length);
490         }
491         v = tailBitsWithRemainingZeros;
492     E:
493         cnt = v.ctpop;
494         if (cnt >= count)
495             goto R;
496         return -1;
497     R:
498         return ret + v.nTrailingBitsToCount(count);
499     }
500 
501     sizediff_t retroNBitsToCount(size_t count)
502     {
503         if (count == 0)
504             return count;
505         size_t ret;
506         U v, cnt;
507         if (isCentralProblem)
508         {
509             v = centralBitsWithRemainingZerosS;
510             goto E;
511         }
512         v = tailBitsWithRemainingZerosS;
513         cnt = v.ctpop;
514         if (cnt >= count)
515             goto R;
516         ret += tailLength;
517         count -= cast(size_t) cnt;
518         if (bodyChunks.length)
519         {
520             auto bc = bodyChunks.lightConst;
521             do
522             {
523                 v = bc.back;
524                 cnt = v.ctpop;
525                 if (cnt >= count)
526                     goto R;
527                 ret += I.sizeof * 8;
528                 count -= cast(size_t) cnt;
529                 bc.popBack;
530             }
531             while(bc.length);
532         }
533         v = headBitsWithRemainingZerosS;
534     E:
535         cnt = v.ctpop;
536         if (cnt >= count)
537             goto R;
538         return -1;
539     R:
540         return ret + v.nLeadingBitsToCount(count);
541     }
542 }
543 
544 /++
545 Сount bits until set bit count is reached. Works with ndslices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
546 Returns: bit count if set bit count is reached or `-1` otherwise.
547 +/
548 sizediff_t nBitsToCount(Field, I)(Slice!(FieldIterator!(BitField!(Field, I))) bitSlice, size_t count)
549 {
550     return BitSliceAccelerator!(Field, I)(bitSlice).nBitsToCount(count);
551 }
552 
553 ///ditto
554 sizediff_t nBitsToCount(Field, I)(Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))) bitSlice, size_t count)
555 {
556     import mir.ndslice.topology: retro;
557     return BitSliceAccelerator!(Field, I)(bitSlice.retro).retroNBitsToCount(count);
558 }
559 
560 ///
561 pure unittest
562 {
563     import mir.ndslice.allocation: bitSlice;
564     import mir.ndslice.topology: retro;
565     auto s = bitSlice(1000);
566     s[50] = true;
567     s[100] = true;
568     s[200] = true;
569     s[300] = true;
570     s[400] = true;
571     assert(s.nBitsToCount(4) == 301);
572     assert(s.retro.nBitsToCount(4) == 900);
573 }
574 
575 private void checkShapesMatch(
576     string fun = __FUNCTION__,
577     string pfun = __PRETTY_FUNCTION__,
578     Slices...)
579     (scope ref const Slices slices)
580     if (Slices.length > 1)
581 {
582     enum msgShape = "all slices must have the same shape"  ~ tailErrorMessage!(fun, pfun);
583     enum N = slices[0].shape.length;
584     foreach (i, Slice; Slices)
585     {
586         static if (i == 0)
587             continue;
588         else
589         static if (slices[i].shape.length == N)
590             assert(slices[i].shape == slices[0].shape, msgShape);
591         else
592         {
593             import mir.ndslice.fuse: fuseShape;
594             static assert(slices[i].fuseShape.length >= N);
595             assert(cast(size_t[N])slices[i].fuseShape[0 .. N] == slices[0].shape, msgShape);
596         }
597     }
598 }
599 
600 
601 package(mir) template allFlattened(args...)
602 {
603     static if (args.length)
604     {
605         alias arg = args[0];
606         @optmath @property ls()()
607         {
608             import mir.ndslice.topology: flattened;
609             return flattened(arg);
610         }
611         alias allFlattened = AliasSeq!(ls, allFlattened!(args[1..$]));
612     }
613     else
614         alias allFlattened = AliasSeq!();
615 }
616 
617 private template areAllContiguousSlices(Slices...)
618 {
619     import mir.ndslice.traits: isContiguousSlice;
620      static if (allSatisfy!(isContiguousSlice, Slices))
621         enum areAllContiguousSlices = Slices[0].N > 1 && areAllContiguousSlicesImpl!(Slices[0].N, Slices[1 .. $]);
622      else
623         enum areAllContiguousSlices = false;
624 }
625 
626 private template areAllContiguousSlicesImpl(size_t N, Slices...)
627 {
628      static if (Slices.length == 0)
629         enum areAllContiguousSlicesImpl = true;
630      else
631         enum areAllContiguousSlicesImpl = Slices[0].N == N && areAllContiguousSlicesImpl!(N, Slices[1 .. $]);
632 }
633 
634 version(LDC) {}
635 else version(GNU) {}
636 else version (Windows) {}
637 else version (X86_64)
638 {
639     //Compiling with DMD for x86-64 for Linux & OS X with optimizations enabled,
640     //"Tensor mutation on-the-fly" unittest was failing. Disabling inlining
641     //caused it to succeed.
642     //TODO: Rework so this is unnecessary!
643     version = Mir_disable_inlining_in_reduce;
644 }
645 
646 version(Mir_disable_inlining_in_reduce)
647 {
648     private enum Mir_disable_inlining_in_reduce = true;
649 
650     private template _naryAliases(size_t n)
651     {
652         static if (n == 0)
653             enum _naryAliases = "";
654         else
655         {
656             enum i = n - 1;
657             enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n";
658         }
659     }
660 
661     private template nonInlinedNaryFun(alias fun)
662     {
663         import mir.math.common : optmath;
664         static if (is(typeof(fun) : string))
665         {
666             /// Specialization for string lambdas
667             @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
668                 if (args.length <= 26)
669             {
670                 pragma(inline,false);
671                 mixin(_naryAliases!(Args.length));
672                 return mixin(fun);
673             }
674         }
675         else static if (is(typeof(fun.opCall) == function))
676         {
677             @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
678                 if (is(typeof(fun.opCall(args))))
679             {
680                 pragma(inline,false);
681                 return fun.opCall(args);
682             }
683         }
684         else
685         {
686             @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
687                 if (is(typeof(fun(args))))
688             {
689                 pragma(inline,false);
690                 return fun(args);
691             }
692         }
693     }
694 }
695 else
696 {
697     private enum Mir_disable_inlining_in_reduce = false;
698 }
699 
700 S reduceImpl(alias fun, S, Slices...)(S seed, scope Slices slices)
701 {
702     do
703     {
704         static if (DimensionCount!(Slices[0]) == 1)
705             seed = fun(seed, frontOf!slices);
706         else
707             seed = .reduceImpl!fun(seed, frontOf!slices);
708         foreach_reverse(ref slice; slices)
709             slice.popFront;
710     }
711     while(!slices[0].empty);
712     return seed;
713 }
714 
715 /++
716 Implements the homonym function (also known as `accumulate`,
717 `compress`, `inject`, or `fold`) present in various programming
718 languages of functional flavor. The call `reduce!(fun)(seed, slice1, ..., sliceN)`
719 first assigns `seed` to an internal variable `result`,
720 also called the accumulator. Then, for each set of element `x1, ..., xN` in
721 `slice1, ..., sliceN`, `result = fun(result, x1, ..., xN)` gets evaluated. Finally,
722 `result` is returned.
723 
724 `reduce` allows to iterate multiple slices in the lockstep.
725 
726 Note:
727     $(NDSLICEREF topology, pack) can be used to specify dimensions.
728 Params:
729     fun = A function.
730 See_Also:
731     $(HTTP llvm.org/docs/LangRef.html#fast-math-flags, LLVM IR: Fast Math Flags)
732 
733     $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
734 +/
735 template reduce(alias fun)
736 {
737     import mir.functional: naryFun;
738     static if (__traits(isSame, naryFun!fun, fun)
739         && !Mir_disable_inlining_in_reduce)
740     /++
741     Params:
742         seed = An initial accumulation value.
743         slices = One or more slices, range, and arrays.
744     Returns:
745         the accumulated `result`
746     +/
747     @optmath auto reduce(S, Slices...)(S seed, scope Slices slices)
748         if (Slices.length)
749     {
750         static if (Slices.length > 1)
751             slices.checkShapesMatch;
752         static if (areAllContiguousSlices!Slices)
753         {
754             import mir.ndslice.topology: flattened;
755             return .reduce!fun(seed, allFlattened!(allLightScope!slices));
756         }
757         else
758         {
759             if (slices[0].anyEmpty)
760                 return cast(Unqual!S) seed;
761             static if (is(S : Unqual!S))
762                 alias UT = Unqual!S;
763             else
764                 alias UT = S;
765             return reduceImpl!(fun, UT, Slices)(seed, allLightScope!slices);
766         }
767     }
768     else version(Mir_disable_inlining_in_reduce)
769     //As above, but with inlining disabled.
770     @optmath auto reduce(S, Slices...)(S seed, scope Slices slices)
771         if (Slices.length)
772     {
773         static if (Slices.length > 1)
774             slices.checkShapesMatch;
775         static if (areAllContiguousSlices!Slices)
776         {
777             import mir.ndslice.topology: flattened;
778             return .reduce!fun(seed, allFlattened!(allLightScope!slices));
779         }
780         else
781         {
782             if (slices[0].anyEmpty)
783                 return cast(Unqual!S) seed;
784             static if (is(S : Unqual!S))
785                 alias UT = Unqual!S;
786             else
787                 alias UT = S;
788             return reduceImpl!(nonInlinedNaryFun!fun, UT, Slices)(seed, allLightScope!slices);
789         }
790     }
791     else
792         alias reduce = .reduce!(naryFun!fun);
793 }
794 
795 /// Ranges and arrays
796 version(mir_test)
797 unittest
798 {
799     auto ar = [1, 2, 3];
800     auto s = 0.reduce!"a + b"(ar);
801     assert (s == 6);
802 }
803 
804 /// Single slice
805 version(mir_test)
806 unittest
807 {
808     import mir.ndslice.topology : iota;
809 
810     //| 0 1 2 | => 3  |
811     //| 3 4 5 | => 12 | => 15
812     auto sl = iota(2, 3);
813 
814     // sum of all element in the slice
815     auto res = size_t(0).reduce!"a + b"(sl);
816 
817     assert(res == 15);
818 }
819 
820 /// Multiple slices, dot product
821 version(mir_test)
822 unittest
823 {
824     import mir.ndslice.allocation : slice;
825     import mir.ndslice.topology : as, iota;
826 
827     //| 0 1 2 |
828     //| 3 4 5 |
829     auto a = iota([2, 3], 0).as!double.slice;
830     //| 1 2 3 |
831     //| 4 5 6 |
832     auto b = iota([2, 3], 1).as!double.slice;
833 
834     alias dot = reduce!"a + b * c";
835     auto res = dot(0.0, a, b);
836 
837     // check the result:
838     import mir.ndslice.topology : flattened;
839     import std.numeric : dotProduct;
840     assert(res == dotProduct(a.flattened, b.flattened));
841 }
842 
843 /// Zipped slices, dot product
844 pure
845 version(mir_test) unittest
846 {
847     import std.typecons : Yes;
848     import std.numeric : dotProduct;
849     import mir.ndslice.allocation : slice;
850     import mir.ndslice.topology : as, iota, zip, universal;
851     import mir.math.common : optmath;
852 
853     static @optmath T fmuladd(T, Z)(const T a, Z z)
854     {
855         return a + z.a * z.b;
856     }
857 
858     // 0 1 2
859     // 3 4 5
860     auto sl1 = iota(2, 3).as!double.slice.universal;
861     // 1 2 3
862     // 4 5 6
863     auto sl2 = iota([2, 3], 1).as!double.slice;
864 
865     // slices must have the same strides for `zip!true`.
866     assert(sl1.strides == sl2.strides);
867 
868     auto z = zip!true(sl1, sl2);
869 
870     auto dot = reduce!fmuladd(0.0, z);
871 
872     assert(dot == dotProduct(iota(6), iota([6], 1)));
873 }
874 
875 /// Tensor mutation on-the-fly
876 version(mir_test)
877 unittest
878 {
879     import mir.ndslice.allocation : slice;
880     import mir.ndslice.topology : as, iota;
881     import mir.math.common : optmath;
882 
883     static @optmath T fun(T)(const T a, ref T b)
884     {
885         return a + b++;
886     }
887 
888     //| 0 1 2 |
889     //| 3 4 5 |
890     auto sl = iota(2, 3).as!double.slice;
891 
892     auto res = reduce!fun(double(0), sl);
893 
894     assert(res == 15);
895 
896     //| 1 2 3 |
897     //| 4 5 6 |
898     assert(sl == iota([2, 3], 1));
899 }
900 
901 /++
902 Packed slices.
903 
904 Computes minimum value of maximum values for each row.
905 +/
906 version(mir_test)
907 unittest
908 {
909     import mir.math.common;
910     import mir.ndslice.allocation : slice;
911     import mir.ndslice.dynamic : transposed;
912     import mir.ndslice.topology : as, iota, pack, map, universal;
913 
914     alias maxVal = (a) => reduce!fmax(-double.infinity, a);
915     alias minVal = (a) => reduce!fmin(double.infinity, a);
916     alias minimaxVal = (a) => minVal(a.pack!1.map!maxVal);
917 
918     auto sl = iota(2, 3).as!double.slice;
919 
920     // Vectorized computation: row stride equals 1.
921     //| 0 1 2 | => | 2 |
922     //| 3 4 5 | => | 5 | => 2
923     auto res = minimaxVal(sl);
924     assert(res == 2);
925 
926     // Common computation: row stride does not equal 1.
927     //| 0 1 2 |    | 0 3 | => | 3 |
928     //| 3 4 5 | => | 1 4 | => | 4 |
929     //             | 2 5 | => | 5 | => 3
930     auto resT = minimaxVal(sl.universal.transposed);
931     assert(resT == 3);
932 }
933 
934 /// Dlang Range API support.
935 version(mir_test)
936 unittest
937 {
938     import mir.algorithm.iteration: each;
939     import std.range: phobos_iota = iota;
940 
941     int s;
942     // 0 1 2 3
943     4.phobos_iota.each!(i => s += i);
944     assert(s == 6);
945 }
946 
947 @safe pure nothrow @nogc
948 version(mir_test) unittest
949 {
950     import mir.ndslice.topology : iota;
951     auto a = reduce!"a + b"(size_t(7), iota([0, 1], 1));
952     assert(a == 7);
953 }
954 
955 void eachImpl(alias fun, Slices...)(scope Slices slices)
956 {
957     foreach(ref slice; slices)
958         assert(!slice.empty);
959     do
960     {
961         static if (DimensionCount!(Slices[0]) == 1)
962             fun(frontOf!slices);
963         else
964             .eachImpl!fun(frontOf!slices);
965         foreach_reverse(i; Iota!(Slices.length))
966             slices[i].popFront;
967     }
968     while(!slices[0].empty);
969 }
970 
971 void chequerEachImpl(alias fun, Slices...)(Chequer color, scope Slices slices)
972 {
973     foreach(ref slice; slices)
974         assert(!slice.empty);
975     static if (DimensionCount!(Slices[0]) == 1)
976     {
977         if (color)
978         {
979             foreach_reverse(i; Iota!(Slices.length))
980                 slices[i].popFront;
981             if (slices[0].empty)
982                 return;
983         }
984         eachImpl!fun(strideOf!slices);
985     }
986     else
987     {
988         do
989         {
990             .chequerEachImpl!fun(color, frontOf!slices);
991             color = cast(Chequer)!color;
992             foreach_reverse(i; Iota!(Slices.length))
993                 slices[i].popFront;
994         }
995         while(!slices[0].empty);
996     }
997 }
998 
999 /++
1000 The call `each!(fun)(slice1, ..., sliceN)`
1001 evaluates `fun` for each set of elements `x1, ..., xN` in
1002 the borders of `slice1, ..., sliceN` respectively.
1003 
1004 `each` allows to iterate multiple slices in the lockstep.
1005 
1006 Params:
1007     fun = A function.
1008 Note:
1009     $(NDSLICEREF dynamic, transposed) and
1010     $(NDSLICEREF topology, pack) can be used to specify dimensions.
1011 +/
1012 template eachOnBorder(alias fun)
1013 {
1014     import mir.functional: naryFun;
1015     static if (__traits(isSame, naryFun!fun, fun))
1016     /++
1017     Params:
1018         slices = One or more slices.
1019     +/
1020     @optmath void eachOnBorder(Slices...)(Slices slices)
1021         if (allSatisfy!(isSlice, Slices))
1022     {
1023         import mir.ndslice.traits: isContiguousSlice;
1024         static if (Slices.length > 1)
1025             slices.checkShapesMatch;
1026         if (!slices[0].anyEmpty)
1027         {
1028             alias N = DimensionCount!(Slices[0]);
1029             static if (N == 1)
1030             {
1031                 fun(frontOf!slices);
1032                 if (slices[0].length > 1)
1033                     fun(backOf!slices);
1034             }
1035             else
1036             static if (anySatisfy!(isContiguousSlice, Slices))
1037             {
1038                 import mir.ndslice.topology: canonical;
1039                 template f(size_t i)
1040                 {
1041                     static if (isContiguousSlice!(Slices[i]))
1042                         auto f () { return canonical(slices[i]); }
1043                     else
1044                         alias f = slices[i];
1045                 }
1046                 eachOnBorder(staticMap!(f, Iota!(Slices.length)));
1047             }
1048             else
1049             {
1050                 foreach (dimension; Iota!N)
1051                 {
1052                     eachImpl!fun(frontOfD!(dimension, slices));
1053                     foreach_reverse(ref slice; slices)
1054                         slice.popFront!dimension;
1055                     if (slices[0].empty!dimension)
1056                         return;
1057                     eachImpl!fun(backOfD!(dimension, slices));
1058                     foreach_reverse(ref slice; slices)
1059                         slice.popBack!dimension;
1060                     if (slices[0].empty!dimension)
1061                         return;
1062                 }
1063             }
1064         }
1065     }
1066     else
1067         alias eachOnBorder = .eachOnBorder!(naryFun!fun);
1068 }
1069 
1070 ///
1071 @safe pure nothrow 
1072 version(mir_test) unittest
1073 {
1074     import mir.ndslice.allocation : slice;
1075     import mir.ndslice.topology : repeat, iota;
1076 
1077     auto sl = [3, 4].iota.slice;
1078     auto zeros = repeat(0, [3, 4]);
1079 
1080     sl.eachOnBorder!"a = b"(zeros);
1081 
1082     assert(sl == 
1083         [[0, 0, 0 ,0],
1084          [0, 5, 6, 0],
1085          [0, 0, 0 ,0]]);
1086     
1087     sl.eachOnBorder!"a = 1";
1088     sl[0].eachOnBorder!"a = 2";
1089 
1090     assert(sl == 
1091         [[2, 1, 1, 2],
1092          [1, 5, 6, 1],
1093          [1, 1, 1 ,1]]);
1094 }
1095 
1096 /++
1097 The call `each!(fun)(slice1, ..., sliceN)`
1098 evaluates `fun` for each set of elements `x1, ..., xN` in
1099 `slice1, ..., sliceN` respectively.
1100 
1101 `each` allows to iterate multiple slices in the lockstep.
1102 Params:
1103     fun = A function.
1104 Note:
1105     $(NDSLICEREF dynamic, transposed) and
1106     $(NDSLICEREF topology, pack) can be used to specify dimensions.
1107 See_Also:
1108     This is functionally similar to $(LREF reduce) but has not seed.
1109 +/
1110 template each(alias fun)
1111 {
1112     import mir.functional: naryFun;
1113     static if (__traits(isSame, naryFun!fun, fun))
1114     {
1115         /++
1116         Params:
1117             slices = One or more slices, ranges, and arrays.
1118         +/
1119         @optmath auto each(Slices...)(scope Slices slices)
1120             if (Slices.length && !is(Slices[0] : Chequer))
1121         {
1122             static if (Slices.length > 1)
1123                 slices.checkShapesMatch;
1124             static if (areAllContiguousSlices!Slices)
1125             {
1126                 import mir.ndslice.topology: flattened;
1127                 .each!fun(allFlattened!(allLightScope!slices));
1128             }
1129             else
1130             {
1131                 if (slices[0].anyEmpty)
1132                     return;
1133                 eachImpl!fun(allLightScope!slices);
1134             }
1135         }
1136 
1137         /++
1138         Iterates elements of selected $(LREF Chequer) color.
1139         Params:
1140             color = $(LREF Chequer).
1141             slices = One or more slices.
1142         +/
1143         @optmath auto each(Slices...)(Chequer color, scope Slices slices)
1144             if (Slices.length && allSatisfy!(isSlice, Slices))
1145         {
1146             static if (Slices.length > 1)
1147                 slices.checkShapesMatch;
1148             if (slices[0].anyEmpty)
1149                 return;
1150             chequerEachImpl!fun(color, allLightScope!slices);
1151         }
1152     }
1153     else
1154         alias each = .each!(naryFun!fun);
1155 }
1156 
1157 /// Ranges and arrays
1158 version(mir_test)
1159 unittest
1160 {
1161     auto ar = [1, 2, 3];
1162     ar.each!"a *= 2";
1163     assert (ar == [2, 4, 6]);
1164 }
1165 
1166 /// Single slice, multiply-add
1167 version(mir_test)
1168 unittest
1169 {
1170     import mir.ndslice.allocation : slice;
1171     import mir.ndslice.topology : as, iota;
1172 
1173     //| 0 1 2 |
1174     //| 3 4 5 |
1175     auto sl = iota(2, 3).as!double.slice;
1176 
1177     sl.each!((ref a) { a = a * 10 + 5; });
1178 
1179     assert(sl ==
1180         [[ 5, 15, 25],
1181          [35, 45, 55]]);
1182 }
1183 
1184 /// Swap two slices
1185 version(mir_test)
1186 unittest
1187 {
1188     import mir.utility : swap;
1189     import mir.ndslice.allocation : slice;
1190     import mir.ndslice.topology : as, iota;
1191 
1192     //| 0 1 2 |
1193     //| 3 4 5 |
1194     auto a = iota([2, 3], 0).as!double.slice;
1195     //| 10 11 12 |
1196     //| 13 14 15 |
1197     auto b = iota([2, 3], 10).as!double.slice;
1198 
1199     each!swap(a, b);
1200 
1201     assert(a == iota([2, 3], 10));
1202     assert(b == iota([2, 3], 0));
1203 }
1204 
1205 /// Swap two zipped slices
1206 version(mir_test)
1207 unittest
1208 {
1209     import mir.utility : swap;
1210     import mir.ndslice.allocation : slice;
1211     import mir.ndslice.topology : as, zip, iota;
1212 
1213     //| 0 1 2 |
1214     //| 3 4 5 |
1215     auto a = iota([2, 3], 0).as!double.slice;
1216     //| 10 11 12 |
1217     //| 13 14 15 |
1218     auto b = iota([2, 3], 10).as!double.slice;
1219 
1220     auto z = zip(a, b);
1221 
1222     z.each!(z => swap(z.a, z.b));
1223 
1224     assert(a == iota([2, 3], 10));
1225     assert(b == iota([2, 3], 0));
1226 }
1227 
1228 @safe pure nothrow
1229 version(mir_test) unittest
1230 {
1231     import mir.ndslice.topology : iota;
1232     size_t i;
1233     iota(0, 2).each!((a){i++;});
1234     assert(i == 0);
1235 }
1236 
1237 /++
1238 The call `eachUploPair!(fun)(matrix)`
1239 evaluates `fun` for each pair (`matrix[j, i]`, `matrix[i, j]`),
1240 for i <= j (default) or i < j (if includeDiagonal is false).
1241 
1242 Params:
1243     fun = A function.
1244     includeDiagonal = true if applying function to diagonal,
1245                       false (default) otherwise.
1246 +/
1247 template eachUploPair(alias fun, bool includeDiagonal = false)
1248 {
1249     import mir.functional: naryFun;
1250     static if (__traits(isSame, naryFun!fun, fun))
1251     {
1252         /++
1253         Params:
1254             matrix = Square matrix.
1255         +/
1256         auto eachUploPair(Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) matrix)
1257         in
1258         {
1259             assert(matrix.length!0 == matrix.length!1, "matrix must be square.");
1260         }
1261         do
1262         {
1263             static if (kind == Contiguous)
1264             {
1265                 import mir.ndslice.topology: canonical;
1266                 .eachUploPair!(fun, includeDiagonal)(matrix.canonical);
1267             }
1268             else
1269             {
1270                 static if (includeDiagonal == true)
1271                 {
1272                     if (matrix.length) do
1273                     {
1274                         eachImpl!fun(matrix.lightScope.front!0, matrix.lightScope.front!1);
1275                         matrix.popFront!1;
1276                         matrix.popFront!0;
1277                         // hint for optimizer
1278                         matrix._lengths[1] = matrix._lengths[0];
1279                     }
1280                     while (matrix.length);
1281                 }
1282                 else
1283                 {
1284                     if (matrix.length) for(;;)
1285                     {
1286                         assert(!matrix.empty!0);
1287                         assert(!matrix.empty!1);
1288                         auto l = matrix.lightScope.front!1;
1289                         auto u = matrix.lightScope.front!0;
1290                         matrix.popFront!1;
1291                         matrix.popFront!0;
1292                         l.popFront;
1293                         u.popFront;
1294                         // hint for optimizer
1295                         matrix._lengths[1] = matrix._lengths[0] = l._lengths[0] = u._lengths[0];
1296                         if (u.length == 0)
1297                             break;
1298                         eachImpl!fun(u, l);
1299                     }
1300                 }
1301             }
1302         }
1303     }
1304     else
1305     {
1306         alias eachUploPair = .eachUploPair!(naryFun!fun, includeDiagonal);
1307     }
1308 }
1309 
1310 /// Transpose matrix in place.
1311 version(mir_test)
1312 unittest
1313 {
1314     import mir.ndslice.allocation: slice;
1315     import mir.ndslice.topology: iota, universal;
1316     import mir.ndslice.dynamic: transposed;
1317     import mir.utility: swap;
1318 
1319     auto m = iota(4, 4).slice;
1320 
1321     m.eachUploPair!swap;
1322 
1323     assert(m == iota(4, 4).universal.transposed);
1324 }
1325 
1326 /// Reflect Upper matrix part to lower part.
1327 version(mir_test)
1328 unittest
1329 {
1330     import mir.ndslice.allocation: slice;
1331     import mir.ndslice.topology: iota, universal;
1332     import mir.ndslice.dynamic: transposed;
1333     import mir.utility: swap;
1334 
1335     // 0 1 2
1336     // 3 4 5
1337     // 6 7 8
1338     auto m = iota(3, 3).slice;
1339 
1340     m.eachUploPair!((u, ref l) { l = u; });
1341 
1342     assert(m == [
1343         [0, 1, 2],
1344         [1, 4, 5],
1345         [2, 5, 8]]);
1346 }
1347 
1348 /// Fill lower triangle and diagonal with zeroes.
1349 version(mir_test)
1350 unittest
1351 {
1352     import mir.ndslice.allocation: slice;
1353     import mir.ndslice.topology: iota;
1354 
1355     // 1 2 3
1356     // 4 5 6
1357     // 7 8 9
1358     auto m = iota([3, 3], 1).slice;
1359 
1360     m.eachUploPair!((u, ref l) { l = 0; }, true);
1361 
1362     assert(m == [
1363         [0, 2, 3],
1364         [0, 0, 6],
1365         [0, 0, 0]]);
1366 }
1367 
1368 version(mir_test)
1369 unittest
1370 {
1371     import mir.ndslice.allocation: slice;
1372     import mir.ndslice.topology: iota;
1373 
1374     // 0 1 2
1375     // 3 4 5
1376     // 6 7 8
1377     auto m = iota(3, 3).slice;
1378     m.eachUploPair!((u, ref l) { l = l + 1; }, true);
1379     assert(m == [
1380         [1, 1, 2],
1381         [4, 5, 5],
1382         [7, 8, 9]]);
1383 }
1384 
1385 version(mir_test)
1386 unittest
1387 {
1388     import mir.ndslice.allocation: slice;
1389     import mir.ndslice.topology: iota;
1390 
1391     // 0 1 2
1392     // 3 4 5
1393     // 6 7 8
1394     auto m = iota(3, 3).slice;
1395     m.eachUploPair!((u, ref l) { l = l + 1; }, false);
1396 
1397     assert(m == [
1398         [0, 1, 2],
1399         [4, 4, 5],
1400         [7, 8, 8]]);
1401 }
1402 
1403 /++
1404 Checks if the matrix is symmetric.
1405 +/
1406 template isSymmetric(alias fun = "a == b")
1407 {
1408     import mir.functional: naryFun;
1409     static if (__traits(isSame, naryFun!fun, fun))
1410     /++
1411     Params:
1412         matrix = 2D ndslice.
1413     +/
1414     bool isSymmetric(Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) matrix)
1415     {
1416         static if (kind == Contiguous)
1417         {
1418             import mir.ndslice.topology: canonical;
1419             return .isSymmetric!fun(matrix.canonical);
1420         }
1421         else
1422         {
1423             if (matrix.length!0 != matrix.length!1)
1424                 return false;
1425             if (matrix.length) do
1426             {
1427                 if (!allImpl!fun(matrix.lightScope.front!0, matrix.lightScope.front!1))
1428                 {
1429                     return false;
1430                 }
1431                 matrix.popFront!1;
1432                 matrix.popFront!0;
1433                 matrix._lengths[1] = matrix._lengths[0];
1434             }
1435             while (matrix.length);
1436             return true;
1437         }
1438     }
1439     else
1440         alias isSymmetric = .isSymmetric!(naryFun!fun);
1441 }
1442 
1443 ///
1444 version(mir_test)
1445 unittest
1446 {
1447     import mir.ndslice.slice: sliced;
1448     import mir.ndslice.topology: iota;
1449     assert(iota(2, 2).isSymmetric == false);
1450 
1451     assert(
1452         [1, 2,
1453          2, 3].sliced(2, 2).isSymmetric == true);
1454 }
1455 
1456 bool minPosImpl(alias fun, Iterator, size_t N, SliceKind kind)(scope ref size_t[N] backwardIndex, scope ref Iterator iterator, Slice!(Iterator, N, kind) slice)
1457 {
1458     bool found;
1459     do
1460     {
1461         static if (slice.shape.length == 1)
1462         {
1463             if (fun(*slice._iterator, *iterator))
1464             {
1465                 backwardIndex[0] = slice.length;
1466                 iterator = slice._iterator;
1467                 found = true;
1468             }
1469         }
1470         else
1471         {
1472             if (minPosImpl!(fun, LightScopeOf!Iterator, N - 1, kind)(backwardIndex[1 .. $], iterator, lightScope(slice).front))
1473             {
1474                 backwardIndex[0] = slice.length;
1475                 found = true;
1476             }
1477         }
1478         slice.popFront;
1479     }
1480     while(!slice.empty);
1481     return found;
1482 }
1483 
1484 bool[2] minmaxPosImpl(alias fun, Iterator, size_t N, SliceKind kind)(scope ref size_t[2][N] backwardIndex, scope ref Iterator[2] iterator, Slice!(Iterator, N, kind) slice)
1485 {
1486     bool[2] found;
1487     do
1488     {
1489         static if (slice.shape.length == 1)
1490         {
1491             if (fun(*slice._iterator, *iterator[0]))
1492             {
1493                 backwardIndex[0][0] = slice.length;
1494                 iterator[0] = slice._iterator;
1495                 found[0] = true;
1496             }
1497             else
1498             if (fun(*iterator[1], *slice._iterator))
1499             {
1500                 backwardIndex[0][1] = slice.length;
1501                 iterator[1] = slice._iterator;
1502                 found[1] = true;
1503             }
1504         }
1505         else
1506         {
1507             auto r = minmaxPosImpl!(fun, LightScopeOf!Iterator, N - 1, kind)(backwardIndex[1 .. $], iterator, lightScope(slice).front);
1508             if (r[0])
1509             {
1510                 backwardIndex[0][0] = slice.length;
1511             }
1512             if (r[1])
1513             {
1514                 backwardIndex[0][1] = slice.length;
1515             }
1516         }
1517         slice.popFront;
1518     }
1519     while(!slice.empty);
1520     return found;
1521 }
1522 
1523 /++
1524 Finds a positions (ndslices) such that
1525 `position[0].first` is minimal and `position[1].first` is maximal elements in the slice.
1526 
1527 Position is sub-ndslice of the same dimension in the right-$(RPAREN)down-$(RPAREN)etc$(LPAREN)$(LPAREN) corner.
1528 
1529 Params:
1530     pred = A predicate.
1531 
1532 See_also:
1533     $(LREF minmaxIndex),
1534     $(LREF minPos),
1535     $(LREF maxPos),
1536     $(NDSLICEREF slice, Slice.backward).
1537 +/
1538 template minmaxPos(alias pred = "a < b")
1539 {
1540     import mir.functional: naryFun;
1541     static if (__traits(isSame, naryFun!pred, pred))
1542     /++
1543     Params:
1544         slice = ndslice.
1545     Returns:
1546         2 subslices with minimal and maximal `first` elements.
1547     +/
1548     @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)[2]
1549         minmaxPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
1550     {
1551         typeof(return) pret;
1552         if (!slice.anyEmpty)
1553         {
1554             size_t[2][N] ret;
1555             auto scopeSlice = lightScope(slice);
1556             auto it = scopeSlice._iterator;
1557             LightScopeOf!Iterator[2] iterator = [it, it];
1558             minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
1559             foreach (i; Iota!N)
1560             {
1561                 pret[0]._lengths[i] = ret[i][0];
1562                 pret[1]._lengths[i] = ret[i][1];
1563             }
1564             pret[0]._iterator = slice._iterator + (iterator[0] - scopeSlice._iterator);
1565             pret[1]._iterator = slice._iterator + (iterator[1] - scopeSlice._iterator);
1566         }
1567         auto strides = slice.strides;
1568         foreach(i; Iota!(0, pret[0].S))
1569         {
1570             pret[0]._strides[i] = strides[i];
1571             pret[1]._strides[i] = strides[i];
1572         }
1573         return pret;
1574     }
1575     else
1576         alias minmaxPos = .minmaxPos!(naryFun!pred);
1577 }
1578 
1579 ///
1580 version(mir_test)
1581 unittest
1582 {
1583     import mir.ndslice.slice: sliced;
1584     auto s = [
1585         2, 6, 4, -3,
1586         0, -4, -3, 3,
1587         -3, -2, 7, 2,
1588         ].sliced(3, 4);
1589 
1590     auto pos = s.minmaxPos;
1591 
1592     assert(pos[0] == s[$ - 2 .. $, $ - 3 .. $]);
1593     assert(pos[1] == s[$ - 1 .. $, $ - 2 .. $]);
1594 
1595     assert(pos[0].first == -4);
1596     assert(s.backward(pos[0].shape) == -4);
1597     assert(pos[1].first ==  7);
1598     assert(s.backward(pos[1].shape) ==  7);
1599 }
1600 
1601 /++
1602 Finds a backward indices such that
1603 `slice[indices[0]]` is minimal and `slice[indices[1]]` is maximal elements in the slice.
1604 
1605 Params:
1606     pred = A predicate.
1607 
1608 See_also:
1609     $(LREF minmaxIndex),
1610     $(LREF minPos),
1611     $(LREF maxPos),
1612     $(REF Slice.backward, mir,ndslice,slice).
1613 +/
1614 template minmaxIndex(alias pred = "a < b")
1615 {
1616     import mir.functional: naryFun;
1617     static if (__traits(isSame, naryFun!pred, pred))
1618     /++
1619     Params:
1620         slice = ndslice.
1621     Returns:
1622         Subslice with minimal (maximal) `first` element.
1623     +/
1624     @optmath size_t[N][2] minmaxIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
1625     {
1626         typeof(return) pret = size_t.max;
1627         if (!slice.anyEmpty)
1628         {
1629             auto shape = slice.shape;
1630             size_t[2][N] ret;
1631             foreach (i; Iota!N)
1632             {
1633                 ret[i][1] = ret[i][0] = shape[i];
1634             }
1635             auto scopeSlice = lightScope(slice);
1636             auto it = scopeSlice._iterator;
1637             LightScopeOf!Iterator[2] iterator = [it, it];
1638             minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
1639             foreach (i; Iota!N)
1640             {
1641                 pret[0][i] = slice._lengths[i] - ret[i][0];
1642                 pret[1][i] = slice._lengths[i] - ret[i][1];
1643             }
1644         }
1645         return pret;
1646     }
1647     else
1648         alias minmaxIndex = .minmaxIndex!(naryFun!pred);
1649 }
1650 
1651 ///
1652 version(mir_test)
1653 unittest
1654 {
1655     import mir.ndslice.slice: sliced;
1656     auto s = [
1657         2, 6, 4, -3,
1658         0, -4, -3, 3,
1659         -3, -2, 7, 8,
1660         ].sliced(3, 4);
1661 
1662     auto indices = s.minmaxIndex;
1663 
1664     assert(indices == [[1, 1], [2, 3]]);
1665     assert(s[indices[0]] == -4);
1666     assert(s[indices[1]] ==  8);
1667 }
1668 
1669 /++
1670 Finds a backward index such that
1671 `slice.backward(index)` is minimal(maximal).
1672 
1673 Params:
1674     pred = A predicate.
1675 
1676 See_also:
1677     $(LREF minIndex),
1678     $(LREF maxPos),
1679     $(LREF maxIndex),
1680     $(REF Slice.backward, mir,ndslice,slice).
1681 +/
1682 template minPos(alias pred = "a < b")
1683 {
1684     import mir.functional: naryFun;
1685     static if (__traits(isSame, naryFun!pred, pred))
1686     /++
1687     Params:
1688         slice = ndslice.
1689     Returns:
1690         Multidimensional backward index such that element is minimal(maximal).
1691         Backward index equals zeros, if slice is empty.
1692     +/
1693     @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)
1694         minPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
1695     {
1696         typeof(return) ret;
1697         auto iterator = slice.lightScope._iterator;
1698         if (!slice.anyEmpty)
1699         {
1700             minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret._lengths, iterator, lightScope(slice));
1701             ret._iterator = slice._iterator + (iterator - slice.lightScope._iterator);
1702         }
1703         auto strides = slice.strides;
1704         foreach(i; Iota!(0, ret.S))
1705         {
1706             ret._strides[i] = strides[i];
1707         }
1708         return ret;
1709     }
1710     else
1711         alias minPos = .minPos!(naryFun!pred);
1712 }
1713 
1714 /// ditto
1715 template maxPos(alias pred = "a < b")
1716 {
1717     import mir.functional: naryFun, reverseArgs;
1718     alias maxPos = minPos!(reverseArgs!(naryFun!pred));
1719 }
1720 
1721 ///
1722 version(mir_test)
1723 unittest
1724 {
1725     import mir.ndslice.slice: sliced;
1726     auto s = [
1727         2, 6, 4, -3,
1728         0, -4, -3, 3,
1729         -3, -2, 7, 2,
1730         ].sliced(3, 4);
1731 
1732     auto pos = s.minPos;
1733 
1734     assert(pos == s[$ - 2 .. $, $ - 3 .. $]);
1735     assert(pos.first == -4);
1736     assert(s.backward(pos.shape) == -4);
1737 
1738     pos = s.maxPos;
1739 
1740     assert(pos == s[$ - 1 .. $, $ - 2 .. $]);
1741     assert(pos.first == 7);
1742     assert(s.backward(pos.shape) == 7);
1743 }
1744 
1745 /++
1746 Finds an index such that
1747 `slice[index]` is minimal(maximal).
1748 
1749 Params:
1750     pred = A predicate.
1751 
1752 See_also:
1753     $(LREF minIndex),
1754     $(LREF maxPos),
1755     $(LREF maxIndex).
1756 +/
1757 template minIndex(alias pred = "a < b")
1758 {
1759     import mir.functional: naryFun;
1760     static if (__traits(isSame, naryFun!pred, pred))
1761     /++
1762     Params:
1763         slice = ndslice.
1764     Returns:
1765         Multidimensional index such that element is minimal(maximal).
1766         Index elements equal to `size_t.max`, if slice is empty.
1767     +/
1768     @optmath size_t[N] minIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
1769     {
1770         size_t[N] ret = size_t.max;
1771         if (!slice.anyEmpty)
1772         {
1773             ret = slice.shape;
1774             auto scopeSlice = lightScope(slice);
1775             auto iterator = scopeSlice._iterator;
1776             minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
1777             foreach (i; Iota!N)
1778                 ret[i] = slice._lengths[i] - ret[i];
1779         }
1780         return ret;
1781     }
1782     else
1783         alias minIndex = .minIndex!(naryFun!pred);
1784 }
1785 
1786 /// ditto
1787 template maxIndex(alias pred = "a < b")
1788 {
1789     import mir.functional: naryFun, reverseArgs;
1790     alias maxIndex = minIndex!(reverseArgs!(naryFun!pred));
1791 }
1792 
1793 ///
1794 version(mir_test)
1795 unittest
1796 {
1797     import mir.ndslice.slice: sliced;
1798     auto s = [
1799         2, 6, 4, -3,
1800         0, -4, -3, 3,
1801         -3, -2, 7, 8,
1802         ].sliced(3, 4);
1803 
1804     auto index = s.minIndex;
1805 
1806     assert(index == [1, 1]);
1807     assert(s[index] == -4);
1808 
1809     index = s.maxIndex;
1810 
1811     assert(index == [2, 3]);
1812     assert(s[index] == 8);
1813 }
1814 
1815 ///
1816 version(mir_test)
1817 unittest
1818 {
1819     import mir.ndslice.slice: sliced;
1820     auto s = [
1821         -8, 6, 4, -3,
1822         0, -4, -3, 3,
1823         -3, -2, 7, 8,
1824         ].sliced(3, 4);
1825 
1826     auto index = s.minIndex;
1827 
1828     assert(index == [0, 0]);
1829     assert(s[index] == -8);
1830 }
1831 
1832 version(mir_test)
1833 unittest
1834 {
1835     import mir.ndslice.slice: sliced;
1836     auto s = [
1837             0, 1, 2, 3,
1838             4, 5, 6, 7,
1839             8, 9, 10, 11
1840             ].sliced(3, 4);
1841 
1842     auto index = s.minIndex;
1843     assert(index == [0, 0]);
1844     assert(s[index] == 0);
1845 
1846     index = s.maxIndex;
1847     assert(index == [2, 3]);
1848     assert(s[index] == 11);
1849 }
1850 
1851 bool findImpl(alias fun, size_t N, Slices...)(scope ref size_t[N] backwardIndex, Slices slices)
1852     if (Slices.length)
1853 {
1854     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
1855     {
1856         auto cnt = BitSliceAccelerator!(Field, I)(slices[0]).cttz;
1857         if (cnt = -1)
1858             return false;
1859         backwardIndex[0] = slices[0].length - cnt;
1860     }
1861     else
1862     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
1863     {
1864         import mir.ndslice.topology: retro;
1865         auto cnt = BitSliceAccelerator!(Field, I)(slices[0].retro).ctlz;
1866         if (cnt = -1)
1867             return false;
1868         backwardIndex[0] = slices[0].length - cnt;
1869     }
1870     else
1871     {
1872         do
1873         {
1874             static if (DimensionCount!(Slices[0]) == 1)
1875             {
1876                 if (fun(frontOf!slices))
1877                 {
1878                     backwardIndex[0] = slices[0].length;
1879                     return true;
1880                 }
1881             }
1882             else
1883             {
1884                 if (findImpl!fun(backwardIndex[1 .. $], frontOf!slices))
1885                 {
1886                     backwardIndex[0] = slices[0].length;
1887                     return true;
1888                 }
1889             }
1890             foreach_reverse(ref slice; slices)
1891                 slice.popFront;
1892         }
1893         while(!slices[0].empty);
1894         return false;
1895     }
1896 }
1897 
1898 /++
1899 Finds an index such that
1900 `pred(slices[0][index], ..., slices[$-1][index])` is `true`.
1901 
1902 Params:
1903     pred = A predicate.
1904 
1905 See_also:
1906     $(LREF find),
1907     $(LREF any).
1908 Optimization:
1909     `findIndex!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
1910 +/
1911 template findIndex(alias pred)
1912 {
1913     import mir.functional: naryFun;
1914     static if (__traits(isSame, naryFun!pred, pred))
1915     /++
1916     Params:
1917         slices = One or more slices.
1918     Returns:
1919         Multidimensional index such that the predicate is true.
1920         Index equals `size_t.max`, if the predicate evaluates `false` for all indices.
1921     Constraints:
1922         All slices must have the same shape.
1923     +/
1924     @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) findIndex(Slices...)(Slices slices)
1925         if (Slices.length)
1926     {
1927         static if (Slices.length > 1)
1928             slices.checkShapesMatch;
1929         size_t[DimensionCount!(Slices[0])] ret = -1;
1930         auto lengths = slices[0].shape;
1931         if (!slices[0].anyEmpty && findImpl!pred(ret, allLightScope!slices))
1932             foreach (i; Iota!(DimensionCount!(Slices[0])))
1933                 ret[i] = lengths[i] - ret[i];
1934         static if (DimensionCount!(Slices[0]) > 1)
1935             return ret;
1936         else
1937             return ret[0];
1938     }
1939     else
1940         alias findIndex = .findIndex!(naryFun!pred);
1941 }
1942 
1943 /// Ranges and arrays
1944 version(mir_test)
1945 unittest
1946 {
1947     import std.range : iota;
1948     // 0 1 2 3 4 5
1949     auto sl = iota(5);
1950     size_t index = sl.findIndex!"a == 3";
1951 
1952     assert(index == 3);
1953     assert(sl[index] == 3);
1954 
1955     assert(sl.findIndex!(a => a == 8) == size_t.max);
1956 }
1957 
1958 ///
1959 @safe pure nothrow @nogc
1960 version(mir_test) unittest
1961 {
1962     import mir.ndslice.topology : iota;
1963     // 0 1 2
1964     // 3 4 5
1965     auto sl = iota(2, 3);
1966     size_t[2] index = sl.findIndex!(a => a == 3);
1967 
1968     assert(sl[index] == 3);
1969 
1970     index = sl.findIndex!"a == 6";
1971     assert(index[0] == size_t.max);
1972     assert(index[1] == size_t.max);
1973 }
1974 
1975 /++
1976 Finds a backward index such that
1977 `pred(slices[0].backward(index), ..., slices[$-1].backward(index))` is `true`.
1978 
1979 Params:
1980     pred = A predicate.
1981 
1982 Optimization:
1983     To check if any element was found
1984     use the last dimension (row index).
1985     This will slightly optimize the code.
1986 --------
1987 if (backwardIndex)
1988 {
1989     auto elem1 = slice1.backward(backwardIndex);
1990     //...
1991     auto elemK = sliceK.backward(backwardIndex);
1992 }
1993 else
1994 {
1995     // not found
1996 }
1997 --------
1998 
1999 See_also:
2000     $(LREF findIndex),
2001     $(LREF any),
2002     $(REF Slice.backward, mir,ndslice,slice).
2003 
2004 Optimization:
2005     `find!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
2006 +/
2007 template find(alias pred)
2008 {
2009     import mir.functional: naryFun;
2010     static if (__traits(isSame, naryFun!pred, pred))
2011     /++
2012     Params:
2013         slices = One or more slices.
2014     Returns:
2015         Multidimensional backward index such that the predicate is true.
2016         Backward index equals zeros, if the predicate evaluates `false` for all indices.
2017     Constraints:
2018         All slices must have the same shape.
2019     +/
2020     @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(auto ref Slices slices)
2021         if (Slices.length && allSatisfy!(hasShape, Slices))
2022     {
2023         static if (Slices.length > 1)
2024             slices.checkShapesMatch;
2025         size_t[DimensionCount!(Slices[0])] ret;
2026         if (!slices[0].anyEmpty)
2027             findImpl!pred(ret, allLightScope!slices);
2028         static if (DimensionCount!(Slices[0]) > 1)
2029             return ret;
2030         else
2031             return ret[0];
2032     }
2033     else
2034         alias find = .find!(naryFun!pred);
2035 }
2036 
2037 /// Ranges and arrays
2038 version(mir_test)
2039 unittest
2040 {
2041     import std.range : iota;
2042 
2043     auto sl = iota(10);
2044     size_t index = sl.find!"a == 3";
2045 
2046     assert(sl[$ - index] == 3);
2047 }
2048 
2049 ///
2050 @safe pure nothrow @nogc
2051 version(mir_test) unittest
2052 {
2053     import mir.ndslice.topology : iota;
2054     // 0 1 2
2055     // 3 4 5
2056     auto sl = iota(2, 3);
2057     size_t[2] bi = sl.find!"a == 3";
2058     assert(sl.backward(bi) == 3);
2059     assert(sl[$ - bi[0], $ - bi[1]] == 3);
2060 
2061     bi = sl.find!"a == 6";
2062     assert(bi[0] == 0);
2063     assert(bi[1] == 0);
2064 }
2065 
2066 /// Multiple slices
2067 @safe pure nothrow @nogc
2068 version(mir_test) unittest
2069 {
2070     import mir.ndslice.topology : iota;
2071 
2072     // 0 1 2
2073     // 3 4 5
2074     auto a = iota(2, 3);
2075     // 10 11 12
2076     // 13 14 15
2077     auto b = iota([2, 3], 10);
2078 
2079     size_t[2] bi = find!((a, b) => a * b == 39)(a, b);
2080     assert(a.backward(bi) == 3);
2081     assert(b.backward(bi) == 13);
2082 }
2083 
2084 /// Zipped slices
2085 @safe pure nothrow
2086 version(mir_test) unittest
2087 {
2088     import mir.ndslice.topology : iota, zip;
2089 
2090     // 0 1 2
2091     // 3 4 5
2092     auto a = iota(2, 3);
2093     // 10 11 12
2094     // 13 14 15
2095     auto b = iota([2, 3], 10);
2096 
2097     size_t[2] bi = zip!true(a, b).find!"a.a * a.b == 39";
2098 
2099     assert(a.backward(bi) == 3);
2100     assert(b.backward(bi) == 13);
2101 }
2102 
2103 /// Mutation on-the-fly
2104 pure nothrow
2105 version(mir_test) unittest
2106 {
2107     import mir.ndslice.allocation : slice;
2108     import mir.ndslice.topology : as, iota;
2109 
2110     // 0 1 2
2111     // 3 4 5
2112     auto sl = iota(2, 3).as!double.slice;
2113 
2114     static bool pred(T)(ref T a)
2115     {
2116         if (a == 5)
2117             return true;
2118         a = 8;
2119         return false;
2120     }
2121 
2122     size_t[2] bi = sl.find!pred;
2123 
2124     assert(bi == [1, 1]);
2125     assert(sl.backward(bi) == 5);
2126 
2127     // sl was changed
2128     assert(sl == [[8, 8, 8],
2129                   [8, 8, 5]]);
2130 }
2131 
2132 @safe pure nothrow
2133 version(mir_test) unittest
2134 {
2135     import mir.ndslice.topology : iota;
2136     size_t i;
2137     size_t[2] bi = iota(2, 0).find!((elem){i++; return true;});
2138     assert(i == 0);
2139     assert(bi == [0, 0]);
2140 }
2141 
2142 size_t anyImpl(alias fun, Slices...)(scope Slices slices)
2143     if (Slices.length)
2144 {
2145     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
2146     {
2147         return BitSliceAccelerator!(Field, I)(slices[0]).any;
2148     }
2149     else
2150     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
2151     {
2152         // pragma(msg, S);
2153         import mir.ndslice.topology: retro;
2154         return .anyImpl!fun(lightScope(slices[0]).retro);
2155     }
2156     else
2157     {
2158         do
2159         {
2160             static if (DimensionCount!(Slices[0]) == 1)
2161             {
2162                 if (fun(frontOf!slices))
2163                     return true;
2164             }
2165             else
2166             {
2167                 if (anyImpl!fun(frontOf!slices))
2168                     return true;
2169             }
2170             foreach_reverse(ref slice; slices)
2171                 slice.popFront;
2172         }
2173         while(!slices[0].empty);
2174         return false;
2175     }
2176 }
2177 
2178 /++
2179 Like $(LREF find), but only returns whether or not the search was successful.
2180 
2181 Params:
2182     pred = The predicate.
2183 Optimization:
2184     `any!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
2185 +/
2186 template any(alias pred = "a")
2187 {
2188     import mir.functional: naryFun;
2189     static if (__traits(isSame, naryFun!pred, pred))
2190     /++
2191     Params:
2192         slices = One or more slices, ranges, and arrays.
2193     Returns:
2194         `true` if the search was successful and `false` otherwise.
2195     Constraints:
2196         All slices must have the same shape.
2197     +/
2198     @optmath bool any(Slices...)(scope Slices slices)
2199         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
2200     {
2201         static if (Slices.length > 1)
2202             slices.checkShapesMatch;
2203         static if (areAllContiguousSlices!Slices)
2204         {
2205             import mir.ndslice.topology: flattened;
2206             return .any!pred(allFlattened!(allLightScope!slices));
2207         }
2208         else
2209         {
2210             return !slices[0].anyEmpty && anyImpl!pred(allLightScope!slices);
2211         }
2212     }
2213     else
2214         alias any = .any!(naryFun!pred);
2215 }
2216 
2217 /// Ranges and arrays
2218 @safe pure nothrow @nogc
2219 version(mir_test) unittest
2220 {
2221     import std.range : iota;
2222     // 0 1 2 3 4 5
2223     auto r = iota(6);
2224 
2225     assert(r.any!"a == 3");
2226     assert(!r.any!"a == 6");
2227 }
2228 
2229 ///
2230 @safe pure nothrow @nogc
2231 version(mir_test) unittest
2232 {
2233     import mir.ndslice.topology : iota;
2234     // 0 1 2
2235     // 3 4 5
2236     auto sl = iota(2, 3);
2237 
2238     assert(sl.any!"a == 3");
2239     assert(!sl.any!"a == 6");
2240 }
2241 
2242 /// Multiple slices
2243 @safe pure nothrow @nogc
2244 version(mir_test) unittest
2245 {
2246     import mir.ndslice.topology : iota;
2247 
2248     // 0 1 2
2249     // 3 4 5
2250     auto a = iota(2, 3);
2251     // 10 11 12
2252     // 13 14 15
2253     auto b = iota([2, 3], 10);
2254 
2255     assert(any!((a, b) => a * b == 39)(a, b));
2256 }
2257 
2258 /// Zipped slices
2259 @safe pure nothrow
2260 version(mir_test) unittest
2261 {
2262     import mir.ndslice.topology : iota, zip;
2263 
2264     // 0 1 2
2265     // 3 4 5
2266     auto a = iota(2, 3);
2267     // 10 11 12
2268     // 13 14 15
2269     auto b = iota([2, 3], 10);
2270 
2271     // slices must have the same strides
2272 
2273     assert(zip!true(a, b).any!"a.a * a.b == 39");
2274 }
2275 
2276 /// Mutation on-the-fly
2277 pure nothrow
2278 version(mir_test) unittest
2279 {
2280     import mir.ndslice.allocation : slice;
2281     import mir.ndslice.topology : as, iota;
2282 
2283     // 0 1 2
2284     // 3 4 5
2285     auto sl = iota(2, 3).as!double.slice;
2286 
2287     static bool pred(T)(ref T a)
2288     {
2289         if (a == 5)
2290             return true;
2291         a = 8;
2292         return false;
2293     }
2294 
2295     assert(sl.any!pred);
2296 
2297     // sl was changed
2298     assert(sl == [[8, 8, 8],
2299                   [8, 8, 5]]);
2300 }
2301 
2302 size_t allImpl(alias fun, Slices...)(scope Slices slices)
2303     if (Slices.length)
2304 {
2305     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
2306     {
2307         return BitSliceAccelerator!(LightScopeOf!Field, I)(lightScope(slices[0])).all;
2308     }
2309     else
2310     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
2311     {
2312         // pragma(msg, S);
2313         import mir.ndslice.topology: retro;
2314         return .allImpl!fun(lightScope(slices[0]).retro);
2315     }
2316     else
2317     {
2318         do
2319         {
2320             static if (DimensionCount!(Slices[0]) == 1)
2321             {
2322                 if (!fun(frontOf!slices))
2323                     return false;
2324             }
2325             else
2326             {
2327                 if (!allImpl!fun(frontOf!slices))
2328                     return false;
2329             }
2330             foreach_reverse(ref slice; slices)
2331                 slice.popFront;
2332         }
2333         while(!slices[0].empty);
2334         return true;
2335     }
2336 }
2337 
2338 /++
2339 Checks if all of the elements verify `pred`.
2340 
2341 Params:
2342     pred = The predicate.
2343 Optimization:
2344     `all!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
2345 +/
2346 template all(alias pred = "a")
2347 {
2348     import mir.functional: naryFun;
2349     static if (__traits(isSame, naryFun!pred, pred))
2350     /++
2351     Params:
2352         slices = One or more slices.
2353     Returns:
2354         `true` all of the elements verify `pred` and `false` otherwise.
2355     Constraints:
2356         All slices must have the same shape.
2357     +/
2358     @optmath bool all(Slices...)(scope Slices slices)
2359         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
2360     {
2361         static if (Slices.length > 1)
2362             slices.checkShapesMatch;
2363         static if (areAllContiguousSlices!Slices)
2364         {
2365             import mir.ndslice.topology: flattened;
2366             return .all!pred(allFlattened!(allLightScope!slices));
2367         }
2368         else
2369         {
2370             return slices[0].anyEmpty || allImpl!pred(allLightScope!slices);
2371         }
2372     }
2373     else
2374         alias all = .all!(naryFun!pred);
2375 }
2376 
2377 /// Ranges and arrays
2378 @safe pure nothrow @nogc
2379 version(mir_test) unittest
2380 {
2381     import std.range : iota;
2382     // 0 1 2 3 4 5
2383     auto r = iota(6);
2384 
2385     assert(r.all!"a < 6");
2386     assert(!r.all!"a < 5");
2387 }
2388 
2389 ///
2390 @safe pure nothrow
2391 version(mir_test) unittest
2392 {
2393     import mir.ndslice.topology : iota;
2394 
2395     // 0 1 2
2396     // 3 4 5
2397     auto sl = iota(2, 3);
2398 
2399     assert(sl.all!"a < 6");
2400     assert(!sl.all!"a < 5");
2401 }
2402 
2403 /// Multiple slices
2404 @safe pure nothrow
2405 version(mir_test) unittest
2406 {
2407     import mir.ndslice.topology : iota;
2408 
2409     // 0 1 2
2410     // 3 4 5
2411     auto sl = iota(2, 3);
2412 
2413     assert(all!"a - b == 0"(sl, sl));
2414 }
2415 
2416 /// Zipped slices
2417 @safe pure nothrow
2418 version(mir_test) unittest
2419 {
2420     import mir.ndslice.topology : iota, zip;
2421 
2422     // 0 1 2
2423     // 3 4 5
2424     auto sl = iota(2, 3);
2425 
2426 
2427     assert(zip!true(sl, sl).all!"a.a - a.b == 0");
2428 }
2429 
2430 /// Mutation on-the-fly
2431 pure nothrow
2432 version(mir_test) unittest
2433 {
2434     import mir.ndslice.allocation : slice;
2435     import mir.ndslice.topology : as, iota;
2436 
2437     // 0 1 2
2438     // 3 4 5
2439     auto sl = iota(2, 3).as!double.slice;
2440 
2441     static bool pred(T)(ref T a)
2442     {
2443         if (a < 4)
2444         {
2445             a = 8;
2446             return true;
2447         }
2448         return false;
2449     }
2450 
2451     assert(!sl.all!pred);
2452 
2453     // sl was changed
2454     assert(sl == [[8, 8, 8],
2455                   [8, 4, 5]]);
2456 }
2457 
2458 @safe pure nothrow
2459 version(mir_test) unittest
2460 {
2461     import mir.ndslice.topology : iota;
2462     size_t i;
2463     assert(iota(2, 0).all!((elem){i++; return true;}));
2464     assert(i == 0);
2465 }
2466 
2467 /++
2468 Counts elements in slices according to the `fun`.
2469 Params:
2470     fun = A predicate.
2471 
2472 Optimization:
2473     `count!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
2474 +/
2475 template count(alias fun)
2476 {
2477     import mir.functional: naryFun;
2478     static if (__traits(isSame, naryFun!fun, fun))
2479     /++
2480     Params:
2481         slices = One or more slices, ranges, and arrays.
2482 
2483     Returns: The number elements according to the `fun`.
2484 
2485     Constraints:
2486         All slices must have the same shape.
2487     +/
2488     @optmath size_t count(Slices...)(scope Slices slices)
2489         if (Slices.length)
2490     {
2491         static if (Slices.length > 1)
2492             slices.checkShapesMatch;
2493         static if (__traits(isSame, fun, naryFun!"true"))
2494         {
2495             return slices[0].elementCount;
2496         }
2497         else
2498         static if (areAllContiguousSlices!Slices)
2499         {
2500             import mir.ndslice.topology: flattened;
2501             return .count!fun(allFlattened!(allLightScope!slices));
2502         }
2503         else
2504         {
2505             if (slices[0].anyEmpty)
2506                 return 0;
2507             return countImpl!(fun)(allLightScope!slices);
2508         }
2509     }
2510     else
2511         alias count = .count!(naryFun!fun);
2512 
2513 }
2514 
2515 /// Ranges and arrays
2516 @safe pure nothrow @nogc
2517 version(mir_test) unittest
2518 {
2519     import std.range : iota;
2520     // 0 1 2 3 4 5
2521     auto r = iota(6);
2522 
2523     assert(r.count!"true" == 6);
2524     assert(r.count!"a" == 5);
2525     assert(r.count!"a % 2" == 3);
2526 }
2527 
2528 /// Single slice
2529 version(mir_test)
2530 unittest
2531 {
2532     import mir.ndslice.topology : iota;
2533 
2534     //| 0 1 2 |
2535     //| 3 4 5 |
2536     auto sl = iota(2, 3);
2537 
2538     assert(sl.count!"true" == 6);
2539     assert(sl.count!"a" == 5);
2540     assert(sl.count!"a % 2" == 3);
2541 }
2542 
2543 /// Accelerated set bit count
2544 version(mir_test)
2545 unittest
2546 {
2547     import mir.ndslice.topology: retro, iota, bitwise;
2548     import mir.ndslice.allocation: slice;
2549 
2550     //| 0 1 2 |
2551     //| 3 4 5 |
2552     auto sl = iota!size_t(2, 3).bitwise;
2553 
2554     assert(sl.count!"true" == 6 * size_t.sizeof * 8);
2555 
2556     assert(sl.slice.count!"a" == 7);
2557 
2558     // accelerated
2559     assert(sl.count!"a" == 7);
2560     assert(sl.retro.count!"a" == 7);
2561 
2562     auto sl2 = iota!ubyte([6], 128).bitwise;
2563     // accelerated
2564     assert(sl2.count!"a" == 13);
2565     assert(sl2[4 .. $].count!"a" == 13);
2566     assert(sl2[4 .. $ - 1].count!"a" == 12);
2567     assert(sl2[4 .. $ - 1].count!"a" == 12);
2568     assert(sl2[41 .. $ - 1].count!"a" == 1);
2569 }
2570 
2571 unittest
2572 {
2573     import mir.ndslice.allocation: slice;
2574     import mir.ndslice.topology: bitwise, assumeFieldsHaveZeroShift;
2575     auto sl = slice!uint([6]).bitwise;
2576     auto slb = slice!ubyte([6]).bitwise;
2577     slb[4] = true;
2578     auto d = slb[4];
2579     auto c = assumeFieldsHaveZeroShift(slb & ~slb);
2580     // pragma(msg, typeof(c));
2581     assert(!sl.any);
2582     assert((~sl).all);
2583     // pragma(msg, typeof(~slb));
2584     // pragma(msg, typeof(~slb));
2585     // assert(sl.findIndex);
2586 }
2587 
2588 /++
2589 Compares two or more slices for equality, as defined by predicate `pred`.
2590 
2591 See_also: $(NDSLICEREF slice, Slice.opEquals)
2592 
2593 Params:
2594     pred = The predicate.
2595 +/
2596 template equal(alias pred = "a == b")
2597 {
2598     import mir.functional: naryFun;
2599     static if (__traits(isSame, naryFun!pred, pred))
2600     {
2601         /++
2602         Params:
2603             slices = Two or more ndslices, ranges, and arrays.
2604 
2605         Returns:
2606             `true` any of the elements verify `pred` and `false` otherwise.
2607         +/
2608         bool equal(Slices...)(scope Slices slices)
2609             if (Slices.length >= 2)
2610         {
2611             import mir.internal.utility;
2612             static if (allSatisfy!(hasShape, Slices))
2613             {
2614                 auto shape0 = slices[0].shape;
2615                 enum N = DimensionCount!(Slices[0]);
2616                 foreach (ref slice; slices[1 .. $])
2617                 {
2618                     if (slice.shape != shape0)
2619                         goto False;
2620                 }
2621                 return all!pred(allLightScope!slices);
2622             }
2623             else
2624             {
2625                 for(;;)
2626                 {
2627                     auto empty = slices[0].empty;
2628                     foreach (ref slice; slices[1 .. $])
2629                     {
2630                         if (slice.empty != empty)
2631                             goto False;
2632                     }
2633                     if (empty)
2634                         return true;
2635                     if (!pred(frontOf!slices))
2636                         goto False;
2637                     foreach (ref slice; slices)
2638                         slice.popFront;
2639                 }
2640             }
2641             False: return false;
2642         }
2643     }
2644     else
2645         alias equal = .equal!(naryFun!pred);
2646 }
2647 
2648 /// Ranges and arrays
2649 @safe pure nothrow
2650 version(mir_test) unittest
2651 {
2652     import std.range : iota;
2653     auto r = iota(6);
2654     assert(r.equal([0, 1, 2, 3, 4, 5]));
2655 }
2656 
2657 ///
2658 @safe pure nothrow @nogc
2659 version(mir_test) unittest
2660 {
2661     import mir.ndslice.allocation : slice;
2662     import mir.ndslice.topology : iota;
2663 
2664     // 0 1 2
2665     // 3 4 5
2666     auto sl1 = iota(2, 3);
2667     // 1 2 3
2668     // 4 5 6
2669     auto sl2 = iota([2, 3], 1);
2670 
2671     assert(equal(sl1, sl1));
2672     assert(sl1 == sl1); //can also use opEquals for two Slices
2673     assert(equal!"2 * a == b + c"(sl1, sl1, sl1));
2674 
2675     assert(equal!"a < b"(sl1, sl2));
2676 
2677     assert(!equal(sl1[0 .. $ - 1], sl1));
2678     assert(!equal(sl1[0 .. $, 0 .. $ - 1], sl1));
2679 }
2680 
2681 @safe pure nothrow @nogc
2682 version(mir_test) unittest
2683 {
2684     import mir.math.common: approxEqual;
2685     import mir.ndslice.allocation: rcslice;
2686     import mir.ndslice.topology: as, iota;
2687 
2688     auto x = 5.iota.as!double.rcslice;
2689     auto y = x.rcslice;
2690 
2691     assert(equal(x, y));
2692     assert(equal!approxEqual(x, y));
2693 }
2694 
2695 ptrdiff_t cmpImpl(alias pred, A, B)
2696     (scope A sl1, scope B sl2)
2697     if (DimensionCount!A == DimensionCount!B)
2698 {
2699     for (;;)
2700     {
2701         static if (DimensionCount!A == 1)
2702         {
2703             import mir.functional : naryFun;
2704             if (naryFun!pred(sl1.front, sl2.front))
2705                 return -1;
2706             if (naryFun!pred(sl2.front, sl1.front))
2707                 return 1;
2708         }
2709         else
2710         {
2711             if (auto res = .cmpImpl!pred(sl1.front, sl2.front))
2712                 return res;
2713         }
2714         sl1.popFront;
2715         if (sl1.empty)
2716             return -cast(ptrdiff_t)(sl2.length > 1);
2717         sl2.popFront;
2718         if (sl2.empty)
2719             return 1;
2720     }
2721 }
2722 
2723 /++
2724 Performs three-way recursive lexicographical comparison on two slices according to predicate `pred`.
2725 Iterating `sl1` and `sl2` in lockstep, `cmp` compares each `N-1` dimensional element `e1` of `sl1`
2726 with the corresponding element `e2` in `sl2` recursively.
2727 If one of the slices has been finished,`cmp` returns a negative value if `sl1` has fewer elements than `sl2`,
2728 a positive value if `sl1` has more elements than `sl2`,
2729 and `0` if the ranges have the same number of elements.
2730 
2731 Params:
2732     pred = The predicate.
2733 +/
2734 template cmp(alias pred = "a < b")
2735 {
2736     import mir.functional: naryFun;
2737     static if (__traits(isSame, naryFun!pred, pred))
2738     /++
2739     Params:
2740         sl1 = First slice, range, or array.
2741         sl2 = Second slice, range, or array.
2742 
2743     Returns:
2744         `0` if both ranges compare equal.
2745         Negative value if the first differing element of `sl1` is less than the corresponding
2746         element of `sl2` according to `pred`.
2747         Positive value if the first differing element of `sl2` is less than the corresponding
2748         element of `sl1` according to `pred`.
2749     +/
2750     auto cmp(A, B)
2751         (scope A sl1, scope B sl2)
2752         if (DimensionCount!A == DimensionCount!B)
2753     {
2754         auto b = sl2.anyEmpty;
2755         if (sl1.anyEmpty)
2756         {
2757             if (!b)
2758                 return -1;
2759             auto sh1 = sl1.shape;
2760             auto sh2 = sl2.shape;
2761             foreach (i; Iota!(DimensionCount!A))
2762                 if (sh1[i] != sh2[i])
2763                     return sh1[i] > sh2[i] ? 1 : -1;
2764             return 0;
2765         }
2766         if (b)
2767             return 1;
2768         return cmpImpl!pred(lightScope(sl1), lightScope(sl2));
2769     }
2770     else
2771         alias cmp = .cmp!(naryFun!pred);
2772 }
2773 
2774 /// Ranges and arrays
2775 @safe pure nothrow
2776 version(mir_test) unittest
2777 {
2778     import std.range : iota;
2779 
2780     // 0 1 2 3 4 5
2781     auto r1 = iota(0, 6);
2782     // 1 2 3 4 5 6
2783     auto r2 = iota(1, 7);
2784 
2785     assert(cmp(r1, r1) == 0);
2786     assert(cmp(r1, r2) < 0);
2787     assert(cmp!"a >= b"(r1, r2) > 0);
2788 }
2789 
2790 ///
2791 @safe pure nothrow @nogc
2792 version(mir_test) unittest
2793 {
2794     import mir.ndslice.topology : iota;
2795 
2796     // 0 1 2
2797     // 3 4 5
2798     auto sl1 = iota(2, 3);
2799     // 1 2 3
2800     // 4 5 6
2801     auto sl2 = iota([2, 3], 1);
2802 
2803     assert(cmp(sl1, sl1) == 0);
2804     assert(cmp(sl1, sl2) < 0);
2805     assert(cmp!"a >= b"(sl1, sl2) > 0);
2806 }
2807 
2808 @safe pure nothrow @nogc
2809 version(mir_test) unittest
2810 {
2811     import mir.ndslice.topology : iota;
2812 
2813     auto sl1 = iota(2, 3);
2814     auto sl2 = iota([2, 3], 1);
2815 
2816     assert(cmp(sl1[0 .. $ - 1], sl1) < 0);
2817     assert(cmp(sl1, sl1[0 .. $, 0 .. $ - 1]) > 0);
2818 
2819     assert(cmp(sl1[0 .. $ - 2], sl1) < 0);
2820     assert(cmp(sl1, sl1[0 .. $, 0 .. $ - 3]) > 0);
2821     assert(cmp(sl1[0 .. $, 0 .. $ - 3], sl1[0 .. $, 0 .. $ - 3]) == 0);
2822     assert(cmp(sl1[0 .. $, 0 .. $ - 3], sl1[0 .. $ - 1, 0 .. $ - 3]) > 0);
2823     assert(cmp(sl1[0 .. $ - 1, 0 .. $ - 3], sl1[0 .. $, 0 .. $ - 3]) < 0);
2824 }
2825 
2826 size_t countImpl(alias fun, Slices...)(scope Slices slices)
2827 {
2828     size_t ret;
2829     alias S = Slices[0];
2830     import mir.functional: naryFun;
2831     import mir.ndslice.iterator: FieldIterator, RetroIterator;
2832     import mir.ndslice.field: BitField;
2833     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
2834     {
2835         ret = BitSliceAccelerator!(Field, I)(slices[0]).ctpop;
2836     }
2837     else
2838     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
2839     {
2840         // pragma(msg, S);
2841         import mir.ndslice.topology: retro;
2842         ret = .countImpl!fun(lightScope(slices[0]).retro);
2843     }
2844     else
2845     do
2846     {
2847         static if (DimensionCount!(Slices[0]) == 1)
2848         {
2849             if(fun(frontOf!slices))
2850                 ret++;
2851         }
2852         else
2853             ret += .countImpl!fun(frontOf!slices);
2854         foreach_reverse(ref slice; slices)
2855             slice.popFront;
2856     }
2857     while(!slices[0].empty);
2858     return ret;
2859 }
2860 
2861 /++
2862 Returns: max length across all dimensions.
2863 +/
2864 size_t maxLength(S)(auto ref scope S s)
2865  if (hasShape!S)
2866 {
2867     auto shape = s.shape;
2868     size_t length = 0;
2869     foreach(i; Iota!(shape.length))
2870         if (shape[i] > length)
2871             length = shape[i];
2872     return length;
2873 }
2874 
2875 /++
2876 The call `eachLower!(fun)(slice1, ..., sliceN)` evaluates `fun` on the lower
2877 triangle in `slice1, ..., sliceN` respectively.
2878 
2879 `eachLower` allows iterating multiple slices in the lockstep.
2880 
2881 Params:
2882     fun = A function
2883 See_Also:
2884     This is functionally similar to $(LREF each).
2885 +/
2886 template eachLower(alias fun)
2887 {
2888     import mir.functional : naryFun;
2889 
2890     static if (__traits(isSame, naryFun!fun, fun))
2891     {
2892         /++
2893         Params:
2894             inputs = One or more two-dimensional slices and an optional
2895                      integer, `k`.
2896 
2897             The value `k` determines which diagonals will have the function
2898             applied:
2899             For k = 0, the function is also applied to the main diagonal
2900             For k = 1 (default), only the non-main diagonals below the main
2901             diagonal will have the function applied.
2902             For k > 1, fewer diagonals below the main diagonal will have the
2903             function applied.
2904             For k < 0, more diagonals above the main diagonal will have the
2905             function applied.
2906         +/
2907         void eachLower(Inputs...)(scope Inputs inputs)
2908             if (((Inputs.length > 1) &&
2909                  (isIntegral!(Inputs[$ - 1]))) ||
2910                 (Inputs.length))
2911         {
2912             import mir.ndslice.traits : isMatrix;
2913 
2914             size_t val;
2915 
2916             static if ((Inputs.length > 1) && (isIntegral!(Inputs[$ - 1])))
2917             {
2918                 immutable(sizediff_t) k = inputs[$ - 1];
2919                 alias Slices = Inputs[0..($ - 1)];
2920                 alias slices = inputs[0..($ - 1)];
2921             }
2922             else
2923             {
2924                 enum sizediff_t k = 1;
2925                 alias Slices = Inputs;
2926                 alias slices = inputs;
2927             }
2928 
2929             static assert (allSatisfy!(isMatrix, Slices),
2930                 "eachLower: Every slice input must be a two-dimensional slice");
2931             static if (Slices.length > 1)
2932                 slices.checkShapesMatch;
2933             if (slices[0].anyEmpty)
2934                 return;
2935 
2936             foreach(ref slice; slices)
2937                 assert(!slice.empty);
2938 
2939             immutable(size_t) m = slices[0].length!0;
2940             immutable(size_t) n = slices[0].length!1;
2941 
2942             if ((n + k) < m)
2943             {
2944                 val = m - (n + k);
2945                 .eachImpl!fun(selectBackOf!(val, slices));
2946             }
2947 
2948             size_t i;
2949 
2950             if (k > 0)
2951             {
2952                 foreach(ref slice; slices)
2953                     slice.popFrontExactly!0(k);
2954                 i = k;
2955             }
2956 
2957             do
2958             {
2959                 val = i - k + 1;
2960                 .eachImpl!fun(frontSelectFrontOf!(val, slices));
2961 
2962                 foreach(ref slice; slices)
2963                         slice.popFront!0;
2964                 i++;
2965             } while ((i < (n + k)) && (i < m));
2966         }
2967     }
2968     else
2969     {
2970         alias eachLower = .eachLower!(naryFun!fun);
2971     }
2972 }
2973 
2974 pure nothrow
2975 version(mir_test) unittest
2976 {
2977     import mir.ndslice.allocation: slice;
2978     import mir.ndslice.topology: iota, canonical, universal;
2979     alias AliasSeq(T...) = T;
2980 
2981     pure nothrow
2982     void test(alias func)()
2983     {
2984         //| 1 2 3 |
2985         //| 4 5 6 |
2986         //| 7 8 9 |
2987         auto m = func(iota([3, 3], 1).slice);
2988         m.eachLower!"a = 0"(0);
2989         assert(m == [
2990             [0, 2, 3],
2991             [0, 0, 6],
2992             [0, 0, 0]]);
2993     }
2994 
2995     @safe pure nothrow @nogc
2996     T identity(T)(T x)
2997     {
2998         return x;
2999     }
3000 
3001     alias kinds = AliasSeq!(identity, canonical, universal);
3002     test!(kinds[0]);
3003     test!(kinds[1]);
3004     test!(kinds[2]);
3005 }
3006 
3007 ///
3008 pure nothrow
3009 version(mir_test) unittest
3010 {
3011     import mir.ndslice.allocation: slice;
3012     import mir.ndslice.topology: iota;
3013 
3014     //| 1 2 3 |
3015     //| 4 5 6 |
3016     //| 7 8 9 |
3017     auto m = iota([3, 3], 1).slice;
3018     m.eachLower!"a = 0";
3019     assert(m == [
3020         [1, 2, 3],
3021         [0, 5, 6],
3022         [0, 0, 9]]);
3023 }
3024 
3025 pure nothrow
3026 version(mir_test) unittest
3027 {
3028     import mir.ndslice.allocation: slice;
3029     import mir.ndslice.topology: iota;
3030 
3031     //| 1 2 3 |
3032     //| 4 5 6 |
3033     //| 7 8 9 |
3034     auto m = iota([3, 3], 1).slice;
3035     m.eachLower!"a = 0"(-1);
3036     assert(m == [
3037         [0, 0, 3],
3038         [0, 0, 0],
3039         [0, 0, 0]]);
3040 }
3041 
3042 pure nothrow
3043 version(mir_test) unittest
3044 {
3045     import mir.ndslice.allocation: slice;
3046     import mir.ndslice.topology: iota;
3047 
3048     //| 1 2 3 |
3049     //| 4 5 6 |
3050     //| 7 8 9 |
3051     auto m = iota([3, 3], 1).slice;
3052     m.eachLower!"a = 0"(2);
3053     assert(m == [
3054         [1, 2, 3],
3055         [4, 5, 6],
3056         [0, 8, 9]]);
3057 }
3058 
3059 pure nothrow
3060 version(mir_test) unittest
3061 {
3062     import mir.ndslice.allocation: slice;
3063     import mir.ndslice.topology: iota;
3064 
3065     //| 1 2 3 |
3066     //| 4 5 6 |
3067     //| 7 8 9 |
3068     auto m = iota([3, 3], 1).slice;
3069     m.eachLower!"a = 0"(-2);
3070     assert(m == [
3071         [0, 0, 0],
3072         [0, 0, 0],
3073         [0, 0, 0]]);
3074 }
3075 
3076 pure nothrow
3077 version(mir_test) unittest
3078 {
3079     import mir.ndslice.allocation: slice;
3080     import mir.ndslice.topology: iota;
3081 
3082     //| 1  2  3  4 |
3083     //| 5  6  7  8 |
3084     //| 9 10 11 12 |
3085     auto m = iota([3, 4], 1).slice;
3086     m.eachLower!"a = 0"(0);
3087     assert(m == [
3088         [0, 2, 3, 4],
3089         [0, 0, 7, 8],
3090         [0, 0, 0, 12]]);
3091 }
3092 
3093 pure nothrow
3094 version(mir_test) unittest
3095 {
3096     import mir.ndslice.allocation: slice;
3097     import mir.ndslice.topology: iota;
3098 
3099     //| 1  2  3  4 |
3100     //| 5  6  7  8 |
3101     //| 9 10 11 12 |
3102     auto m = iota([3, 4], 1).slice;
3103     m.eachLower!"a = 0";
3104     assert(m == [
3105         [1, 2, 3, 4],
3106         [0, 6, 7, 8],
3107         [0, 0, 11, 12]]);
3108 }
3109 
3110 pure nothrow
3111 version(mir_test) unittest
3112 {
3113     import mir.ndslice.allocation: slice;
3114     import mir.ndslice.topology: iota;
3115 
3116     //| 1  2  3  4 |
3117     //| 5  6  7  8 |
3118     //| 9 10 11 12 |
3119     auto m = iota([3, 4], 1).slice;
3120     m.eachLower!"a = 0"(-1);
3121     assert(m == [
3122         [0, 0, 3, 4],
3123         [0, 0, 0, 8],
3124         [0, 0, 0, 0]]);
3125 }
3126 
3127 pure nothrow
3128 version(mir_test) unittest
3129 {
3130     import mir.ndslice.allocation: slice;
3131     import mir.ndslice.topology: iota;
3132 
3133     //| 1  2  3  4 |
3134     //| 5  6  7  8 |
3135     //| 9 10 11 12 |
3136     auto m = iota([3, 4], 1).slice;
3137     m.eachLower!"a = 0"(2);
3138     assert(m == [
3139         [1, 2, 3, 4],
3140         [5, 6, 7, 8],
3141         [0, 10, 11, 12]]);
3142 }
3143 
3144 pure nothrow
3145 version(mir_test) unittest
3146 {
3147     import mir.ndslice.allocation: slice;
3148     import mir.ndslice.topology: iota;
3149 
3150     //| 1  2  3  4 |
3151     //| 5  6  7  8 |
3152     //| 9 10 11 12 |
3153     auto m = iota([3, 4], 1).slice;
3154     m.eachLower!"a = 0"(-2);
3155     assert(m == [
3156         [0, 0, 0, 4],
3157         [0, 0, 0, 0],
3158         [0, 0, 0, 0]]);
3159 }
3160 
3161 pure nothrow
3162 version(mir_test) unittest
3163 {
3164     import mir.ndslice.allocation: slice;
3165     import mir.ndslice.topology: iota;
3166 
3167     //|  1  2  3 |
3168     //|  4  5  6 |
3169     //|  7  8  9 |
3170     //| 10 11 12 |
3171     auto m = iota([4, 3], 1).slice;
3172     m.eachLower!"a = 0"(0);
3173     assert(m == [
3174         [0, 2, 3],
3175         [0, 0, 6],
3176         [0, 0, 0],
3177         [0, 0, 0]]);
3178 }
3179 
3180 pure nothrow
3181 version(mir_test) unittest
3182 {
3183     import mir.ndslice.allocation: slice;
3184     import mir.ndslice.topology: iota;
3185 
3186     //|  1  2  3 |
3187     //|  4  5  6 |
3188     //|  7  8  9 |
3189     //| 10 11 12 |
3190     auto m = iota([4, 3], 1).slice;
3191     m.eachLower!"a = 0";
3192     assert(m == [
3193         [1, 2, 3],
3194         [0, 5, 6],
3195         [0, 0, 9],
3196         [0, 0, 0]]);
3197 }
3198 
3199 pure nothrow
3200 version(mir_test) unittest
3201 {
3202     import mir.ndslice.allocation: slice;
3203     import mir.ndslice.topology: iota;
3204 
3205     //|  1  2  3 |
3206     //|  4  5  6 |
3207     //|  7  8  9 |
3208     //| 10 11 12 |
3209     auto m = iota([4, 3], 1).slice;
3210     m.eachLower!"a = 0"(-1);
3211     assert(m == [
3212         [0, 0, 3],
3213         [0, 0, 0],
3214         [0, 0, 0],
3215         [0, 0, 0]]);
3216 }
3217 
3218 pure nothrow
3219 version(mir_test) unittest
3220 {
3221     import mir.ndslice.allocation: slice;
3222     import mir.ndslice.topology: iota;
3223 
3224     //|  1  2  3 |
3225     //|  4  5  6 |
3226     //|  7  8  9 |
3227     //| 10 11 12 |
3228     auto m = iota([4, 3], 1).slice;
3229     m.eachLower!"a = 0"(2);
3230     assert(m == [
3231         [1, 2, 3],
3232         [4, 5, 6],
3233         [0, 8, 9],
3234         [0, 0, 12]]);
3235 }
3236 
3237 pure nothrow
3238 version(mir_test) unittest
3239 {
3240     import mir.ndslice.allocation: slice;
3241     import mir.ndslice.topology: iota;
3242 
3243     //|  1  2  3 |
3244     //|  4  5  6 |
3245     //|  7  8  9 |
3246     //| 10 11 12 |
3247     auto m = iota([4, 3], 1).slice;
3248     m.eachLower!"a = 0"(-2);
3249     assert(m == [
3250         [0, 0, 0],
3251         [0, 0, 0],
3252         [0, 0, 0],
3253         [0, 0, 0]]);
3254 }
3255 
3256 /// Swap two slices
3257 pure nothrow
3258 version(mir_test) unittest
3259 {
3260     import mir.utility : swap;
3261     import mir.ndslice.allocation : slice;
3262     import mir.ndslice.topology : as, iota;
3263 
3264     //| 0 1 2 |
3265     //| 3 4 5 |
3266     //| 6 7 8 |
3267     auto a = iota([3, 3]).as!double.slice;
3268     //| 10 11 12 |
3269     //| 13 14 15 |
3270     //| 16 17 18 |
3271     auto b = iota([3, 3], 10).as!double.slice;
3272 
3273     eachLower!swap(a, b);
3274 
3275     assert(a == [
3276         [ 0,  1, 2],
3277         [13,  4, 5],
3278         [16, 17, 8]]);
3279     assert(b == [
3280         [10, 11, 12],
3281         [ 3, 14, 15],
3282         [ 6,  7, 18]]);
3283 }
3284 
3285 /// Swap two zipped slices
3286 pure nothrow
3287 version(mir_test) unittest
3288 {
3289     import mir.utility : swap;
3290     import mir.ndslice.allocation : slice;
3291     import mir.ndslice.topology : as, zip, iota;
3292 
3293     //| 0 1 2 |
3294     //| 3 4 5 |
3295     //| 6 7 8 |
3296     auto a = iota([3, 3]).as!double.slice;
3297     //| 10 11 12 |
3298     //| 13 14 15 |
3299     //| 16 17 18 |
3300     auto b = iota([3, 3], 10).as!double.slice;
3301 
3302     auto z = zip(a, b);
3303 
3304     z.eachLower!(z => swap(z.a, z.b));
3305 
3306     assert(a == [
3307         [ 0,  1, 2],
3308         [13,  4, 5],
3309         [16, 17, 8]]);
3310     assert(b == [
3311         [10, 11, 12],
3312         [ 3, 14, 15],
3313         [ 6,  7, 18]]);
3314 }
3315 
3316 /++
3317 The call `eachUpper!(fun)(slice1, ..., sliceN)` evaluates `fun` on the upper
3318 triangle in `slice1, ..., sliceN`, respectively.
3319 
3320 `eachUpper` allows iterating multiple slices in the lockstep.
3321 
3322 Params:
3323     fun = A function
3324 See_Also:
3325     This is functionally similar to $(LREF each).
3326 +/
3327 template eachUpper(alias fun)
3328 {
3329     import mir.functional: naryFun;
3330 
3331     static if (__traits(isSame, naryFun!fun, fun))
3332     {
3333         /++
3334         Params:
3335             inputs = One or more two-dimensional slices and an optional
3336                      integer, `k`.
3337 
3338             The value `k` determines which diagonals will have the function
3339             applied:
3340             For k = 0, the function is also applied to the main diagonal
3341             For k = 1 (default), only the non-main diagonals above the main
3342             diagonal will have the function applied.
3343             For k > 1, fewer diagonals below the main diagonal will have the
3344             function applied.
3345             For k < 0, more diagonals above the main diagonal will have the
3346             function applied.
3347         +/
3348         void eachUpper(Inputs...)(scope Inputs inputs)
3349             if (((Inputs.length > 1) &&
3350                  (isIntegral!(Inputs[$ - 1]))) ||
3351                 (Inputs.length))
3352         {
3353             import mir.ndslice.traits : isMatrix;
3354 
3355             size_t val;
3356 
3357             static if ((Inputs.length > 1) && (isIntegral!(Inputs[$ - 1])))
3358             {
3359                 immutable(sizediff_t) k = inputs[$ - 1];
3360                 alias Slices = Inputs[0..($ - 1)];
3361                 alias slices = inputs[0..($ - 1)];
3362             }
3363             else
3364             {
3365                 enum sizediff_t k = 1;
3366                 alias Slices = Inputs;
3367                 alias slices = inputs;
3368             }
3369 
3370             static assert (allSatisfy!(isMatrix, Slices),
3371                 "eachUpper: Every slice input must be a two-dimensional slice");
3372             static if (Slices.length > 1)
3373                 slices.checkShapesMatch;
3374             if (slices[0].anyEmpty)
3375                 return;
3376 
3377             foreach(ref slice; slices)
3378                 assert(!slice.empty);
3379 
3380             immutable(size_t) m = slices[0].length!0;
3381             immutable(size_t) n = slices[0].length!1;
3382 
3383             size_t i;
3384 
3385             if (k < 0)
3386             {
3387                 val = -k;
3388                 .eachImpl!fun(selectFrontOf!(val, slices));
3389 
3390                 foreach(ref slice; slices)
3391                     slice.popFrontExactly!0(-k);
3392                 i = -k;
3393             }
3394 
3395             do
3396             {
3397                 val = (n - k) - i;
3398                 .eachImpl!fun(frontSelectBackOf!(val, slices));
3399 
3400                 foreach(ref slice; slices)
3401                     slice.popFront;
3402                 i++;
3403             } while ((i < (n - k)) && (i < m));
3404         }
3405     }
3406     else
3407     {
3408         alias eachUpper = .eachUpper!(naryFun!fun);
3409     }
3410 }
3411 
3412 pure nothrow
3413 version(mir_test) unittest
3414 {
3415     import mir.ndslice.allocation: slice;
3416     import mir.ndslice.topology: iota, canonical, universal;
3417 
3418     pure nothrow
3419     void test(alias func)()
3420     {
3421         //| 1 2 3 |
3422         //| 4 5 6 |
3423         //| 7 8 9 |
3424         auto m = func(iota([3, 3], 1).slice);
3425         m.eachUpper!"a = 0"(0);
3426         assert(m == [
3427             [0, 0, 0],
3428             [4, 0, 0],
3429             [7, 8, 0]]);
3430     }
3431 
3432     @safe pure nothrow @nogc
3433     T identity(T)(T x)
3434     {
3435         return x;
3436     }
3437 
3438     alias kinds = AliasSeq!(identity, canonical, universal);
3439     test!(kinds[0]);
3440     test!(kinds[1]);
3441     test!(kinds[2]);
3442 }
3443 
3444 ///
3445 pure nothrow
3446 version(mir_test) unittest
3447 {
3448     import mir.ndslice.allocation: slice;
3449     import mir.ndslice.topology: iota;
3450 
3451     //| 1 2 3 |
3452     //| 4 5 6 |
3453     //| 7 8 9 |
3454     auto m = iota([3, 3], 1).slice;
3455     m.eachUpper!"a = 0";
3456     assert(m == [
3457         [1, 0, 0],
3458         [4, 5, 0],
3459         [7, 8, 9]]);
3460 }
3461 
3462 pure nothrow
3463 version(mir_test) unittest
3464 {
3465     import mir.ndslice.allocation: slice;
3466     import mir.ndslice.topology: iota;
3467 
3468     //| 1 2 3 |
3469     //| 4 5 6 |
3470     //| 7 8 9 |
3471     auto m = iota([3, 3], 1).slice;
3472     m.eachUpper!"a = 0"(-1);
3473     assert(m == [
3474         [0, 0, 0],
3475         [0, 0, 0],
3476         [7, 0, 0]]);
3477 }
3478 
3479 pure nothrow
3480 version(mir_test) unittest
3481 {
3482     import mir.ndslice.allocation: slice;
3483     import mir.ndslice.topology: iota;
3484 
3485     //| 1 2 3 |
3486     //| 4 5 6 |
3487     //| 7 8 9 |
3488     auto m = iota([3, 3], 1).slice;
3489     m.eachUpper!"a = 0"(2);
3490     assert(m == [
3491         [1, 2, 0],
3492         [4, 5, 6],
3493         [7, 8, 9]]);
3494 }
3495 
3496 pure nothrow
3497 version(mir_test) unittest
3498 {
3499     import mir.ndslice.allocation: slice;
3500     import mir.ndslice.topology: iota;
3501 
3502     //| 1 2 3 |
3503     //| 4 5 6 |
3504     //| 7 8 9 |
3505     auto m = iota([3, 3], 1).slice;
3506     m.eachUpper!"a = 0"(-2);
3507     assert(m == [
3508         [0, 0, 0],
3509         [0, 0, 0],
3510         [0, 0, 0]]);
3511 }
3512 
3513 pure nothrow
3514 version(mir_test) unittest
3515 {
3516     import mir.ndslice.allocation: slice;
3517     import mir.ndslice.topology: iota;
3518 
3519     //| 1  2  3  4 |
3520     //| 5  6  7  8 |
3521     //| 9 10 11 12 |
3522     auto m = iota([3, 4], 1).slice;
3523     m.eachUpper!"a = 0"(0);
3524     assert(m == [
3525         [0, 0, 0, 0],
3526         [5, 0, 0, 0],
3527         [9, 10, 0, 0]]);
3528 }
3529 
3530 pure nothrow
3531 version(mir_test) unittest
3532 {
3533     import mir.ndslice.allocation: slice;
3534     import mir.ndslice.topology: iota;
3535 
3536     //| 1  2  3  4 |
3537     //| 5  6  7  8 |
3538     //| 9 10 11 12 |
3539     auto m = iota([3, 4], 1).slice;
3540     m.eachUpper!"a = 0";
3541     assert(m == [
3542         [1, 0, 0, 0],
3543         [5, 6, 0, 0],
3544         [9, 10, 11, 0]]);
3545 }
3546 
3547 pure nothrow
3548 version(mir_test) unittest
3549 {
3550     import mir.ndslice.allocation: slice;
3551     import mir.ndslice.topology: iota;
3552 
3553     //| 1  2  3  4 |
3554     //| 5  6  7  8 |
3555     //| 9 10 11 12 |
3556     auto m = iota([3, 4], 1).slice;
3557     m.eachUpper!"a = 0"(-1);
3558     assert(m == [
3559         [0, 0, 0, 0],
3560         [0, 0, 0, 0],
3561         [9, 0, 0, 0]]);
3562 }
3563 
3564 pure nothrow
3565 version(mir_test) unittest
3566 {
3567     import mir.ndslice.allocation: slice;
3568     import mir.ndslice.topology: iota;
3569 
3570     //| 1  2  3  4 |
3571     //| 5  6  7  8 |
3572     //| 9 10 11 12 |
3573     auto m = iota([3, 4], 1).slice;
3574     m.eachUpper!"a = 0"(2);
3575     assert(m == [
3576         [1, 2, 0, 0],
3577         [5, 6, 7, 0],
3578         [9, 10, 11, 12]]);
3579 }
3580 
3581 pure nothrow
3582 version(mir_test) unittest
3583 {
3584     import mir.ndslice.allocation: slice;
3585     import mir.ndslice.topology: iota;
3586 
3587     //| 1  2  3  4 |
3588     //| 5  6  7  8 |
3589     //| 9 10 11 12 |
3590     auto m = iota([3, 4], 1).slice;
3591     m.eachUpper!"a = 0"(-2);
3592     assert(m == [
3593         [0, 0, 0, 0],
3594         [0, 0, 0, 0],
3595         [0, 0, 0, 0]]);
3596 }
3597 
3598 pure nothrow
3599 version(mir_test) unittest
3600 {
3601     import mir.ndslice.allocation: slice;
3602     import mir.ndslice.topology: iota;
3603 
3604     //|  1  2  3 |
3605     //|  4  5  6 |
3606     //|  7  8  9 |
3607     //| 10 11 12 |
3608     auto m = iota([4, 3], 1).slice;
3609     m.eachUpper!"a = 0"(0);
3610     assert(m == [
3611         [0, 0, 0],
3612         [4, 0, 0],
3613         [7, 8, 0],
3614         [10, 11, 12]]);
3615 }
3616 
3617 pure nothrow
3618 version(mir_test) unittest
3619 {
3620     import mir.ndslice.allocation: slice;
3621     import mir.ndslice.topology: iota;
3622 
3623     //|  1  2  3 |
3624     //|  4  5  6 |
3625     //|  7  8  9 |
3626     //| 10 11 12 |
3627     auto m = iota([4, 3], 1).slice;
3628     m.eachUpper!"a = 0";
3629     assert(m == [
3630         [1, 0, 0],
3631         [4, 5, 0],
3632         [7, 8, 9],
3633         [10, 11, 12]]);
3634 }
3635 
3636 pure nothrow
3637 version(mir_test) unittest
3638 {
3639     import mir.ndslice.allocation: slice;
3640     import mir.ndslice.topology: iota;
3641 
3642     //|  1  2  3 |
3643     //|  4  5  6 |
3644     //|  7  8  9 |
3645     //| 10 11 12 |
3646     auto m = iota([4, 3], 1).slice;
3647     m.eachUpper!"a = 0"(-1);
3648     assert(m == [
3649         [0, 0, 0],
3650         [0, 0, 0],
3651         [7, 0, 0],
3652         [10, 11, 0]]);
3653 }
3654 
3655 pure nothrow
3656 version(mir_test) unittest
3657 {
3658     import mir.ndslice.allocation: slice;
3659     import mir.ndslice.topology: iota;
3660 
3661     //|  1  2  3 |
3662     //|  4  5  6 |
3663     //|  7  8  9 |
3664     //| 10 11 12 |
3665     auto m = iota([4, 3], 1).slice;
3666     m.eachUpper!"a = 0"(2);
3667     assert(m == [
3668         [1, 2, 0],
3669         [4, 5, 6],
3670         [7, 8, 9],
3671         [10, 11, 12]]);
3672 }
3673 
3674 pure nothrow
3675 version(mir_test) unittest
3676 {
3677     import mir.ndslice.allocation: slice;
3678     import mir.ndslice.topology: iota;
3679 
3680     //|  1  2  3 |
3681     //|  4  5  6 |
3682     //|  7  8  9 |
3683     //| 10 11 12 |
3684     auto m = iota([4, 3], 1).slice;
3685     m.eachUpper!"a = 0"(-2);
3686     assert(m == [
3687         [0, 0, 0],
3688         [0, 0, 0],
3689         [0, 0, 0],
3690         [10, 0, 0]]);
3691 }
3692 
3693 /// Swap two slices
3694 pure nothrow
3695 version(mir_test) unittest
3696 {
3697     import mir.utility : swap;
3698     import mir.ndslice.allocation : slice;
3699     import mir.ndslice.topology : as, iota;
3700 
3701     //| 0 1 2 |
3702     //| 3 4 5 |
3703     //| 6 7 8 |
3704     auto a = iota([3, 3]).as!double.slice;
3705     //| 10 11 12 |
3706     //| 13 14 15 |
3707     //| 16 17 18 |
3708     auto b = iota([3, 3], 10).as!double.slice;
3709 
3710     eachUpper!swap(a, b);
3711 
3712     assert(a == [
3713         [0, 11, 12],
3714         [3,  4, 15],
3715         [6,  7,  8]]);
3716     assert(b == [
3717         [10,  1,  2],
3718         [13, 14,  5],
3719         [16, 17, 18]]);
3720 }
3721 
3722 /// Swap two zipped slices
3723 pure nothrow
3724 version(mir_test) unittest
3725 {
3726     import mir.utility : swap;
3727     import mir.ndslice.allocation : slice;
3728     import mir.ndslice.topology : as, zip, iota;
3729 
3730     //| 0 1 2 |
3731     //| 3 4 5 |
3732     //| 6 7 8 |
3733     auto a = iota([3, 3]).as!double.slice;
3734     //| 10 11 12 |
3735     //| 13 14 15 |
3736     //| 16 17 18 |
3737     auto b = iota([3, 3], 10).as!double.slice;
3738 
3739     auto z = zip(a, b);
3740 
3741     z.eachUpper!(z => swap(z.a, z.b));
3742 
3743     assert(a == [
3744         [0, 11, 12],
3745         [3,  4, 15],
3746         [6,  7,  8]]);
3747     assert(b == [
3748         [10,  1,  2],
3749         [13, 14,  5],
3750         [16, 17, 18]]);
3751 }
3752 
3753 // uniq
3754 /**
3755 Lazily iterates unique consecutive elements of the given range (functionality
3756 akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
3757 utility). Equivalence of elements is assessed by using the predicate
3758 $(D pred), by default $(D "a == b"). The predicate is passed to
3759 $(REF nary, mir,functional), and can either accept a string, or any callable
3760 that can be executed via $(D pred(element, element)). If the given range is
3761 bidirectional, $(D uniq) also yields a
3762 `std,range,primitives`.
3763 Params:
3764     pred = Predicate for determining equivalence between range elements.
3765 */
3766 template uniq(alias pred = "a == b")
3767 {
3768     static if (__traits(isSame, naryFun!pred, pred))
3769     {
3770         /++
3771         Params:
3772             r = An input range of elements to filter.
3773         Returns:
3774             An input range of
3775             consecutively unique elements in the original range. If `r` is also a
3776             forward range or bidirectional range, the returned range will be likewise.
3777         +/
3778         Uniq!(naryFun!pred, Range) uniq(Range)(Range r)
3779          if (isInputRange!Range && !isSlice!Range)
3780         {
3781             import core.lifetime: move;
3782             return typeof(return)(r.move);
3783         }
3784 
3785         /// ditto
3786         auto uniq(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
3787         {
3788             import mir.ndslice.topology: flattened; 
3789             import core.lifetime: move;
3790             auto r = slice.move.flattened;
3791             return Uniq!(pred, typeof(r))(move(r));
3792         }
3793     }
3794     else
3795         alias uniq = .uniq!(naryFun!pred);
3796 }
3797 
3798 ///
3799 @safe version(mir_test) unittest
3800 {
3801     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
3802     assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ]));
3803 
3804     import std.algorithm.mutation : copy;
3805     // Filter duplicates in-place using copy
3806     arr.length -= arr.uniq.copy(arr).length;
3807     assert(arr == [ 1, 2, 3, 4, 5 ]);
3808 
3809     // Note that uniqueness is only determined consecutively; duplicated
3810     // elements separated by an intervening different element will not be
3811     // eliminated:
3812     assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
3813 }
3814 
3815 /// N-dimensional case
3816 version(mir_test)
3817 @safe pure unittest
3818 {
3819     import mir.ndslice.fuse;
3820     import mir.ndslice.topology: byDim, map, iota;
3821 
3822     auto matrix = [ [1, 2, 2], [2, 2, 3], [4, 4, 4] ].fuse;
3823 
3824     assert(matrix.uniq.equal([ 1, 2, 3, 4 ]));
3825 
3826     // unique elements for each row
3827     assert(matrix.byDim!0.map!uniq.equal!equal([ [1, 2], [2, 3], [4] ]));
3828 }
3829 
3830 /++
3831 Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (betterC rework)
3832 +/
3833 struct Uniq(alias pred, Range)
3834 {
3835     Range _input;
3836 
3837     ref opSlice() inout
3838     {
3839         return this;
3840     }
3841 
3842     void popFront() scope
3843     {
3844         assert(!empty, "Attempting to popFront an empty uniq.");
3845         auto last = _input.front;
3846         do
3847         {
3848             _input.popFront();
3849         }
3850         while (!_input.empty && pred(last, _input.front));
3851     }
3852 
3853     auto ref front() @property
3854     {
3855         assert(!empty, "Attempting to fetch the front of an empty uniq.");
3856         return _input.front;
3857     }
3858 
3859     import std.range.primitives: isBidirectionalRange;
3860 
3861     static if (isBidirectionalRange!Range)
3862     {
3863         void popBack() scope
3864         {
3865             assert(!empty, "Attempting to popBack an empty uniq.");
3866             auto last = _input.back;
3867             do
3868             {
3869                 _input.popBack();
3870             }
3871             while (!_input.empty && pred(last, _input.back));
3872         }
3873 
3874         auto ref back() scope return @property
3875         {
3876             assert(!empty, "Attempting to fetch the back of an empty uniq.");
3877             return _input.back;
3878         }
3879     }
3880 
3881     static if (isInfinite!Range)
3882     {
3883         enum bool empty = false;  // Propagate infiniteness.
3884     }
3885     else
3886     {
3887         @property bool empty() const { return _input.empty; }
3888     }
3889 
3890     import std.range.primitives: isForwardRange;
3891 
3892     static if (isForwardRange!Range)
3893     {
3894         @property typeof(this) save() scope return
3895         {
3896             return typeof(this)(_input.save);
3897         }
3898     }
3899 }
3900 
3901 version(none)
3902 @safe version(mir_test) unittest
3903 {
3904     import std.internal.test.dummyrange;
3905     import std.range;
3906 
3907     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
3908     auto r = uniq(arr);
3909     static assert(isForwardRange!(typeof(r)));
3910 
3911     assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
3912     assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
3913 
3914     foreach (DummyType; AllDummyRanges)
3915     {
3916         DummyType d;
3917         auto u = uniq(d);
3918         assert(equal(u, [1,2,3,4,5,6,7,8,9,10]));
3919 
3920         static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u)));
3921 
3922         static if (d.rt >= RangeType.Bidirectional)
3923         {
3924             assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
3925         }
3926     }
3927 }
3928 
3929 @safe version(mir_test) unittest // https://issues.dlang.org/show_bug.cgi?id=17264
3930 {
3931     const(int)[] var = [0, 1, 1, 2];
3932     assert(var.uniq.equal([0, 1, 2]));
3933 }
3934 
3935 @safe version(mir_test) unittest {
3936     import mir.ndslice.allocation;
3937     import mir.math.common: approxEqual;
3938     auto x = rcslice!double(2);
3939     auto y = rcslice!double(2);
3940     x[] = [2, 3];
3941     y[] = [2, 3];
3942     assert(equal!approxEqual(x,y));
3943 }
3944 
3945 /++
3946 Implements the higher order filter function. The predicate is passed to
3947 `mir.functional.naryFun`, and can either accept a string, or any callable
3948 that can be executed via `pred(element)`.
3949 Params:
3950     pred = Function to apply to each element of range
3951 Returns:
3952     `filter!(pred)(range)` returns a new range containing only elements `x` in `range` for
3953     which `pred(x)` returns `true`.
3954 See_Also:
3955     $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
3956 Note:
3957     $(RED User and library code MUST call `empty` method ahead each call of pair or one of `front` and `popFront` methods.)
3958 +/
3959 template filter(alias pred = "a")
3960 {
3961     static if (__traits(isSame, naryFun!pred, pred))
3962     {
3963         /++
3964         Params:
3965             r = An input range of elements to filter.
3966         Returns:
3967             A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
3968         +/
3969         Filter!(naryFun!pred, Range) filter(Range)(Range r)
3970             if (isInputRange!Range && !isSlice!Range)
3971         {
3972             import core.lifetime: move;
3973             return typeof(return)(r.move);
3974         }
3975 
3976         /// ditto
3977         auto filter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
3978         {
3979             import mir.ndslice.topology: flattened; 
3980             import core.lifetime: move;
3981             auto r = slice.move.flattened;
3982             return Filter!(pred, typeof(r))(move(r));
3983         }
3984     }
3985     else
3986         alias filter = .filter!(naryFun!pred);
3987 }
3988 
3989 /// ditto
3990 struct Filter(alias pred, Range)
3991 {
3992     Range _input;
3993     version(assert) bool _freshEmpty;
3994 
3995     ref opSlice() inout
3996     {
3997         return this;
3998     }
3999 
4000     void popFront() scope
4001     {
4002         assert(!_input.empty, "Attempting to popFront an empty Filter.");
4003         version(assert) assert(_freshEmpty, "Attempting to pop the front of a Filter without calling '.empty' method ahead.");
4004         version(assert) _freshEmpty = false;
4005         _input.popFront;
4006     }
4007 
4008     auto ref front() @property
4009     {
4010         assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
4011         version(assert) assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
4012         return _input.front;
4013     }
4014 
4015     bool empty() @property
4016     {
4017         version(assert) _freshEmpty = true;
4018         for (;;)
4019         {
4020             if (auto r = _input.empty)
4021                 return true;
4022             if (pred(_input.front))
4023                 return false;
4024             _input.popFront;
4025         }
4026     }
4027 
4028     import std.range.primitives: isForwardRange;
4029     static if (isForwardRange!Range)
4030     {
4031         @property typeof(this) save() scope return
4032         {
4033             return typeof(this)(_input.save);
4034         }
4035     }
4036 }
4037 
4038 ///
4039 version(mir_test)
4040 @safe pure nothrow unittest
4041 {
4042     int[] arr = [ 0, 1, 2, 3, 4, 5 ];
4043 
4044     // Filter below 3
4045     auto small = filter!(a => a < 3)(arr);
4046     assert(equal(small, [ 0, 1, 2 ]));
4047 
4048     // Filter again, but with Uniform Function Call Syntax (UFCS)
4049     auto sum = arr.filter!(a => a < 3);
4050     assert(equal(sum, [ 0, 1, 2 ]));
4051 
4052     // Filter with the default predicate
4053     auto nonZeros = arr.filter;
4054     assert(equal(nonZeros, [ 1, 2, 3, 4, 5 ]));
4055 
4056     // In combination with concatenation() to span multiple ranges
4057     import mir.ndslice.concatenation;
4058 
4059     int[] a = [ 3, -2, 400 ];
4060     int[] b = [ 100, -101, 102 ];
4061     auto r = concatenation(a, b).filter!(a => a > 0);
4062     assert(equal(r, [ 3, 400, 100, 102 ]));
4063 
4064     // Mixing convertible types is fair game, too
4065     double[] c = [ 2.5, 3.0 ];
4066     auto r1 = concatenation(c, a, b).filter!(a => cast(int) a != a);
4067     assert(equal(r1, [ 2.5 ]));
4068 }
4069 
4070 /// N-dimensional filtering
4071 version(mir_test)
4072 @safe pure unittest
4073 {
4074     import mir.ndslice.fuse;
4075     import mir.ndslice.topology: byDim, map;
4076 
4077     auto matrix =
4078         [[   3,   -2, 400 ],
4079          [ 100, -101, 102 ]].fuse;
4080 
4081     alias filterPositive = filter!"a > 0";
4082 
4083     // filter all elements in the matrix
4084     auto r = filterPositive(matrix);
4085     assert(equal(r, [ 3, 400, 100, 102 ]));
4086 
4087     // filter all elements for each row
4088     auto rr = matrix.byDim!0.map!filterPositive;
4089     assert(equal!equal(rr, [ [3, 400], [100, 102] ]));
4090 
4091     // filter all elements for each column
4092     auto rc = matrix.byDim!1.map!filterPositive;
4093     assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
4094 }
4095 
4096 /++
4097 Implements the higher order filter and map function. The predicate and map functions are passed to
4098 `mir.functional.naryFun`, and can either accept a string, or any callable
4099 that can be executed via `pred(element)` and `map(element)`.
4100 Params:
4101     pred = Filter function to apply to each element of range (optional)
4102     map = Map function to apply to each  element of range
4103 Returns:
4104     `rcfilter!(pred)(range)` returns a new RCArray containing only elements `map(x)` in `range` for
4105     which `pred(x)` returns `true`.
4106 See_Also:
4107     $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
4108 +/
4109 template rcfilter(alias pred = "a", alias map = "a")
4110 {
4111     static if (__traits(isSame, naryFun!pred, pred) && __traits(isSame, naryFun!map, map))
4112     {
4113         /++ 
4114         Params:
4115             r = An input range of elements to filter.
4116         Returns:
4117             A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
4118         +/
4119         auto rcfilter(Range)(Range r)
4120             if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
4121         {
4122             import core.lifetime: forward;
4123             import mir.appender: scopedBuffer;
4124             import mir.primitives: isInputRange;
4125             import mir.rc.array: RCArray;
4126 
4127             alias T = typeof(map(r.front));
4128             auto buffer = scopedBuffer!T;
4129             foreach (ref e; r)
4130             {
4131                 if (pred(e))
4132                 {
4133                     static if (__traits(isSame, naryFun!"a", map))
4134                         buffer.put(forward!e);
4135                     else
4136                         buffer.put(map(forward!e));
4137                 }
4138             }
4139             return () @trusted
4140             {
4141                 auto ret = RCArray!T(buffer.length);
4142                 buffer.moveDataAndEmplaceTo(ret[]);
4143                 return ret;
4144             } ();
4145         }
4146 
4147         /// ditto
4148         auto rcfilter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
4149             if (N > 1)
4150         {
4151             import mir.ndslice.topology: flattened; 
4152             import core.lifetime: move;
4153             return rcfilter(slice.move.flattened);
4154         }
4155     }
4156     else
4157         alias rcfilter = .rcfilter!(naryFun!pred, naryFun!map);
4158 }
4159 
4160 ///
4161 version(mir_test)
4162 @safe pure nothrow @nogc unittest
4163 {
4164     import mir.ndslice.topology: iota;
4165 
4166     auto val = 3;
4167     auto factor = 5;
4168     // Filter iota 2x3 matrix below 3
4169     assert(iota(2, 3).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
4170     // Filter and map below 3
4171     assert(6.iota.rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
4172 }
4173 
4174 version(mir_test)
4175 @safe pure nothrow @nogc unittest
4176 {
4177     import mir.ndslice.topology: iota, as;
4178 
4179     auto val = 3;
4180     auto factor = 5;
4181     // Filter iota 2x3 matrix below 3
4182     assert(iota(2, 3).as!(const int).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
4183     // Filter and map below 3
4184     assert(6.iota.as!(immutable int).rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
4185 }
4186 
4187 /++
4188 Implements the homonym function (also known as `accumulate`, $(D
4189 compress), `inject`, or `foldl`) present in various programming
4190 languages of functional flavor. The call `fold!(fun)(slice, seed)`
4191 first assigns `seed` to an internal variable `result`,
4192 also called the accumulator. Then, for each element `x` in $(D
4193 slice), `result = fun(result, x)` gets evaluated. Finally, $(D
4194 result) is returned.
4195 
4196 Params:
4197     fun = the predicate function to apply to the elements
4198 
4199 See_Also:
4200     $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
4201     $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
4202     precise summing of floating point numbers.
4203     This is functionally equivalent to $(LREF reduce) with the argument order
4204     reversed.
4205 +/
4206 template fold(alias fun)
4207 {
4208     /++
4209     Params:
4210         slice = A slice, range, and array.
4211         seed = An initial accumulation value.
4212     Returns:
4213         the accumulated result
4214     +/
4215     @optmath auto fold(Slice, S)(scope Slice slice, S seed)
4216     {
4217         import core.lifetime: move;
4218         return reduce!fun(seed, slice.move);
4219     }
4220 }
4221 
4222 ///
4223 version(mir_test)
4224 @safe pure nothrow
4225 unittest
4226 {
4227     import mir.ndslice.slice: sliced;
4228     import mir.ndslice.topology: map;
4229 
4230     auto arr = [1, 2, 3, 4, 5].sliced;
4231 
4232     // Sum all elements
4233     assert(arr.fold!((a, b) => a + b)(0) == 15);
4234     assert(arr.fold!((a, b) => a + b)(6) == 21);
4235 
4236     // Can be used in a UFCS chain
4237     assert(arr.map!(a => a + 1).fold!((a, b) => a + b)(0) == 20);
4238 
4239     // Return the last element of any range
4240     assert(arr.fold!((a, b) => b)(0) == 5);
4241 }
4242 
4243 /// Works for matrices
4244 version(mir_test)
4245 @safe pure
4246 unittest
4247 {
4248     import mir.ndslice.fuse: fuse;
4249 
4250     auto arr = [
4251         [1, 2, 3], 
4252         [4, 5, 6]
4253     ].fuse;
4254 
4255     assert(arr.fold!((a, b) => a + b)(0) == 21);
4256 }
4257 
4258 version(mir_test)
4259 @safe pure nothrow
4260 unittest
4261 {
4262     import mir.ndslice.topology: map;
4263 
4264     int[] arr = [1, 2, 3, 4, 5];
4265 
4266     // Sum all elements
4267     assert(arr.fold!((a, b) => a + b)(0) == 15);
4268     assert(arr.fold!((a, b) => a + b)(6) == 21);
4269 
4270     // Can be used in a UFCS chain
4271     assert(arr.map!(a => a + 1).fold!((a, b) => a + b)(0) == 20);
4272 
4273     // Return the last element of any range
4274     assert(arr.fold!((a, b) => b)(0) == 5);
4275 }
4276 
4277 version(mir_test)
4278 @safe pure nothrow 
4279 unittest
4280 {
4281     int[] arr = [1];
4282     static assert(!is(typeof(arr.fold!()(0))));
4283     static assert(!is(typeof(arr.fold!(a => a)(0))));
4284     static assert(is(typeof(arr.fold!((a, b) => a)(0))));
4285     assert(arr.length == 1);
4286 }
4287 
4288 unittest
4289 {
4290     import mir.rc.array: RCArray;
4291     import mir.algorithm.iteration: minmaxPos, minPos, maxPos, minmaxIndex, minIndex, maxIndex;
4292 
4293     static immutable a = [0.0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
4294 
4295     auto x = RCArray!double(12);
4296     foreach(i, ref e; x)
4297         e = a[i];
4298     auto y = x.asSlice;
4299     auto z0 = y.minmaxPos;
4300     auto z1 = y.minPos;
4301     auto z2 = y.maxPos;
4302     auto z3 = y.minmaxIndex;
4303     auto z4 = y.minIndex;
4304     auto z5 = y.maxIndex;
4305 }