1 // Written in the D programming language.
2 /**
3 This is a submodule of $(MREF std, algorithm).
4 It contains generic iteration algorithms.
5 
6 $(SCRIPT inhibitQuickIndex = 1;)
7 $(BOOKTABLE Cheat Sheet,
8 $(TR $(TH Function Name) $(TH Description))
9 $(T2 cache,
10         Eagerly evaluates and caches another range's `front`.)
11 $(T2 cacheBidirectional,
12         As above, but also provides `back` and `popBack`.)
13 $(T2 chunkBy,
14         `chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])`
15         returns a range containing 3 subranges: the first with just
16         `[1, 1]`; the second with the elements `[1, 2]` and `[2, 2]`;
17         and the third with just `[2, 1]`.)
18 $(T2 cumulativeFold,
19         `cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])` returns a
20         lazily-evaluated range containing the successive reduced values `1`,
21         `3`, `6`, `10`.)
22 $(T2 each,
23         `each!writeln([1, 2, 3])` eagerly prints the numbers `1`, `2`
24         and `3` on their own lines.)
25 $(T2 filter,
26         `filter!(a => a > 0)([1, -1, 2, 0, -3])` iterates over elements `1`
27         and `2`.)
28 $(T2 filterBidirectional,
29         Similar to `filter`, but also provides `back` and `popBack` at
30         a small increase in cost.)
31 $(T2 fold,
32         `fold!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.)
33 $(T2 group,
34         `group([5, 2, 2, 3, 3])` returns a range containing the tuples
35         `tuple(5, 1)`, `tuple(2, 2)`, and `tuple(3, 2)`.)
36 $(T2 joiner,
37         `joiner(["hello", "world!"], "; ")` returns a range that iterates
38         over the characters `"hello; world!"`. No new string is created -
39         the existing inputs are iterated.)
40 $(T2 map,
41         `map!(a => a * 2)([1, 2, 3])` lazily returns a range with the numbers
42         `2`, `4`, `6`.)
43 $(T2 mean,
44         Colloquially known as the average, `mean([1, 2, 3])` returns `2`.)
45 $(T2 permutations,
46         Lazily computes all permutations using Heap's algorithm.)
47 $(T2 reduce,
48         `reduce!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.
49         This is the old implementation of `fold`.)
50 $(T2 splitWhen,
51         Lazily splits a range by comparing adjacent elements.)
52 $(T2 splitter,
53         Lazily splits a range by a separator.)
54 $(T2 substitute,
55         `[1, 2].substitute(1, 0.1)` returns `[0.1, 2]`.)
56 $(T2 sum,
57         Same as `fold`, but specialized for accurate summation.)
58 $(T2 uniq,
59         Iterates over the unique elements in a range, which is assumed sorted.)
60 )
61 
62 Copyright: Andrei Alexandrescu 2008-.
63 
64 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
65 
66 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
67 
68 Source: $(PHOBOSSRC std/algorithm/iteration.d)
69 
70 Macros:
71 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
72  */
73 module std.algorithm.iteration;
74 
75 import std.functional : unaryFun, binaryFun;
76 import std.range.primitives;
77 import std.traits;
78 import std.typecons : Flag, Yes, No;
79 
80 /++
81 `cache` eagerly evaluates $(REF_ALTTEXT front, front, std,range,primitives) of `range`
82 on each construction or call to $(REF_ALTTEXT popFront, popFront, std,range,primitives),
83 to store the result in a _cache.
84 The result is then directly returned when $(REF_ALTTEXT front, front, std,range,primitives) is called,
85 rather than re-evaluated.
86 
87 This can be a useful function to place in a chain, after functions
88 that have expensive evaluation, as a lazy alternative to $(REF array, std,array).
89 In particular, it can be placed after a call to $(LREF map), or before a call
90 $(REF filter, std,range) or $(REF tee, std,range)
91 
92 `cache` may provide
93 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
94 iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the
95 call to `cacheBidirectional`. Furthermore, a bidirectional _cache will
96 evaluate the "center" element twice, when there is only one element left in
97 the range.
98 
99 `cache` does not provide random access primitives,
100 as `cache` would be unable to _cache the random accesses.
101 If `Range` provides slicing primitives,
102 then `cache` will provide the same slicing primitives,
103 but `hasSlicing!Cache` will not yield true (as the $(REF hasSlicing, std,range,primitives)
104 trait also checks for random access).
105 
106 Params:
107     range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
108 
109 Returns:
110     An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with the cached values of range
111 +/
112 auto cache(Range)(Range range)
113 if (isInputRange!Range)
114 {
115     return _Cache!(Range, false)(range);
116 }
117 
118 /// ditto
119 auto cacheBidirectional(Range)(Range range)
120 if (isBidirectionalRange!Range)
121 {
122     return _Cache!(Range, true)(range);
123 }
124 
125 ///
126 @safe unittest
127 {
128     import std.algorithm.comparison : equal;
129     import std.range, std.stdio;
130     import std.typecons : tuple;
131 
132     ulong counter = 0;
133     double fun(int x)
134     {
135         ++counter;
136         // http://en.wikipedia.org/wiki/Quartic_function
137         return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;
138     }
139     // Without cache, with array (greedy)
140     auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
141                              .filter!(a => a[1] < 0)()
142                              .map!(a => a[0])()
143                              .array();
144 
145     // the values of x that have a negative y are:
146     assert(equal(result1, [-3, -2, 2]));
147 
148     // Check how many times fun was evaluated.
149     // As many times as the number of items in both source and result.
150     assert(counter == iota(-4, 5).length + result1.length);
151 
152     counter = 0;
153     // Without array, with cache (lazy)
154     auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
155                              .cache()
156                              .filter!(a => a[1] < 0)()
157                              .map!(a => a[0])();
158 
159     // the values of x that have a negative y are:
160     assert(equal(result2, [-3, -2, 2]));
161 
162     // Check how many times fun was evaluated.
163     // Only as many times as the number of items in source.
164     assert(counter == iota(-4, 5).length);
165 }
166 
167 // https://issues.dlang.org/show_bug.cgi?id=15891
168 @safe pure unittest
169 {
170     assert([1].map!(x=>[x].map!(y=>y)).cache.front.front == 1);
171 }
172 
173 /++
174 Tip: `cache` is eager when evaluating elements. If calling front on the
175 underlying range has a side effect, it will be observable before calling
176 front on the actual cached range.
177 
178 Furthermore, care should be taken composing `cache` with $(REF take, std,range).
179 By placing `take` before `cache`, then `cache` will be "aware"
180 of when the range ends, and correctly stop caching elements when needed.
181 If calling front has no side effect though, placing `take` after `cache`
182 may yield a faster range.
183 
184 Either way, the resulting ranges will be equivalent, but maybe not at the
185 same cost or side effects.
186 +/
187 @safe unittest
188 {
189     import std.algorithm.comparison : equal;
190     import std.range;
191     int i = 0;
192 
193     auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);
194     auto r1 = r.take(3).cache();
195     auto r2 = r.cache().take(3);
196 
197     assert(equal(r1, [0, 1, 2]));
198     assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared.
199 
200     assert(equal(r2, [0, 1, 2]));
201     assert(i == 3); //cache has accessed 3. It is still stored internally by cache.
202 }
203 
204 @safe unittest
205 {
206     import std.algorithm.comparison : equal;
207     import std.range;
208     auto a = [1, 2, 3, 4];
209     assert(equal(a.map!(a => (a - 1) * a)().cache(),                      [ 0, 2, 6, 12]));
210     assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2,  0]));
211     auto r1 = [1, 2, 3, 4].cache()             [1 .. $];
212     auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $];
213     assert(equal(r1, [2, 3, 4]));
214     assert(equal(r2, [2, 3, 4]));
215 }
216 
217 @safe unittest
218 {
219     import std.algorithm.comparison : equal;
220 
221     //immutable test
222     static struct S
223     {
224         int i;
225         this(int i)
226         {
227             //this.i = i;
228         }
229     }
230     immutable(S)[] s = [S(1), S(2), S(3)];
231     assert(equal(s.cache(),              s));
232     assert(equal(s.cacheBidirectional(), s));
233 }
234 
235 @safe pure nothrow unittest
236 {
237     import std.algorithm.comparison : equal;
238 
239     //safety etc
240     auto a = [1, 2, 3, 4];
241     assert(equal(a.cache(),              a));
242     assert(equal(a.cacheBidirectional(), a));
243 }
244 
245 @safe unittest
246 {
247     char[][] stringbufs = ["hello".dup, "world".dup];
248     auto strings = stringbufs.map!((a)=>a.idup)().cache();
249     assert(strings.front is strings.front);
250 }
251 
252 @safe unittest
253 {
254     import std.range : cycle;
255     import std.algorithm.comparison : equal;
256 
257     auto c = [1, 2, 3].cycle().cache();
258     c = c[1 .. $];
259     auto d = c[0 .. 1];
260     assert(d.equal([2]));
261 }
262 
263 @safe unittest
264 {
265     static struct Range
266     {
267         bool initialized = false;
268         bool front() @property {return initialized = true;}
269         void popFront() {initialized = false;}
270         enum empty = false;
271     }
272     auto r = Range().cache();
273     assert(r.source.initialized == true);
274 }
275 
276 private struct _Cache(R, bool bidir)
277 {
278     import core.exception : RangeError;
279 
280     private
281     {
282         import std.algorithm.internal : algoFormat;
283         import std.meta : AliasSeq;
284 
285         alias E  = ElementType!R;
286         alias UE = Unqual!E;
287 
288         R source;
289 
290         static if (bidir) alias CacheTypes = AliasSeq!(UE, UE);
291         else              alias CacheTypes = AliasSeq!UE;
292         CacheTypes caches;
293 
294         static assert(isAssignable!(UE, E) && is(UE : E),
295             algoFormat(
296                 "Cannot instantiate range with %s because %s elements are not assignable to %s.",
297                 R.stringof,
298                 E.stringof,
299                 UE.stringof
300             )
301         );
302     }
303 
304     this(R range)
305     {
306         source = range;
307         if (!range.empty)
308         {
309             caches[0] = source.front;
310             static if (bidir)
311                 caches[1] = source.back;
312         }
313         else
314         {
315             // needed, because the compiler cannot deduce, that 'caches' is initialized
316             // see https://issues.dlang.org/show_bug.cgi?id=15891
317             caches[0] = UE.init;
318             static if (bidir)
319                 caches[1] = UE.init;
320         }
321     }
322 
323     static if (isInfinite!R)
324         enum empty = false;
325     else
326         bool empty() @property
327         {
328             return source.empty;
329         }
330 
331     mixin ImplementLength!source;
332 
333     E front() @property
334     {
335         version (assert) if (empty) throw new RangeError();
336         return caches[0];
337     }
338     static if (bidir) E back() @property
339     {
340         version (assert) if (empty) throw new RangeError();
341         return caches[1];
342     }
343 
344     void popFront()
345     {
346         version (assert) if (empty) throw new RangeError();
347         source.popFront();
348         if (!source.empty)
349             caches[0] = source.front;
350         else
351         {
352             // see https://issues.dlang.org/show_bug.cgi?id=15891
353             caches[0] = UE.init;
354             static if (bidir)
355                 caches[1] = UE.init;
356         }
357     }
358     static if (bidir) void popBack()
359     {
360         version (assert) if (empty) throw new RangeError();
361         source.popBack();
362         if (!source.empty)
363             caches[1] = source.back;
364         else
365         {
366             // see https://issues.dlang.org/show_bug.cgi?id=15891
367             caches[0] = UE.init;
368             caches[1] = UE.init;
369         }
370     }
371 
372     static if (isForwardRange!R)
373     {
374         private this(R source, ref CacheTypes caches)
375         {
376             this.source = source;
377             this.caches = caches;
378         }
379         typeof(this) save() @property
380         {
381             return typeof(this)(source.save, caches);
382         }
383     }
384 
385     static if (hasSlicing!R)
386     {
387         enum hasEndSlicing = is(typeof(source[size_t.max .. $]));
388 
389         static if (hasEndSlicing)
390         {
391             private static struct DollarToken{}
392             enum opDollar = DollarToken.init;
393 
394             auto opSlice(size_t low, DollarToken)
395             {
396                 return typeof(this)(source[low .. $]);
397             }
398         }
399 
400         static if (!isInfinite!R)
401         {
402             typeof(this) opSlice(size_t low, size_t high)
403             {
404                 return typeof(this)(source[low .. high]);
405             }
406         }
407         else static if (hasEndSlicing)
408         {
409             auto opSlice(size_t low, size_t high)
410             in
411             {
412                 assert(low <= high, "Bounds error when slicing cache.");
413             }
414             do
415             {
416                 import std.range : takeExactly;
417                 return this[low .. $].takeExactly(high - low);
418             }
419         }
420     }
421 }
422 
423 /**
424 Implements the homonym function (also known as `transform`) present
425 in many languages of functional flavor. The call `map!(fun)(range)`
426 returns a range of which elements are obtained by applying `fun(a)`
427 left to right for all elements `a` in `range`. The original ranges are
428 not changed. Evaluation is done lazily.
429 
430 Params:
431     fun = one or more transformation functions
432 
433 See_Also:
434     $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
435 */
436 template map(fun...)
437 if (fun.length >= 1)
438 {
439     /**
440     Params:
441         r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
442     Returns:
443         A range with each fun applied to all the elements. If there is more than one
444         fun, the element type will be `Tuple` containing one element for each fun.
445      */
446     auto map(Range)(Range r)
447     if (isInputRange!(Unqual!Range))
448     {
449         import std.meta : AliasSeq, staticMap;
450 
451         alias RE = ElementType!(Range);
452         static if (fun.length > 1)
453         {
454             import std.functional : adjoin;
455             import std.meta : staticIndexOf;
456 
457             alias _funs = staticMap!(unaryFun, fun);
458             alias _fun = adjoin!_funs;
459 
460             // Once https://issues.dlang.org/show_bug.cgi?id=5710 is fixed
461             // accross all compilers (as of 2020-04, it wasn't fixed in LDC and GDC),
462             // this validation loop can be moved into a template.
463             foreach (f; _funs)
464             {
465                 static assert(!is(typeof(f(RE.init)) == void),
466                     "Mapping function(s) must not return void: " ~ _funs.stringof);
467             }
468         }
469         else
470         {
471             alias _fun = unaryFun!fun;
472             alias _funs = AliasSeq!(_fun);
473 
474             // Do the validation separately for single parameters due to
475             // https://issues.dlang.org/show_bug.cgi?id=15777.
476             static assert(!is(typeof(_fun(RE.init)) == void),
477                 "Mapping function(s) must not return void: " ~ _funs.stringof);
478         }
479 
480         return MapResult!(_fun, Range)(r);
481     }
482 }
483 
484 ///
485 @safe @nogc unittest
486 {
487     import std.algorithm.comparison : equal;
488     import std.range : chain, only;
489     auto squares =
490         chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);
491     assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
492 }
493 
494 /**
495 Multiple functions can be passed to `map`. In that case, the
496 element type of `map` is a tuple containing one element for each
497 function.
498 */
499 @safe unittest
500 {
501     auto sums = [2, 4, 6, 8];
502     auto products = [1, 4, 9, 16];
503 
504     size_t i = 0;
505     foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
506     {
507         assert(result[0] == sums[i]);
508         assert(result[1] == products[i]);
509         ++i;
510     }
511 }
512 
513 /**
514 You may alias `map` with some function(s) to a symbol and use
515 it separately:
516 */
517 @safe unittest
518 {
519     import std.algorithm.comparison : equal;
520     import std.conv : to;
521 
522     alias stringize = map!(to!string);
523     assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
524 }
525 
526 // Verify workaround for https://issues.dlang.org/show_bug.cgi?id=15777
527 @safe unittest
528 {
529     import std.algorithm.mutation, std.string;
530     auto foo(string[] args)
531     {
532         return args.map!strip;
533     }
534 }
535 
536 private struct MapResult(alias fun, Range)
537 {
538     alias R = Unqual!Range;
539     R _input;
540 
541     static if (isBidirectionalRange!R)
542     {
543         @property auto ref back()()
544         {
545             assert(!empty, "Attempting to fetch the back of an empty map.");
546             return fun(_input.back);
547         }
548 
549         void popBack()()
550         {
551             assert(!empty, "Attempting to popBack an empty map.");
552             _input.popBack();
553         }
554     }
555 
556     this(R input)
557     {
558         _input = input;
559     }
560 
561     static if (isInfinite!R)
562     {
563         // Propagate infinite-ness.
564         enum bool empty = false;
565     }
566     else
567     {
568         @property bool empty()
569         {
570             return _input.empty;
571         }
572     }
573 
574     void popFront()
575     {
576         assert(!empty, "Attempting to popFront an empty map.");
577         _input.popFront();
578     }
579 
580     @property auto ref front()
581     {
582         assert(!empty, "Attempting to fetch the front of an empty map.");
583         return fun(_input.front);
584     }
585 
586     static if (isRandomAccessRange!R)
587     {
588         static if (is(typeof(Range.init[ulong.max])))
589             private alias opIndex_t = ulong;
590         else
591             private alias opIndex_t = uint;
592 
593         auto ref opIndex(opIndex_t index)
594         {
595             return fun(_input[index]);
596         }
597     }
598 
599     mixin ImplementLength!_input;
600 
601     static if (hasSlicing!R)
602     {
603         static if (is(typeof(_input[ulong.max .. ulong.max])))
604             private alias opSlice_t = ulong;
605         else
606             private alias opSlice_t = uint;
607 
608         static if (hasLength!R)
609         {
610             auto opSlice(opSlice_t low, opSlice_t high)
611             {
612                 return typeof(this)(_input[low .. high]);
613             }
614         }
615         else static if (is(typeof(_input[opSlice_t.max .. $])))
616         {
617             struct DollarToken{}
618             enum opDollar = DollarToken.init;
619             auto opSlice(opSlice_t low, DollarToken)
620             {
621                 return typeof(this)(_input[low .. $]);
622             }
623 
624             auto opSlice(opSlice_t low, opSlice_t high)
625             {
626                 import std.range : takeExactly;
627                 return this[low .. $].takeExactly(high - low);
628             }
629         }
630     }
631 
632     static if (isForwardRange!R)
633     {
634         @property auto save()
635         {
636             return typeof(this)(_input.save);
637         }
638     }
639 }
640 
641 @safe unittest
642 {
643     import std.algorithm.comparison : equal;
644     import std.conv : to;
645     import std.functional : adjoin;
646 
647     alias stringize = map!(to!string);
648     assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
649 
650     uint counter;
651     alias count = map!((a) { return counter++; });
652     assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ]));
653 
654     counter = 0;
655     adjoin!((a) { return counter++; }, (a) { return counter++; })(1);
656     alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; });
657     //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ]));
658 }
659 
660 @safe unittest
661 {
662     import std.algorithm.comparison : equal;
663     import std.ascii : toUpper;
664     import std.internal.test.dummyrange;
665     import std.range;
666     import std.typecons : tuple;
667     import std.random : uniform, Random = Xorshift;
668 
669     int[] arr1 = [ 1, 2, 3, 4 ];
670     const int[] arr1Const = arr1;
671     int[] arr2 = [ 5, 6 ];
672     auto squares = map!("a * a")(arr1Const);
673     assert(squares[$ - 1] == 16);
674     assert(equal(squares, [ 1, 4, 9, 16 ][]));
675     assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
676 
677     // Test the caching stuff.
678     assert(squares.back == 16);
679     auto squares2 = squares.save;
680     assert(squares2.back == 16);
681 
682     assert(squares2.front == 1);
683     squares2.popFront();
684     assert(squares2.front == 4);
685     squares2.popBack();
686     assert(squares2.front == 4);
687     assert(squares2.back == 9);
688 
689     assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
690 
691     uint i;
692     foreach (e; map!("a", "a * a")(arr1))
693     {
694         assert(e[0] == ++i);
695         assert(e[1] == i * i);
696     }
697 
698     // Test length.
699     assert(squares.length == 4);
700     assert(map!"a * a"(chain(arr1, arr2)).length == 6);
701 
702     // Test indexing.
703     assert(squares[0] == 1);
704     assert(squares[1] == 4);
705     assert(squares[2] == 9);
706     assert(squares[3] == 16);
707 
708     // Test slicing.
709     auto squareSlice = squares[1 .. squares.length - 1];
710     assert(equal(squareSlice, [4, 9][]));
711     assert(squareSlice.back == 9);
712     assert(squareSlice[1] == 9);
713 
714     // Test on a forward range to make sure it compiles when all the fancy
715     // stuff is disabled.
716     auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1));
717     assert(fibsSquares.front == 1);
718     fibsSquares.popFront();
719     fibsSquares.popFront();
720     assert(fibsSquares.front == 4);
721     fibsSquares.popFront();
722     assert(fibsSquares.front == 9);
723 
724     auto repeatMap = map!"a"(repeat(1));
725     auto gen = Random(123_456_789);
726     auto index = uniform(0, 1024, gen);
727     static assert(isInfinite!(typeof(repeatMap)));
728     assert(repeatMap[index] == 1);
729 
730     auto intRange = map!"a"([1,2,3]);
731     static assert(isRandomAccessRange!(typeof(intRange)));
732     assert(equal(intRange, [1, 2, 3]));
733 
734     foreach (DummyType; AllDummyRanges)
735     {
736         DummyType d;
737         auto m = map!"a * a"(d);
738 
739         static assert(propagatesRangeType!(typeof(m), DummyType));
740         assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
741     }
742 
743     //Test string access
744     string  s1 = "hello world!";
745     dstring s2 = "日本語";
746     dstring s3 = "hello world!"d;
747     auto ms1 = map!(toUpper)(s1);
748     auto ms2 = map!(toUpper)(s2);
749     auto ms3 = map!(toUpper)(s3);
750     static assert(!is(ms1[0])); //narrow strings can't be indexed
751     assert(ms2[0] == '日');
752     assert(ms3[0] == 'H');
753     static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced
754     assert(equal(ms2[0 .. 2], "日本"w));
755     assert(equal(ms3[0 .. 2], "HE"));
756 
757     // https://issues.dlang.org/show_bug.cgi?id=5753
758     static void voidFun(int) {}
759     static int nonvoidFun(int) { return 0; }
760     static assert(!__traits(compiles, map!voidFun([1])));
761     static assert(!__traits(compiles, map!(voidFun, voidFun)([1])));
762     static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1])));
763     static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1])));
764     static assert(!__traits(compiles, map!(a => voidFun(a))([1])));
765 
766     // https://issues.dlang.org/show_bug.cgi?id=15480
767     auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]);
768     assert(dd[0] == tuple(1, 1));
769     assert(dd[1] == tuple(4, 8));
770     assert(dd[2] == tuple(9, 27));
771     assert(dd[3] == tuple(16, 64));
772     assert(dd.length == 4);
773 }
774 
775 // Verify fix for: https://issues.dlang.org/show_bug.cgi?id=16034
776 @safe unittest
777 {
778     struct One
779     {
780         int entry = 1;
781         @disable this(this);
782     }
783 
784     One[] ones = [One(), One()];
785 
786     import std.algorithm.comparison : equal;
787 
788     assert(ones.map!`a.entry + 1`.equal([2, 2]));
789 }
790 
791 
792 @safe unittest
793 {
794     import std.algorithm.comparison : equal;
795     import std.range;
796     auto LL = iota(1L, 4L);
797     auto m = map!"a*a"(LL);
798     assert(equal(m, [1L, 4L, 9L]));
799 }
800 
801 @safe unittest
802 {
803     import std.range : iota;
804 
805     // https://issues.dlang.org/show_bug.cgi?id=10130 - map of iota with const step.
806     const step = 2;
807     assert(map!(i => i)(iota(0, 10, step)).walkLength == 5);
808 
809     // Need these to all by const to repro the float case, due to the
810     // CommonType template used in the float specialization of iota.
811     const floatBegin = 0.0;
812     const floatEnd = 1.0;
813     const floatStep = 0.02;
814     assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50);
815 }
816 
817 @safe unittest
818 {
819     import std.algorithm.comparison : equal;
820     import std.range;
821     //slicing infinites
822     auto rr = iota(0, 5).cycle().map!"a * a"();
823     alias RR = typeof(rr);
824     static assert(hasSlicing!RR);
825     rr = rr[6 .. $]; //Advances 1 cycle and 1 unit
826     assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0]));
827 }
828 
829 @safe unittest
830 {
831     import std.range;
832     struct S {int* p;}
833     auto m = immutable(S).init.repeat().map!"a".save;
834     assert(m.front == immutable(S)(null));
835 }
836 
837 // Issue 20928
838 @safe unittest
839 {
840     struct Always3
841     {
842         enum empty = false;
843         auto save() { return this; }
844         long front() { return 3; }
845         void popFront() {}
846         long opIndex(ulong i) { return 3; }
847         long opIndex(ulong i) immutable { return 3; }
848     }
849 
850     import std.algorithm.iteration : map;
851     Always3.init.map!(e => e)[ulong.max];
852 }
853 
854 // each
855 /**
856 Eagerly iterates over `r` and calls `fun` with _each element.
857 
858 If no function to call is specified, `each` defaults to doing nothing but
859 consuming the entire range. `r.front` will be evaluated, but that can be avoided
860 by specifying a lambda with a `lazy` parameter.
861 
862 `each` also supports `opApply`-based types, so it works with e.g. $(REF
863 parallel, std,parallelism).
864 
865 Normally the entire range is iterated. If partial iteration (early stopping) is
866 desired, `fun` needs to return a value of type $(REF Flag,
867 std,typecons)`!"each"` (`Yes.each` to continue iteration, or `No.each` to stop
868 iteration).
869 
870 Params:
871     fun = function to apply to _each element of the range
872     r = range or iterable over which `each` iterates
873 
874 Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early
875 stopping.
876 
877 See_Also: $(REF tee, std,range)
878  */
879 template each(alias fun = "a")
880 {
881     import std.meta : AliasSeq;
882     import std.traits : Parameters;
883     import std.typecons : Flag, Yes, No;
884 
885 private:
886     alias BinaryArgs = AliasSeq!(fun, "i", "a");
887 
888     enum isRangeUnaryIterable(R) =
889         is(typeof(unaryFun!fun(R.init.front)));
890 
891     enum isRangeBinaryIterable(R) =
892         is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
893 
894     enum isRangeIterable(R) =
895         isInputRange!R &&
896         (isRangeUnaryIterable!R || isRangeBinaryIterable!R);
897 
898     enum isForeachUnaryIterable(R) =
899         is(typeof((R r) {
900             foreach (ref a; r)
901                 cast(void) unaryFun!fun(a);
902         }));
903 
904     enum isForeachUnaryWithIndexIterable(R) =
905         is(typeof((R r) {
906             foreach (i, ref a; r)
907                 cast(void) binaryFun!BinaryArgs(i, a);
908         }));
909 
910     enum isForeachBinaryIterable(R) =
911         is(typeof((R r) {
912             foreach (ref a, ref b; r)
913                 cast(void) binaryFun!fun(a, b);
914         }));
915 
916     enum isForeachIterable(R) =
917         (!isForwardRange!R || isDynamicArray!R) &&
918         (isForeachUnaryIterable!R || isForeachBinaryIterable!R ||
919          isForeachUnaryWithIndexIterable!R);
920 
921 public:
922     /**
923     Params:
924         r = range or iterable over which each iterates
925      */
926     Flag!"each" each(Range)(Range r)
927     if (!isForeachIterable!Range && (
928         isRangeIterable!Range ||
929         __traits(compiles, typeof(r.front).length)))
930     {
931         static if (isRangeIterable!Range)
932         {
933             debug(each) pragma(msg, "Using while for ", Range.stringof);
934             static if (isRangeUnaryIterable!Range)
935             {
936                 while (!r.empty)
937                 {
938                     static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each"))
939                     {
940                         cast(void) unaryFun!fun(r.front);
941                     }
942                     else
943                     {
944                         if (unaryFun!fun(r.front) == No.each) return No.each;
945                     }
946 
947                     r.popFront();
948                 }
949             }
950             else // if (isRangeBinaryIterable!Range)
951             {
952                 size_t i = 0;
953                 while (!r.empty)
954                 {
955                     static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) == Flag!"each"))
956                     {
957                         cast(void) binaryFun!BinaryArgs(i, r.front);
958                     }
959                     else
960                     {
961                         if (binaryFun!BinaryArgs(i, r.front) == No.each) return No.each;
962                     }
963                     r.popFront();
964                     i++;
965                 }
966             }
967         }
968         else
969         {
970             // range interface with >2 parameters.
971             for (auto range = r; !range.empty; range.popFront())
972             {
973                 static if (!is(typeof(fun(r.front.expand)) == Flag!"each"))
974                 {
975                     cast(void) fun(range.front.expand);
976                 }
977                 else
978                 {
979                     if (fun(range.front.expand)) return No.each;
980                 }
981             }
982         }
983         return Yes.each;
984     }
985 
986     /// ditto
987     Flag!"each" each(Iterable)(auto ref Iterable r)
988     if (isForeachIterable!Iterable ||
989         __traits(compiles, Parameters!(Parameters!(r.opApply))))
990     {
991         static if (isForeachIterable!Iterable)
992         {
993             static if (isForeachUnaryIterable!Iterable)
994             {
995                 debug(each) pragma(msg, "Using foreach UNARY for ", Iterable.stringof);
996                 {
997                     foreach (ref e; r)
998                     {
999                         static if (!is(typeof(unaryFun!fun(e)) == Flag!"each"))
1000                         {
1001                             cast(void) unaryFun!fun(e);
1002                         }
1003                         else
1004                         {
1005                             if (unaryFun!fun(e) == No.each) return No.each;
1006                         }
1007                     }
1008                 }
1009             }
1010             else static if (isForeachBinaryIterable!Iterable)
1011             {
1012                 debug(each) pragma(msg, "Using foreach BINARY for ", Iterable.stringof);
1013                 foreach (ref a, ref b; r)
1014                 {
1015                     static if (!is(typeof(binaryFun!fun(a, b)) == Flag!"each"))
1016                     {
1017                         cast(void) binaryFun!fun(a, b);
1018                     }
1019                     else
1020                     {
1021                         if (binaryFun!fun(a, b) == No.each) return No.each;
1022                     }
1023                 }
1024             }
1025             else static if (isForeachUnaryWithIndexIterable!Iterable)
1026             {
1027                 debug(each) pragma(msg, "Using foreach INDEX for ", Iterable.stringof);
1028                 foreach (i, ref e; r)
1029                 {
1030                     static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each"))
1031                     {
1032                         cast(void) binaryFun!BinaryArgs(i, e);
1033                     }
1034                     else
1035                     {
1036                         if (binaryFun!BinaryArgs(i, e) == No.each) return No.each;
1037                     }
1038                 }
1039             }
1040             else
1041             {
1042                 static assert(0, "Invalid foreach iteratable type " ~ Iterable.stringof ~ " met.");
1043             }
1044             return Yes.each;
1045         }
1046         else
1047         {
1048             // opApply with >2 parameters. count the delegate args.
1049             // only works if it is not templated (otherwise we cannot count the args)
1050             auto result = Yes.each;
1051             auto dg(Parameters!(Parameters!(r.opApply)) params)
1052             {
1053                 static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each"))
1054                 {
1055                     fun(params);
1056                     return 0; // tells opApply to continue iteration
1057                 }
1058                 else
1059                 {
1060                     result = fun(params);
1061                     return result == Yes.each ? 0 : -1;
1062                 }
1063             }
1064             r.opApply(&dg);
1065             return result;
1066         }
1067     }
1068 }
1069 
1070 ///
1071 @safe unittest
1072 {
1073     import std.range : iota;
1074     import std.typecons : No;
1075 
1076     int[] arr;
1077     iota(5).each!(n => arr ~= n);
1078     assert(arr == [0, 1, 2, 3, 4]);
1079 
1080     // stop iterating early
1081     iota(5).each!((n) { arr ~= n; return No.each; });
1082     assert(arr == [0, 1, 2, 3, 4, 0]);
1083 
1084     // If the range supports it, the value can be mutated in place
1085     arr.each!((ref n) => n++);
1086     assert(arr == [1, 2, 3, 4, 5, 1]);
1087 
1088     arr.each!"a++";
1089     assert(arr == [2, 3, 4, 5, 6, 2]);
1090 
1091     auto m = arr.map!(n => n);
1092     // by-ref lambdas are not allowed for non-ref ranges
1093     static assert(!__traits(compiles, m.each!((ref n) => n++)));
1094 
1095     // The default predicate consumes the range
1096     (&m).each();
1097     assert(m.empty);
1098 }
1099 
1100 /// `each` can pass an index variable for iterable objects which support this
1101 @safe unittest
1102 {
1103     auto arr = new size_t[4];
1104 
1105     arr.each!"a=i"();
1106     assert(arr == [0, 1, 2, 3]);
1107 
1108     arr.each!((i, ref e) => e = i * 2);
1109     assert(arr == [0, 2, 4, 6]);
1110 }
1111 
1112 /// opApply iterators work as well
1113 @system unittest
1114 {
1115     static class S
1116     {
1117         int x;
1118         int opApply(scope int delegate(ref int _x) dg) { return dg(x); }
1119     }
1120 
1121     auto s = new S;
1122     s.each!"a++";
1123     assert(s.x == 1);
1124 }
1125 
1126 // binary foreach with two ref args
1127 @system unittest
1128 {
1129     import std.range : lockstep;
1130 
1131     auto a = [ 1, 2, 3 ];
1132     auto b = [ 2, 3, 4 ];
1133 
1134     a.lockstep(b).each!((ref x, ref y) { ++x; ++y; });
1135 
1136     assert(a == [ 2, 3, 4 ]);
1137     assert(b == [ 3, 4, 5 ]);
1138 }
1139 
1140 // https://issues.dlang.org/show_bug.cgi?id=15358
1141 // application of `each` with >2 args (opApply)
1142 @system unittest
1143 {
1144     import std.range : lockstep;
1145     auto a = [0,1,2];
1146     auto b = [3,4,5];
1147     auto c = [6,7,8];
1148 
1149     lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; });
1150 
1151     assert(a == [1,2,3]);
1152     assert(b == [4,5,6]);
1153     assert(c == [7,8,9]);
1154 }
1155 
1156 // https://issues.dlang.org/show_bug.cgi?id=15358
1157 // application of `each` with >2 args (range interface)
1158 @safe unittest
1159 {
1160     import std.range : zip;
1161     auto a = [0,1,2];
1162     auto b = [3,4,5];
1163     auto c = [6,7,8];
1164 
1165     int[] res;
1166 
1167     zip(a, b, c).each!((x, y, z) { res ~= x + y + z; });
1168 
1169     assert(res == [9, 12, 15]);
1170 }
1171 
1172 // https://issues.dlang.org/show_bug.cgi?id=16255
1173 // `each` on opApply doesn't support ref
1174 @safe unittest
1175 {
1176     int[] dynamicArray = [1, 2, 3, 4, 5];
1177     int[5] staticArray = [1, 2, 3, 4, 5];
1178 
1179     dynamicArray.each!((ref x) => x++);
1180     assert(dynamicArray == [2, 3, 4, 5, 6]);
1181 
1182     staticArray.each!((ref x) => x++);
1183     assert(staticArray == [2, 3, 4, 5, 6]);
1184 
1185     staticArray[].each!((ref x) => x++);
1186     assert(staticArray == [3, 4, 5, 6, 7]);
1187 }
1188 
1189 // https://issues.dlang.org/show_bug.cgi?id=16255
1190 // `each` on opApply doesn't support ref
1191 @system unittest
1192 {
1193     struct S
1194     {
1195        int x;
1196        int opApply(int delegate(ref int _x) dg) { return dg(x); }
1197     }
1198 
1199     S s;
1200     foreach (ref a; s) ++a;
1201     assert(s.x == 1);
1202     s.each!"++a";
1203     assert(s.x == 2);
1204 }
1205 
1206 // https://issues.dlang.org/show_bug.cgi?id=15357
1207 // `each` should behave similar to foreach
1208 @safe unittest
1209 {
1210     import std.range : iota;
1211 
1212     auto arr = [1, 2, 3, 4];
1213 
1214     // 1 ref parameter
1215     arr.each!((ref e) => e = 0);
1216     assert(arr.sum == 0);
1217 
1218     // 1 ref parameter and index
1219     arr.each!((i, ref e) => e = cast(int) i);
1220     assert(arr.sum == 4.iota.sum);
1221 }
1222 
1223 // https://issues.dlang.org/show_bug.cgi?id=15357
1224 // `each` should behave similar to foreach
1225 @system unittest
1226 {
1227     import std.range : iota, lockstep;
1228 
1229     // 2 ref parameters and index
1230     auto arrA = [1, 2, 3, 4];
1231     auto arrB = [5, 6, 7, 8];
1232     lockstep(arrA, arrB).each!((ref a, ref b) {
1233         a = 0;
1234         b = 1;
1235     });
1236     assert(arrA.sum == 0);
1237     assert(arrB.sum == 4);
1238 
1239     // 3 ref parameters
1240     auto arrC = [3, 3, 3, 3];
1241     lockstep(arrA, arrB, arrC).each!((ref a, ref b, ref c) {
1242         a = 1;
1243         b = 2;
1244         c = 3;
1245     });
1246     assert(arrA.sum == 4);
1247     assert(arrB.sum == 8);
1248     assert(arrC.sum == 12);
1249 }
1250 
1251 // https://issues.dlang.org/show_bug.cgi?id=15357
1252 // `each` should behave similar to foreach
1253 @system unittest
1254 {
1255     import std.range : lockstep;
1256     import std.typecons : Tuple;
1257 
1258     auto a = "abc";
1259     auto b = "def";
1260 
1261     // print each character with an index
1262     {
1263         alias Element = Tuple!(size_t, "index", dchar, "value");
1264         Element[] rForeach, rEach;
1265         foreach (i, c ; a) rForeach ~= Element(i, c);
1266         a.each!((i, c) => rEach ~= Element(i, c));
1267         assert(rForeach == rEach);
1268         assert(rForeach == [Element(0, 'a'), Element(1, 'b'), Element(2, 'c')]);
1269     }
1270 
1271     // print pairs of characters
1272     {
1273         alias Element = Tuple!(dchar, "a", dchar, "b");
1274         Element[] rForeach, rEach;
1275         foreach (c1, c2 ; a.lockstep(b)) rForeach ~= Element(c1, c2);
1276         a.lockstep(b).each!((c1, c2) => rEach ~= Element(c1, c2));
1277         assert(rForeach == rEach);
1278         assert(rForeach == [Element('a', 'd'), Element('b', 'e'), Element('c', 'f')]);
1279     }
1280 }
1281 
1282 // filter
1283 /**
1284 `filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for
1285 which `predicate(x)` returns `true`.
1286 
1287 The predicate is passed to $(REF unaryFun, std,functional), and can be either a string, or
1288 any callable that can be executed via `pred(element)`.
1289 
1290 Params:
1291     predicate = Function to apply to each element of range
1292 
1293 Returns:
1294     An input range that contains the filtered elements. If `range` is at least a forward range, the return value of `filter`
1295     will also be a forward range.
1296 
1297 See_Also:
1298     $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)),
1299     $(REF filterBidirectional, std,algorithm,iteration)
1300  */
1301 template filter(alias predicate)
1302 if (is(typeof(unaryFun!predicate)))
1303 {
1304     /**
1305     Params:
1306         range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1307         of elements
1308     Returns:
1309         A range containing only elements `x` in `range` for
1310         which `predicate(x)` returns `true`.
1311      */
1312     auto filter(Range)(Range range)
1313     if (isInputRange!(Unqual!Range))
1314     {
1315         return FilterResult!(unaryFun!predicate, Range)(range);
1316     }
1317 }
1318 
1319 ///
1320 @safe unittest
1321 {
1322     import std.algorithm.comparison : equal;
1323     import std.math.operations : isClose;
1324     import std.range;
1325 
1326     int[] arr = [ 1, 2, 3, 4, 5 ];
1327 
1328     // Filter below 3
1329     auto small = filter!(a => a < 3)(arr);
1330     assert(equal(small, [ 1, 2 ]));
1331 
1332     // Filter again, but with Uniform Function Call Syntax (UFCS)
1333     auto sum = arr.filter!(a => a < 3);
1334     assert(equal(sum, [ 1, 2 ]));
1335 
1336     // In combination with chain() to span multiple ranges
1337     int[] a = [ 3, -2, 400 ];
1338     int[] b = [ 100, -101, 102 ];
1339     auto r = chain(a, b).filter!(a => a > 0);
1340     assert(equal(r, [ 3, 400, 100, 102 ]));
1341 
1342     // Mixing convertible types is fair game, too
1343     double[] c = [ 2.5, 3.0 ];
1344     auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
1345     assert(isClose(r1, [ 2.5 ]));
1346 }
1347 
1348 private struct FilterResult(alias pred, Range)
1349 {
1350     alias R = Unqual!Range;
1351     R _input;
1352     private bool _primed;
1353 
1354     private void prime()
1355     {
1356         if (_primed) return;
1357         while (!_input.empty && !pred(_input.front))
1358         {
1359             _input.popFront();
1360         }
1361         _primed = true;
1362     }
1363 
1364     this(R r)
1365     {
1366         _input = r;
1367     }
1368 
1369     private this(R r, bool primed)
1370     {
1371         _input = r;
1372         _primed = primed;
1373     }
1374 
1375     auto opSlice() { return this; }
1376 
1377     static if (isInfinite!Range)
1378     {
1379         enum bool empty = false;
1380     }
1381     else
1382     {
1383         @property bool empty() { prime; return _input.empty; }
1384     }
1385 
1386     void popFront()
1387     {
1388         prime;
1389         do
1390         {
1391             _input.popFront();
1392         } while (!_input.empty && !pred(_input.front));
1393     }
1394 
1395     @property auto ref front()
1396     {
1397         prime;
1398         assert(!empty, "Attempting to fetch the front of an empty filter.");
1399         return _input.front;
1400     }
1401 
1402     static if (isForwardRange!R)
1403     {
1404         @property auto save()
1405         {
1406             return typeof(this)(_input.save, _primed);
1407         }
1408     }
1409 }
1410 
1411 @safe unittest
1412 {
1413     import std.algorithm.comparison : equal;
1414     import std.internal.test.dummyrange;
1415     import std.range;
1416 
1417     auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0);
1418     static assert(isInfinite!(typeof(shouldNotLoop4ever)));
1419     assert(!shouldNotLoop4ever.empty);
1420 
1421     int[] a = [ 3, 4, 2 ];
1422     auto r = filter!("a > 3")(a);
1423     static assert(isForwardRange!(typeof(r)));
1424     assert(equal(r, [ 4 ][]));
1425 
1426     a = [ 1, 22, 3, 42, 5 ];
1427     auto under10 = filter!("a < 10")(a);
1428     assert(equal(under10, [1, 3, 5][]));
1429     static assert(isForwardRange!(typeof(under10)));
1430     under10.front = 4;
1431     assert(equal(under10, [4, 3, 5][]));
1432     under10.front = 40;
1433     assert(equal(under10, [40, 3, 5][]));
1434     under10.front = 1;
1435 
1436     auto infinite = filter!"a > 2"(repeat(3));
1437     static assert(isInfinite!(typeof(infinite)));
1438     static assert(isForwardRange!(typeof(infinite)));
1439     assert(infinite.front == 3);
1440 
1441     foreach (DummyType; AllDummyRanges)
1442     {
1443         DummyType d;
1444         auto f = filter!"a & 1"(d);
1445         assert(equal(f, [1,3,5,7,9]));
1446 
1447         static if (isForwardRange!DummyType)
1448         {
1449             static assert(isForwardRange!(typeof(f)));
1450         }
1451     }
1452 
1453     // With delegates
1454     int x = 10;
1455     int overX(int a) { return a > x; }
1456     typeof(filter!overX(a)) getFilter()
1457     {
1458         return filter!overX(a);
1459     }
1460     auto r1 = getFilter();
1461     assert(equal(r1, [22, 42]));
1462 
1463     // With chain
1464     auto nums = [0,1,2,3,4];
1465     assert(equal(filter!overX(chain(a, nums)), [22, 42]));
1466 
1467     // With copying of inner struct Filter to Map
1468     auto arr = [1,2,3,4,5];
1469     auto m = map!"a + 1"(filter!"a < 4"(arr));
1470     assert(equal(m, [2, 3, 4]));
1471 }
1472 
1473 @safe unittest
1474 {
1475     import std.algorithm.comparison : equal;
1476 
1477     int[] a = [ 3, 4 ];
1478     const aConst = a;
1479     auto r = filter!("a > 3")(aConst);
1480     assert(equal(r, [ 4 ][]));
1481 
1482     a = [ 1, 22, 3, 42, 5 ];
1483     auto under10 = filter!("a < 10")(a);
1484     assert(equal(under10, [1, 3, 5][]));
1485     assert(equal(under10.save, [1, 3, 5][]));
1486     assert(equal(under10.save, under10));
1487 }
1488 
1489 @safe unittest
1490 {
1491     import std.algorithm.comparison : equal;
1492     import std.functional : compose, pipe;
1493 
1494     assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]),
1495                     [2,6,10]));
1496     assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]),
1497             [2,6,10]));
1498 }
1499 
1500 @safe unittest
1501 {
1502     import std.algorithm.comparison : equal;
1503 
1504     int x = 10;
1505     int underX(int a) { return a < x; }
1506     const(int)[] list = [ 1, 2, 10, 11, 3, 4 ];
1507     assert(equal(filter!underX(list), [ 1, 2, 3, 4 ]));
1508 }
1509 
1510 // https://issues.dlang.org/show_bug.cgi?id=19823
1511 @safe unittest
1512 {
1513     import std.algorithm.comparison : equal;
1514     import std.range : dropOne;
1515 
1516     auto a = [1, 2, 3, 4];
1517     assert(a.filter!(a => a != 1).dropOne.equal([3, 4]));
1518     assert(a.filter!(a => a != 2).dropOne.equal([3, 4]));
1519     assert(a.filter!(a => a != 3).dropOne.equal([2, 4]));
1520     assert(a.filter!(a => a != 4).dropOne.equal([2, 3]));
1521     assert(a.filter!(a => a == 1).dropOne.empty);
1522     assert(a.filter!(a => a == 2).dropOne.empty);
1523     assert(a.filter!(a => a == 3).dropOne.empty);
1524     assert(a.filter!(a => a == 4).dropOne.empty);
1525 }
1526 
1527 /**
1528  * Similar to `filter`, except it defines a
1529  * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
1530  * There is a speed disadvantage - the constructor spends time
1531  * finding the last element in the range that satisfies the filtering
1532  * condition (in addition to finding the first one). The advantage is
1533  * that the filtered range can be spanned from both directions. Also,
1534  * $(REF retro, std,range) can be applied against the filtered range.
1535  *
1536  * The predicate is passed to $(REF unaryFun, std,functional), and can either
1537  * accept a string, or any callable that can be executed via `pred(element)`.
1538  *
1539  * Params:
1540  *     pred = Function to apply to each element of range
1541  */
1542 template filterBidirectional(alias pred)
1543 {
1544     /**
1545     Params:
1546         r = Bidirectional range of elements
1547     Returns:
1548         A range containing only the elements in `r` for which `pred` returns `true`.
1549      */
1550     auto filterBidirectional(Range)(Range r)
1551     if (isBidirectionalRange!(Unqual!Range))
1552     {
1553         return FilterBidiResult!(unaryFun!pred, Range)(r);
1554     }
1555 }
1556 
1557 ///
1558 @safe unittest
1559 {
1560     import std.algorithm.comparison : equal;
1561     import std.range;
1562 
1563     int[] arr = [ 1, 2, 3, 4, 5 ];
1564     auto small = filterBidirectional!("a < 3")(arr);
1565     static assert(isBidirectionalRange!(typeof(small)));
1566     assert(small.back == 2);
1567     assert(equal(small, [ 1, 2 ]));
1568     assert(equal(retro(small), [ 2, 1 ]));
1569     // In combination with chain() to span multiple ranges
1570     int[] a = [ 3, -2, 400 ];
1571     int[] b = [ 100, -101, 102 ];
1572     auto r = filterBidirectional!("a > 0")(chain(a, b));
1573     assert(r.back == 102);
1574 }
1575 
1576 private struct FilterBidiResult(alias pred, Range)
1577 {
1578     alias R = Unqual!Range;
1579     R _input;
1580 
1581     this(R r)
1582     {
1583         _input = r;
1584         while (!_input.empty && !pred(_input.front)) _input.popFront();
1585         while (!_input.empty && !pred(_input.back)) _input.popBack();
1586     }
1587 
1588     @property bool empty() { return _input.empty; }
1589 
1590     void popFront()
1591     {
1592         do
1593         {
1594             _input.popFront();
1595         } while (!_input.empty && !pred(_input.front));
1596     }
1597 
1598     @property auto ref front()
1599     {
1600         assert(!empty, "Attempting to fetch the front of an empty filterBidirectional.");
1601         return _input.front;
1602     }
1603 
1604     void popBack()
1605     {
1606         do
1607         {
1608             _input.popBack();
1609         } while (!_input.empty && !pred(_input.back));
1610     }
1611 
1612     @property auto ref back()
1613     {
1614         assert(!empty, "Attempting to fetch the back of an empty filterBidirectional.");
1615         return _input.back;
1616     }
1617 
1618     @property auto save()
1619     {
1620         return typeof(this)(_input.save);
1621     }
1622 }
1623 
1624 /**
1625 Groups consecutively equivalent elements into a single tuple of the element and
1626 the number of its repetitions.
1627 
1628 Similarly to `uniq`, `group` produces a range that iterates over unique
1629 consecutive elements of the given range. Each element of this range is a tuple
1630 of the element and the number of times it is repeated in the original range.
1631 Equivalence of elements is assessed by using the predicate `pred`, which
1632 defaults to `"a == b"`.  The predicate is passed to $(REF binaryFun, std,functional),
1633 and can either accept a string, or any callable that can be executed via
1634 `pred(element, element)`.
1635 
1636 Params:
1637     pred = Binary predicate for determining equivalence of two elements.
1638     R = The range type
1639     r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
1640         iterate over.
1641 
1642 Returns: A range of elements of type `Tuple!(ElementType!R, uint)`,
1643 representing each consecutively unique element and its respective number of
1644 occurrences in that run.  This will be an input range if `R` is an input
1645 range, and a forward range in all other cases.
1646 
1647 See_Also: $(LREF chunkBy), which chunks an input range into subranges
1648     of equivalent adjacent elements.
1649 */
1650 Group!(pred, Range) group(alias pred = "a == b", Range)(Range r)
1651 {
1652     return typeof(return)(r);
1653 }
1654 
1655 /// ditto
1656 struct Group(alias pred, R)
1657 if (isInputRange!R)
1658 {
1659     import std.typecons : Rebindable, tuple, Tuple;
1660 
1661     private alias comp = binaryFun!pred;
1662 
1663     private alias E = ElementType!R;
1664     static if ((is(E == class) || is(E == interface)) &&
1665                (is(E == const) || is(E == immutable)))
1666     {
1667         private alias MutableE = Rebindable!E;
1668     }
1669     else static if (is(E : Unqual!E))
1670     {
1671         private alias MutableE = Unqual!E;
1672     }
1673     else
1674     {
1675         private alias MutableE = E;
1676     }
1677 
1678     private R _input;
1679     private Tuple!(MutableE, uint) _current;
1680 
1681     ///
1682     this(R input)
1683     {
1684         _input = input;
1685         if (!_input.empty) popFront();
1686     }
1687 
1688     private this(R input, Tuple!(MutableE, uint) current)
1689     {
1690         _input = input;
1691         _current = current;
1692     }
1693 
1694     ///
1695     void popFront()
1696     {
1697         if (_input.empty)
1698         {
1699             _current[1] = 0;
1700         }
1701         else
1702         {
1703             _current = tuple(_input.front, 1u);
1704             _input.popFront();
1705             while (!_input.empty && comp(_current[0], _input.front))
1706             {
1707                 ++_current[1];
1708                 _input.popFront();
1709             }
1710         }
1711     }
1712 
1713     static if (isInfinite!R)
1714     {
1715         ///
1716         enum bool empty = false;  // Propagate infiniteness.
1717     }
1718     else
1719     {
1720         ///
1721         @property bool empty()
1722         {
1723             return _current[1] == 0;
1724         }
1725     }
1726 
1727     /// Returns: the front of the range
1728     @property auto ref front()
1729     {
1730         assert(!empty, "Attempting to fetch the front of an empty Group.");
1731         return _current;
1732     }
1733 
1734     static if (isForwardRange!R)
1735     {
1736         ///
1737         @property typeof(this) save()
1738         {
1739             return Group(_input.save, _current);
1740         }
1741     }
1742 }
1743 
1744 ///
1745 @safe unittest
1746 {
1747     import std.algorithm.comparison : equal;
1748     import std.typecons : tuple, Tuple;
1749 
1750     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1751     assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
1752         tuple(4, 3u), tuple(5, 1u) ][]));
1753 }
1754 
1755 /**
1756  * Using group, an associative array can be easily generated with the count of each
1757  * unique element in the range.
1758  */
1759 @safe unittest
1760 {
1761     import std.algorithm.sorting : sort;
1762     import std.array : assocArray;
1763 
1764     uint[string] result;
1765     auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"];
1766     result = range.sort!((a, b) => a < b)
1767         .group
1768         .assocArray;
1769 
1770     assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]);
1771 }
1772 
1773 @safe unittest
1774 {
1775     import std.algorithm.comparison : equal;
1776     import std.internal.test.dummyrange;
1777     import std.typecons : tuple, Tuple;
1778 
1779     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1780     assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
1781                             tuple(4, 3u), tuple(5, 1u) ][]));
1782     static assert(isForwardRange!(typeof(group(arr))));
1783 
1784     foreach (DummyType; AllDummyRanges)
1785     {
1786         DummyType d;
1787         auto g = group(d);
1788 
1789         static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g)));
1790 
1791         assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u),
1792             tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u),
1793             tuple(9, 1u), tuple(10, 1u)]));
1794     }
1795 }
1796 
1797 @safe unittest
1798 {
1799     import std.algorithm.comparison : equal;
1800     import std.typecons : tuple;
1801 
1802     // https://issues.dlang.org/show_bug.cgi?id=13857
1803     immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9];
1804     auto g1 = group(a1);
1805     assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u),
1806                        tuple(4, 2u), tuple(5, 1u), tuple(6, 2u),
1807                        tuple(7, 1u), tuple(8, 1u), tuple(9, 3u)
1808                      ]));
1809 
1810     // https://issues.dlang.org/show_bug.cgi?id=13162
1811     immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0];
1812     auto g2 = a2.group;
1813     assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ]));
1814 
1815     // https://issues.dlang.org/show_bug.cgi?id=10104
1816     const a3 = [1, 1, 2, 2];
1817     auto g3 = a3.group;
1818     assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
1819 
1820     interface I {}
1821     static class C : I { override size_t toHash() const nothrow @safe { return 0; } }
1822     const C[] a4 = [new const C()];
1823     auto g4 = a4.group!"a is b";
1824     assert(g4.front[1] == 1);
1825 
1826     immutable I[] a5 = [new immutable C()];
1827     auto g5 = a5.group!"a is b";
1828     assert(g5.front[1] == 1);
1829 
1830     const(int[][]) a6 = [[1], [1]];
1831     auto g6 = a6.group;
1832     assert(equal(g6.front[0], [1]));
1833 }
1834 
1835 @safe unittest
1836 {
1837     import std.algorithm.comparison : equal;
1838     import std.typecons : tuple;
1839 
1840     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1841     auto r = arr.group;
1842     assert(r.equal([ tuple(1,1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
1843     r.popFront;
1844     assert(r.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
1845     auto s = r.save;
1846     r.popFrontN(2);
1847     assert(r.equal([ tuple(4, 3u), tuple(5, 1u) ]));
1848     assert(s.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
1849     s.popFront;
1850     auto t = s.save;
1851     r.popFront;
1852     s.popFront;
1853     assert(r.equal([ tuple(5, 1u) ]));
1854     assert(s.equal([ tuple(4, 3u), tuple(5, 1u) ]));
1855     assert(t.equal([ tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
1856 }
1857 
1858 // https://issues.dlang.org/show_bug.cgi?id=18657
1859 pure @safe unittest
1860 {
1861     import std.algorithm.comparison : equal;
1862     import std.range : refRange;
1863     string s = "foo";
1864     auto r = refRange(&s).group;
1865     assert(equal(r.save, "foo".group));
1866     assert(equal(r, "foo".group));
1867 }
1868 
1869 // Used by implementation of chunkBy for non-forward input ranges.
1870 private struct ChunkByChunkImpl(alias pred, Range)
1871 if (isInputRange!Range && !isForwardRange!Range)
1872 {
1873     alias fun = binaryFun!pred;
1874 
1875     private Range *r;
1876     private ElementType!Range prev;
1877 
1878     this(ref Range range, ElementType!Range _prev)
1879     {
1880         r = &range;
1881         prev = _prev;
1882     }
1883 
1884     @property bool empty()
1885     {
1886         return r.empty || !fun(prev, r.front);
1887     }
1888 
1889     @property ElementType!Range front()
1890     {
1891         assert(!empty, "Attempting to fetch the front of an empty chunkBy chunk.");
1892         return r.front;
1893     }
1894 
1895     void popFront()
1896     {
1897         assert(!empty, "Attempting to popFront an empty chunkBy chunk.");
1898         r.popFront();
1899     }
1900 }
1901 
1902 private template ChunkByImplIsUnary(alias pred, Range)
1903 {
1904     alias e = lvalueOf!(ElementType!Range);
1905 
1906     static if (is(typeof(binaryFun!pred(e, e)) : bool))
1907         enum ChunkByImplIsUnary = false;
1908     else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) : bool))
1909         enum ChunkByImplIsUnary = true;
1910     else
1911         static assert(0, "chunkBy expects either a binary predicate or "~
1912                          "a unary predicate on range elements of type: "~
1913                          ElementType!Range.stringof);
1914 }
1915 
1916 // Implementation of chunkBy for non-forward input ranges.
1917 private struct ChunkByImpl(alias pred, Range)
1918 if (isInputRange!Range && !isForwardRange!Range)
1919 {
1920     enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
1921 
1922     static if (isUnary)
1923         alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
1924     else
1925         alias eq = binaryFun!pred;
1926 
1927     private Range r;
1928     private ElementType!Range _prev;
1929     private bool openChunk = false;
1930 
1931     this(Range _r)
1932     {
1933         r = _r;
1934         if (!empty)
1935         {
1936             // Check reflexivity if predicate is claimed to be an equivalence
1937             // relation.
1938             assert(eq(r.front, r.front),
1939                    "predicate is not reflexive");
1940 
1941             // _prev's type may be a nested struct, so must be initialized
1942             // directly in the constructor (cannot call savePred()).
1943             _prev = r.front;
1944         }
1945         else
1946         {
1947             // We won't use _prev, but must be initialized.
1948             _prev = typeof(_prev).init;
1949         }
1950     }
1951     @property bool empty() { return r.empty && !openChunk; }
1952 
1953     @property auto front()
1954     {
1955         assert(!empty, "Attempting to fetch the front of an empty chunkBy.");
1956         openChunk = true;
1957         static if (isUnary)
1958         {
1959             import std.typecons : tuple;
1960             return tuple(unaryFun!pred(_prev),
1961                          ChunkByChunkImpl!(eq, Range)(r, _prev));
1962         }
1963         else
1964         {
1965             return ChunkByChunkImpl!(eq, Range)(r, _prev);
1966         }
1967     }
1968 
1969     void popFront()
1970     {
1971         assert(!empty, "Attempting to popFront an empty chunkBy.");
1972         openChunk = false;
1973         while (!r.empty)
1974         {
1975             if (!eq(_prev, r.front))
1976             {
1977                 _prev = r.front;
1978                 break;
1979             }
1980             r.popFront();
1981         }
1982     }
1983 }
1984 // Outer range for forward range version of chunkBy
1985 private struct ChunkByOuter(Range, bool eqEquivalenceAssured)
1986 {
1987     size_t groupNum;
1988     Range  current;
1989     Range  next;
1990     static if (!eqEquivalenceAssured)
1991     {
1992         bool nextUpdated;
1993     }
1994 }
1995 
1996 // Inner range for forward range version of chunkBy
1997 private struct ChunkByGroup(alias eq, Range, bool eqEquivalenceAssured)
1998 {
1999     import std.typecons : RefCounted;
2000 
2001     alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured);
2002 
2003     private size_t groupNum;
2004     static if (eqEquivalenceAssured)
2005     {
2006         private Range  start;
2007     }
2008     private Range  current;
2009 
2010     // using union prevents RefCounted destructor from propagating @system to
2011     // user code
2012     union { private RefCounted!(OuterRange) mothership; }
2013     private @trusted ref cargo() { return mothership.refCountedPayload; }
2014 
2015     private this(ref RefCounted!(OuterRange) origin)
2016     {
2017         () @trusted { mothership = origin; }();
2018         groupNum = cargo.groupNum;
2019         current = cargo.current.save;
2020         assert(!current.empty, "Passed range 'r' must not be empty");
2021 
2022         static if (eqEquivalenceAssured)
2023         {
2024             start = cargo.current.save;
2025 
2026             // Check for reflexivity.
2027             assert(eq(start.front, current.front),
2028                 "predicate is not reflexive");
2029         }
2030     }
2031 
2032     // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239
2033     this(this) scope @trusted
2034     {
2035         import core.lifetime : emplace;
2036         // since mothership has to be in a union, we have to manually trigger
2037         // an increment to the reference count.
2038         auto temp = mothership;
2039         mothership = temp;
2040 
2041         // prevents the reference count from falling back with brute force
2042         emplace(&temp);
2043     }
2044 
2045     @property bool empty() { return groupNum == size_t.max; }
2046     @property auto ref front() { return current.front; }
2047 
2048     void popFront()
2049     {
2050         static if (!eqEquivalenceAssured)
2051         {
2052             auto prevElement = current.front;
2053         }
2054 
2055         current.popFront();
2056 
2057         static if (eqEquivalenceAssured)
2058         {
2059             //this requires transitivity from the predicate.
2060             immutable nowEmpty = current.empty || !eq(start.front, current.front);
2061         }
2062         else
2063         {
2064             immutable nowEmpty = current.empty || !eq(prevElement, current.front);
2065         }
2066 
2067 
2068         if (nowEmpty)
2069         {
2070             if (groupNum == cargo.groupNum)
2071             {
2072                 // If parent range hasn't moved on yet, help it along by
2073                 // saving location of start of next Group.
2074                 cargo.next = current.save;
2075                 static if (!eqEquivalenceAssured)
2076                 {
2077                     cargo.nextUpdated = true;
2078                 }
2079             }
2080 
2081             groupNum = size_t.max;
2082         }
2083     }
2084 
2085     @property auto save()
2086     {
2087         auto copy = this;
2088         copy.current = current.save;
2089         return copy;
2090     }
2091 
2092     @trusted ~this()
2093     {
2094         mothership.destroy;
2095     }
2096 }
2097 
2098 private enum GroupingOpType{binaryEquivalent, binaryAny, unary}
2099 
2100 // Single-pass implementation of chunkBy for forward ranges.
2101 private struct ChunkByImpl(alias pred, alias eq, GroupingOpType opType, Range)
2102 if (isForwardRange!Range)
2103 {
2104     import std.typecons : RefCounted;
2105 
2106     enum bool eqEquivalenceAssured = opType != GroupingOpType.binaryAny;
2107     alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured);
2108     alias InnerRange = ChunkByGroup!(eq, Range, eqEquivalenceAssured);
2109 
2110     static assert(isForwardRange!InnerRange);
2111 
2112     // using union prevents RefCounted destructor from propagating @system to
2113     // user code
2114     union { private RefCounted!OuterRange _impl; }
2115     private @trusted ref impl() { return _impl; }
2116     private @trusted ref implPL() { return _impl.refCountedPayload; }
2117 
2118     this(Range r)
2119     {
2120         import core.lifetime : move;
2121 
2122         auto savedR = r.save;
2123 
2124         static if (eqEquivalenceAssured) () @trusted
2125         {
2126             _impl = RefCounted!OuterRange(0, r, savedR.move);
2127         }();
2128         else () @trusted
2129         {
2130             _impl = RefCounted!OuterRange(0, r, savedR.move, false);
2131         }();
2132     }
2133 
2134     // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239
2135     this(this) scope @trusted
2136     {
2137         import core.lifetime : emplace;
2138         // since _impl has to be in a union, we have to manually trigger
2139         // an increment to the reference count.
2140         auto temp = _impl;
2141         _impl = temp;
2142 
2143         // prevents the reference count from falling back with brute force
2144         emplace(&temp);
2145     }
2146 
2147     @property bool empty() { return implPL.current.empty; }
2148 
2149     static if (opType == GroupingOpType.unary) @property auto front()
2150     {
2151         import std.typecons : tuple;
2152 
2153         return tuple(unaryFun!pred(implPL.current.front), InnerRange(impl));
2154     }
2155     else @property auto front()
2156     {
2157         return InnerRange(impl);
2158     }
2159 
2160     static if (eqEquivalenceAssured) void popFront()
2161     {
2162         // Scan for next group. If we're lucky, one of our Groups would have
2163         // already set .next to the start of the next group, in which case the
2164         // loop is skipped.
2165         while (!implPL.next.empty && eq(implPL.current.front, implPL.next.front))
2166         {
2167             implPL.next.popFront();
2168         }
2169 
2170         implPL.current = implPL.next.save;
2171 
2172         // Indicate to any remaining Groups that we have moved on.
2173         implPL.groupNum++;
2174     }
2175     else void popFront()
2176     {
2177         if (implPL.nextUpdated)
2178         {
2179             implPL.current = implPL.next.save;
2180         }
2181         else while (true)
2182         {
2183             auto prevElement = implPL.current.front;
2184             implPL.current.popFront();
2185             if (implPL.current.empty) break;
2186             if (!eq(prevElement, implPL.current.front)) break;
2187         }
2188 
2189         implPL.nextUpdated = false;
2190         // Indicate to any remaining Groups that we have moved on.
2191         implPL.groupNum++;
2192     }
2193 
2194     @property auto save()
2195     {
2196         // Note: the new copy of the range will be detached from any existing
2197         // satellite Groups, and will not benefit from the .next acceleration.
2198         return typeof(this)(implPL.current.save);
2199     }
2200 
2201     static assert(isForwardRange!(typeof(this)), typeof(this).stringof
2202             ~ " must be a forward range");
2203 
2204     @trusted ~this()
2205     {
2206         _impl.destroy;
2207     }
2208 }
2209 
2210 //Test for https://issues.dlang.org/show_bug.cgi?id=14909
2211 @safe unittest
2212 {
2213     import std.algorithm.comparison : equal;
2214     import std.typecons : tuple;
2215     import std.stdio;
2216     auto n = 3;
2217     auto s = [1,2,3].chunkBy!(a => a+n);
2218     auto t = s.save.map!(x=>x[0]);
2219     auto u = s.map!(x=>x[1]);
2220     assert(t.equal([4,5,6]));
2221     assert(u.equal!equal([[1],[2],[3]]));
2222 }
2223 
2224 //Testing inferring @system correctly
2225 @safe unittest
2226 {
2227     struct DeadlySave
2228     {
2229         int front;
2230         @safe void popFront(){front++;}
2231         @safe bool empty(){return front >= 5;}
2232         @system auto save(){return this;}
2233     }
2234 
2235     auto test1()
2236     {
2237         DeadlySave src;
2238         return src.walkLength;
2239 
2240     }
2241 
2242     auto test2()
2243     {
2244         DeadlySave src;
2245         return src.chunkBy!((a,b) => a % 2 == b % 2).walkLength;
2246     }
2247 
2248     static assert(isSafe!test1);
2249     static assert(!isSafe!test2);
2250 }
2251 
2252 //Test for https://issues.dlang.org/show_bug.cgi?id=18751
2253 @safe unittest
2254 {
2255     import std.algorithm.comparison : equal;
2256 
2257     string[] data = [ "abc", "abc", "def" ];
2258     int[] indices = [ 0, 1, 2 ];
2259 
2260     auto chunks = indices.chunkBy!((i, j) => data[i] == data[j]);
2261     assert(chunks.equal!equal([ [ 0, 1 ], [ 2 ] ]));
2262 }
2263 
2264 //Additional test for fix for issues 14909 and 18751
2265 @safe unittest
2266 {
2267     import std.algorithm.comparison : equal;
2268     auto v = [2,4,8,3,6,9,1,5,7];
2269     auto i = 2;
2270     assert(v.chunkBy!((a,b) => a % i == b % i).equal!equal([[2,4,8],[3],[6],[9,1,5,7]]));
2271 }
2272 
2273 @system unittest
2274 {
2275     import std.algorithm.comparison : equal;
2276 
2277     size_t popCount = 0;
2278     static class RefFwdRange
2279     {
2280         int[]  impl;
2281         size_t* pcount;
2282 
2283         @safe nothrow:
2284 
2285         this(int[] data, size_t* pcount) { impl = data; this.pcount = pcount; }
2286         @property bool empty() { return impl.empty; }
2287         @property auto ref front() { return impl.front; }
2288         void popFront()
2289         {
2290             impl.popFront();
2291             (*pcount)++;
2292         }
2293         @property auto save() { return new RefFwdRange(impl, pcount); }
2294     }
2295     static assert(isForwardRange!RefFwdRange);
2296 
2297     auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9], &popCount);
2298     auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2));
2299     auto outerSave1 = groups.save;
2300 
2301     // Sanity test
2302     assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]]));
2303     assert(groups.empty);
2304 
2305     // Performance test for single-traversal use case: popFront should not have
2306     // been called more times than there are elements if we traversed the
2307     // segmented range exactly once.
2308     assert(popCount == 9);
2309 
2310     // Outer range .save test
2311     groups = outerSave1.save;
2312     assert(!groups.empty);
2313 
2314     // Inner range .save test
2315     auto grp1 = groups.front.save;
2316     auto grp1b = grp1.save;
2317     assert(grp1b.equal([1, 3, 5]));
2318     assert(grp1.save.equal([1, 3, 5]));
2319 
2320     // Inner range should remain consistent after outer range has moved on.
2321     groups.popFront();
2322     assert(grp1.save.equal([1, 3, 5]));
2323 
2324     // Inner range should not be affected by subsequent inner ranges.
2325     assert(groups.front.equal([2, 4]));
2326     assert(grp1.save.equal([1, 3, 5]));
2327 }
2328 
2329 /**
2330  * Chunks an input range into subranges of equivalent adjacent elements.
2331  * In other languages this is often called `partitionBy`, `groupBy`
2332  * or `sliceWhen`.
2333  *
2334  * Equivalence is defined by the predicate `pred`, which can be either
2335  * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is
2336  * passed to $(REF unaryFun, std,functional). In the binary form, two range elements
2337  * `a` and `b` are considered equivalent if `pred(a,b)` is true. In
2338  * unary form, two elements are considered equivalent if `pred(a) == pred(b)`
2339  * is true.
2340  *
2341  * This predicate must be an equivalence relation, that is, it must be
2342  * reflexive (`pred(x,x)` is always true), symmetric
2343  * (`pred(x,y) == pred(y,x)`), and transitive (`pred(x,y) && pred(y,z)`
2344  * implies `pred(x,z)`). If this is not the case, the range returned by
2345  * chunkBy may assert at runtime or behave erratically. Use $(LREF splitWhen)
2346  * if you want to chunk by a predicate that is not an equivalence relation.
2347  *
2348  * Params:
2349  *  pred = Predicate for determining equivalence.
2350  *  r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked.
2351  *
2352  * Returns: With a binary predicate, a range of ranges is returned in which
2353  * all elements in a given subrange are equivalent under the given predicate.
2354  * With a unary predicate, a range of tuples is returned, with the tuple
2355  * consisting of the result of the unary predicate for each subrange, and the
2356  * subrange itself. Copying the range currently has reference semantics, but this may
2357  * change in the future.
2358  *
2359  * Notes:
2360  *
2361  * Equivalent elements separated by an intervening non-equivalent element will
2362  * appear in separate subranges; this function only considers adjacent
2363  * equivalence. Elements in the subranges will always appear in the same order
2364  * they appear in the original range.
2365  *
2366  * See_also:
2367  * $(LREF group), which collapses adjacent equivalent elements into a single
2368  * element.
2369  */
2370 auto chunkBy(alias pred, Range)(Range r)
2371 if (isInputRange!Range)
2372 {
2373     static if (ChunkByImplIsUnary!(pred, Range))
2374     {
2375         enum opType = GroupingOpType.unary;
2376         alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
2377     }
2378     else
2379     {
2380         enum opType = GroupingOpType.binaryEquivalent;
2381         alias eq = binaryFun!pred;
2382     }
2383     static if (isForwardRange!Range)
2384         return ChunkByImpl!(pred, eq, opType, Range)(r);
2385     else
2386         return ChunkByImpl!(pred, Range)(r);
2387 }
2388 
2389 /// Showing usage with binary predicate:
2390 @safe unittest
2391 {
2392     import std.algorithm.comparison : equal;
2393 
2394     // Grouping by particular attribute of each element:
2395     auto data = [
2396         [1, 1],
2397         [1, 2],
2398         [2, 2],
2399         [2, 3]
2400     ];
2401 
2402     auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);
2403     assert(r1.equal!equal([
2404         [[1, 1], [1, 2]],
2405         [[2, 2], [2, 3]]
2406     ]));
2407 
2408     auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);
2409     assert(r2.equal!equal([
2410         [[1, 1]],
2411         [[1, 2], [2, 2]],
2412         [[2, 3]]
2413     ]));
2414 }
2415 
2416 /// Showing usage with unary predicate:
2417 /* FIXME: pure nothrow*/ @safe unittest
2418 {
2419     import std.algorithm.comparison : equal;
2420     import std.range.primitives;
2421     import std.typecons : tuple;
2422 
2423     // Grouping by particular attribute of each element:
2424     auto range =
2425     [
2426         [1, 1],
2427         [1, 1],
2428         [1, 2],
2429         [2, 2],
2430         [2, 3],
2431         [2, 3],
2432         [3, 3]
2433     ];
2434 
2435     auto byX = chunkBy!(a => a[0])(range);
2436     auto expected1 =
2437     [
2438         tuple(1, [[1, 1], [1, 1], [1, 2]]),
2439         tuple(2, [[2, 2], [2, 3], [2, 3]]),
2440         tuple(3, [[3, 3]])
2441     ];
2442     foreach (e; byX)
2443     {
2444         assert(!expected1.empty);
2445         assert(e[0] == expected1.front[0]);
2446         assert(e[1].equal(expected1.front[1]));
2447         expected1.popFront();
2448     }
2449 
2450     auto byY = chunkBy!(a => a[1])(range);
2451     auto expected2 =
2452     [
2453         tuple(1, [[1, 1], [1, 1]]),
2454         tuple(2, [[1, 2], [2, 2]]),
2455         tuple(3, [[2, 3], [2, 3], [3, 3]])
2456     ];
2457     foreach (e; byY)
2458     {
2459         assert(!expected2.empty);
2460         assert(e[0] == expected2.front[0]);
2461         assert(e[1].equal(expected2.front[1]));
2462         expected2.popFront();
2463     }
2464 }
2465 
2466 /*FIXME: pure @safe nothrow*/ @system unittest
2467 {
2468     import std.algorithm.comparison : equal;
2469     import std.typecons : tuple;
2470 
2471     struct Item { int x, y; }
2472 
2473     // Force R to have only an input range API with reference semantics, so
2474     // that we're not unknowingly making use of array semantics outside of the
2475     // range API.
2476     class RefInputRange(R)
2477     {
2478         R data;
2479         this(R _data) pure @safe nothrow { data = _data; }
2480         @property bool empty() pure @safe nothrow { return data.empty; }
2481         @property auto front() pure @safe nothrow { assert(!empty); return data.front; }
2482         void popFront() pure @safe nothrow { assert(!empty); data.popFront(); }
2483     }
2484     auto refInputRange(R)(R range) { return new RefInputRange!R(range); }
2485 
2486     // An input range API with value semantics.
2487     struct ValInputRange(R)
2488     {
2489         R data;
2490         this(R _data) pure @safe nothrow { data = _data; }
2491         @property bool empty() pure @safe nothrow { return data.empty; }
2492         @property auto front() pure @safe nothrow { assert(!empty); return data.front; }
2493         void popFront() pure @safe nothrow { assert(!empty); data.popFront(); }
2494     }
2495     auto valInputRange(R)(R range) { return ValInputRange!R(range); }
2496 
2497     {
2498         auto arr = [ Item(1,2), Item(1,3), Item(2,3) ];
2499         static assert(isForwardRange!(typeof(arr)));
2500 
2501         auto byX = chunkBy!(a => a.x)(arr);
2502         static assert(isForwardRange!(typeof(byX)));
2503 
2504         auto byX_subrange1 = byX.front[1].save;
2505         auto byX_subrange2 = byX.front[1].save;
2506         static assert(isForwardRange!(typeof(byX_subrange1)));
2507         static assert(isForwardRange!(typeof(byX_subrange2)));
2508 
2509         byX.popFront();
2510         assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ]));
2511         byX_subrange1.popFront();
2512         assert(byX_subrange1.equal([ Item(1,3) ]));
2513         assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ]));
2514 
2515         auto byY = chunkBy!(a => a.y)(arr);
2516         static assert(isForwardRange!(typeof(byY)));
2517 
2518         auto byY2 = byY.save;
2519         static assert(is(typeof(byY) == typeof(byY2)));
2520         byY.popFront();
2521         assert(byY.front[0] == 3);
2522         assert(byY.front[1].equal([ Item(1,3), Item(2,3) ]));
2523         assert(byY2.front[0] == 2);
2524         assert(byY2.front[1].equal([ Item(1,2) ]));
2525     }
2526 
2527     // Test non-forward input ranges with reference semantics.
2528     {
2529         auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2530         auto byX = chunkBy!(a => a.x)(range);
2531         assert(byX.front[0] == 1);
2532         assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
2533         byX.popFront();
2534         assert(byX.front[0] == 2);
2535         assert(byX.front[1].equal([ Item(2,2) ]));
2536         byX.popFront();
2537         assert(byX.empty);
2538         assert(range.empty);
2539 
2540         range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2541         auto byY = chunkBy!(a => a.y)(range);
2542         assert(byY.front[0] == 1);
2543         assert(byY.front[1].equal([ Item(1,1) ]));
2544         byY.popFront();
2545         assert(byY.front[0] == 2);
2546         assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
2547         byY.popFront();
2548         assert(byY.empty);
2549         assert(range.empty);
2550     }
2551 
2552     // Test non-forward input ranges with value semantics.
2553     {
2554         auto range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2555         auto byX = chunkBy!(a => a.x)(range);
2556         assert(byX.front[0] == 1);
2557         assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
2558         byX.popFront();
2559         assert(byX.front[0] == 2);
2560         assert(byX.front[1].equal([ Item(2,2) ]));
2561         byX.popFront();
2562         assert(byX.empty);
2563         assert(!range.empty);    // Opposite of refInputRange test
2564 
2565         range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2566         auto byY = chunkBy!(a => a.y)(range);
2567         assert(byY.front[0] == 1);
2568         assert(byY.front[1].equal([ Item(1,1) ]));
2569         byY.popFront();
2570         assert(byY.front[0] == 2);
2571         assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
2572         byY.popFront();
2573         assert(byY.empty);
2574         assert(!range.empty);    // Opposite of refInputRange test
2575     }
2576 
2577     /* https://issues.dlang.org/show_bug.cgi?id=19532
2578      * General behavior of non-forward input ranges.
2579      *
2580      * - If the same chunk is retrieved multiple times via front, the separate chunk
2581      *   instances refer to a shared range segment that advances as a single range.
2582      * - Emptying a chunk via popFront does not implicitly popFront the chunk off
2583      *   main range. The chunk is still available via front, it is just empty.
2584      */
2585     {
2586         import std.algorithm.comparison : equal;
2587         import core.exception : AssertError;
2588         import std.exception : assertThrown;
2589 
2590         auto a = [[0, 0], [0, 1],
2591                   [1, 2], [1, 3], [1, 4],
2592                   [2, 5], [2, 6],
2593                   [3, 7],
2594                   [4, 8]];
2595 
2596         // Value input range
2597         {
2598             auto r = valInputRange(a).chunkBy!((a, b) => a[0] == b[0]);
2599 
2600             size_t numChunks = 0;
2601             while (!r.empty)
2602             {
2603                 ++numChunks;
2604                 auto chunk = r.front;
2605                 while (!chunk.empty)
2606                 {
2607                     assert(r.front.front[1] == chunk.front[1]);
2608                     chunk.popFront;
2609                 }
2610                 assert(!r.empty);
2611                 assert(r.front.empty);
2612                 r.popFront;
2613             }
2614 
2615             assert(numChunks == 5);
2616 
2617             // Now front and popFront should assert.
2618             bool thrown = false;
2619             try r.front;
2620             catch (AssertError) thrown = true;
2621             assert(thrown);
2622 
2623             thrown = false;
2624             try r.popFront;
2625             catch (AssertError) thrown = true;
2626             assert(thrown);
2627         }
2628 
2629         // Reference input range
2630         {
2631             auto r = refInputRange(a).chunkBy!((a, b) => a[0] == b[0]);
2632 
2633             size_t numChunks = 0;
2634             while (!r.empty)
2635             {
2636                 ++numChunks;
2637                 auto chunk = r.front;
2638                 while (!chunk.empty)
2639                 {
2640                     assert(r.front.front[1] == chunk.front[1]);
2641                     chunk.popFront;
2642                 }
2643                 assert(!r.empty);
2644                 assert(r.front.empty);
2645                 r.popFront;
2646             }
2647 
2648             assert(numChunks == 5);
2649 
2650             // Now front and popFront should assert.
2651             bool thrown = false;
2652             try r.front;
2653             catch (AssertError) thrown = true;
2654             assert(thrown);
2655 
2656             thrown = false;
2657             try r.popFront;
2658             catch (AssertError) thrown = true;
2659             assert(thrown);
2660         }
2661 
2662         // Ensure that starting with an empty range doesn't create an empty chunk.
2663         {
2664             int[] emptyRange = [];
2665 
2666             auto r1 = valInputRange(emptyRange).chunkBy!((a, b) => a == b);
2667             auto r2 = refInputRange(emptyRange).chunkBy!((a, b) => a == b);
2668 
2669             assert(r1.empty);
2670             assert(r2.empty);
2671 
2672             bool thrown = false;
2673             try r1.front;
2674             catch (AssertError) thrown = true;
2675             assert(thrown);
2676 
2677             thrown = false;
2678             try r1.popFront;
2679             catch (AssertError) thrown = true;
2680             assert(thrown);
2681 
2682             thrown = false;
2683             try r2.front;
2684             catch (AssertError) thrown = true;
2685             assert(thrown);
2686 
2687             thrown = false;
2688             try r2.popFront;
2689             catch (AssertError) thrown = true;
2690             assert(thrown);
2691         }
2692     }
2693 
2694     // https://issues.dlang.org/show_bug.cgi?id=19532 - Using roundRobin/chunkBy
2695     {
2696         import std.algorithm.comparison : equal;
2697         import std.range : roundRobin;
2698 
2699         auto a0 = [0, 1, 3, 6];
2700         auto a1 = [0, 2, 4, 6, 7];
2701         auto a2 = [1, 2, 4, 6, 8, 8, 9];
2702 
2703         auto expected =
2704             [[0, 0], [1, 1], [2, 2], [3], [4, 4], [6, 6, 6], [7], [8, 8], [9]];
2705 
2706         auto r1 = roundRobin(valInputRange(a0), valInputRange(a1), valInputRange(a2))
2707             .chunkBy!((a, b) => a == b);
2708         assert(r1.equal!equal(expected));
2709 
2710         auto r2 = roundRobin(refInputRange(a0), refInputRange(a1), refInputRange(a2))
2711             .chunkBy!((a, b) => a == b);
2712         assert(r2.equal!equal(expected));
2713 
2714         auto r3 = roundRobin(a0, a1, a2).chunkBy!((a, b) => a == b);
2715         assert(r3.equal!equal(expected));
2716     }
2717 
2718     // https://issues.dlang.org/show_bug.cgi?id=19532 - Using merge/chunkBy
2719     {
2720         import std.algorithm.comparison : equal;
2721         import std.algorithm.sorting : merge;
2722 
2723         auto a0 = [2, 3, 5];
2724         auto a1 = [2, 4, 5];
2725         auto a2 = [1, 2, 4, 5];
2726 
2727         auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
2728 
2729         auto r1 = merge(valInputRange(a0), valInputRange(a1), valInputRange(a2))
2730             .chunkBy!((a, b) => a == b);
2731         assert(r1.equal!equal(expected));
2732 
2733         auto r2 = merge(refInputRange(a0), refInputRange(a1), refInputRange(a2))
2734             .chunkBy!((a, b) => a == b);
2735         assert(r2.equal!equal(expected));
2736 
2737         auto r3 = merge(a0, a1, a2).chunkBy!((a, b) => a == b);
2738         assert(r3.equal!equal(expected));
2739     }
2740 
2741     // https://issues.dlang.org/show_bug.cgi?id=19532 - Using chunkBy/map-fold
2742     {
2743         import std.algorithm.comparison : equal;
2744         import std.algorithm.iteration : fold, map;
2745 
2746         auto a = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8, 9];
2747         auto expected = [0, 3, 4, 6, 8, 5, 18, 7, 16, 9];
2748 
2749         auto r1 = a
2750             .chunkBy!((a, b) => a == b)
2751             .map!(c => c.fold!((a, b) => a + b));
2752         assert(r1.equal(expected));
2753 
2754         auto r2 = valInputRange(a)
2755             .chunkBy!((a, b) => a == b)
2756             .map!(c => c.fold!((a, b) => a + b));
2757         assert(r2.equal(expected));
2758 
2759         auto r3 = refInputRange(a)
2760             .chunkBy!((a, b) => a == b)
2761             .map!(c => c.fold!((a, b) => a + b));
2762         assert(r3.equal(expected));
2763     }
2764 
2765     // https://issues.dlang.org/show_bug.cgi?id=16169
2766     // https://issues.dlang.org/show_bug.cgi?id=17966
2767     // https://issues.dlang.org/show_bug.cgi?id=19532
2768     // Using multiwayMerge/chunkBy
2769     {
2770         import std.algorithm.comparison : equal;
2771         import std.algorithm.setops : multiwayMerge;
2772 
2773         {
2774             auto a0 = [2, 3, 5];
2775             auto a1 = [2, 4, 5];
2776             auto a2 = [1, 2, 4, 5];
2777 
2778             auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
2779             auto r = multiwayMerge([a0, a1, a2]).chunkBy!((a, b) => a == b);
2780             assert(r.equal!equal(expected));
2781         }
2782         {
2783             auto a0 = [2, 3, 5];
2784             auto a1 = [2, 4, 5];
2785             auto a2 = [1, 2, 4, 5];
2786 
2787             auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
2788             auto r =
2789                 multiwayMerge([valInputRange(a0), valInputRange(a1), valInputRange(a2)])
2790                 .chunkBy!((a, b) => a == b);
2791             assert(r.equal!equal(expected));
2792         }
2793         {
2794             auto a0 = [2, 3, 5];
2795             auto a1 = [2, 4, 5];
2796             auto a2 = [1, 2, 4, 5];
2797 
2798             auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
2799             auto r =
2800                 multiwayMerge([refInputRange(a0), refInputRange(a1), refInputRange(a2)])
2801                 .chunkBy!((a, b) => a == b);
2802             assert(r.equal!equal(expected));
2803         }
2804     }
2805 
2806     // https://issues.dlang.org/show_bug.cgi?id=20496
2807     {
2808         auto r = [1,1,1,2,2,2,3,3,3];
2809         r.chunkBy!((ref e1, ref e2) => e1 == e2);
2810     }
2811 }
2812 
2813 
2814 
2815 // https://issues.dlang.org/show_bug.cgi?id=13805
2816 @safe unittest
2817 {
2818     [""].map!((s) => s).chunkBy!((x, y) => true);
2819 }
2820 
2821 /**
2822 Splits a forward range into subranges in places determined by a binary
2823 predicate.
2824 
2825 When iterating, one element of `r` is compared with `pred` to the next
2826 element. If `pred` return true, a new subrange is started for the next element.
2827 Otherwise, they are part of the same subrange.
2828 
2829 If the elements are compared with an inequality (!=) operator, consider
2830 $(LREF chunkBy) instead, as it's likely faster to execute.
2831 
2832 Params:
2833 pred = Predicate for determining where to split. The earlier element in the
2834 source range is always given as the first argument.
2835 r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to be split.
2836 Returns: a range of subranges of `r`, split such that within a given subrange,
2837 calling `pred` with any pair of adjacent elements as arguments returns `false`.
2838 Copying the range currently has reference semantics, but this may change in the future.
2839 
2840 See_also:
2841 $(LREF splitter), which uses elements as splitters instead of element-to-element
2842 relations.
2843 */
2844 
2845 auto splitWhen(alias pred, Range)(Range r)
2846 if (isForwardRange!Range)
2847 {   import std.functional : not;
2848     return ChunkByImpl!(not!pred, not!pred, GroupingOpType.binaryAny, Range)(r);
2849 }
2850 
2851 ///
2852 nothrow pure @safe unittest
2853 {
2854     import std.algorithm.comparison : equal;
2855     import std.range : dropExactly;
2856     auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0];
2857 
2858     auto result1 = source.splitWhen!((a,b) => a <= b);
2859     assert(result1.save.equal!equal([
2860         [4, 3, 2],
2861         [11, 0, -3],
2862         [-3],
2863         [5, 3, 0]
2864     ]));
2865 
2866     //splitWhen, like chunkBy, is currently a reference range (this may change
2867     //in future). Remember to call `save` when appropriate.
2868     auto result2 = result1.dropExactly(2);
2869     assert(result1.save.equal!equal([
2870         [-3],
2871         [5, 3, 0]
2872     ]));
2873 }
2874 
2875 //ensure we don't iterate the underlying range twice
2876 nothrow @safe unittest
2877 {
2878     import std.algorithm.comparison : equal;
2879     import std.math.algebraic : abs;
2880 
2881     struct SomeRange
2882     {
2883         int[] elements;
2884         static int popfrontsSoFar;
2885 
2886         auto front(){return elements[0];}
2887         nothrow @safe void popFront()
2888         {   popfrontsSoFar++;
2889             elements = elements[1 .. $];
2890         }
2891         auto empty(){return elements.length == 0;}
2892         auto save(){return this;}
2893     }
2894 
2895     auto result = SomeRange([10, 9, 8, 5, 0, 1, 0, 8, 11, 10, 8, 12])
2896         .splitWhen!((a, b) => abs(a - b) >= 3);
2897 
2898     assert(result.equal!equal([
2899         [10, 9, 8],
2900         [5],
2901         [0, 1, 0],
2902         [8],
2903         [11, 10, 8],
2904         [12]
2905     ]));
2906 
2907     assert(SomeRange.popfrontsSoFar == 12);
2908 }
2909 
2910 // Issue 13595
2911 @safe unittest
2912 {
2913     import std.algorithm.comparison : equal;
2914     auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].splitWhen!((x, y) => ((x*y) % 3) > 0);
2915     assert(r.equal!equal([
2916         [1],
2917         [2, 3, 4],
2918         [5, 6, 7],
2919         [8, 9]
2920     ]));
2921 }
2922 
2923 nothrow pure @safe unittest
2924 {
2925     // Grouping by maximum adjacent difference:
2926     import std.math.algebraic : abs;
2927     import std.algorithm.comparison : equal;
2928     auto r3 = [1, 3, 2, 5, 4, 9, 10].splitWhen!((a, b) => abs(a-b) >= 3);
2929     assert(r3.equal!equal([
2930         [1, 3, 2],
2931         [5, 4],
2932         [9, 10]
2933     ]));
2934 }
2935 
2936 // empty range splitWhen
2937 @nogc nothrow pure @system unittest
2938 {
2939     int[1] sliceable;
2940     auto result = sliceable[0 .. 0].splitWhen!((a,b) => a+b > 10);
2941     assert(result.empty);
2942 }
2943 
2944 // joiner
2945 /**
2946 Lazily joins a range of ranges with a separator. The separator itself
2947 is a range. If a separator is not provided, then the ranges are
2948 joined directly without anything in between them (often called `flatten`
2949 in other languages).
2950 
2951 Params:
2952     r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input
2953         ranges to be joined.
2954     sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of
2955         element(s) to serve as separators in the joined range.
2956 
2957 Returns:
2958 A range of elements in the joined range. This will be a bidirectional range if
2959 both outer and inner ranges of `RoR` are at least bidirectional ranges. Else if
2960 both outer and inner ranges of `RoR` are forward ranges, the returned range will
2961 be likewise. Otherwise it will be only an input range. The
2962 $(REF_ALTTEXT range bidirectionality, isBidirectionalRange, std,range,primitives)
2963 is propagated if no separator is specified.
2964 
2965 See_also:
2966 $(REF chain, std,range), which chains a sequence of ranges with compatible elements
2967 into a single range.
2968 
2969 Note:
2970 When both outer and inner ranges of `RoR` are bidirectional and the joiner is
2971 iterated from the back to the front, the separator will still be consumed from
2972 front to back, even if it is a bidirectional range too.
2973  */
2974 auto joiner(RoR, Separator)(RoR r, Separator sep)
2975 {
2976     static assert(isInputRange!RoR, "The type of RoR '", RoR.stringof
2977             , " must be an InputRange (isInputRange!", RoR.stringof, ").");
2978     static assert(isInputRange!(ElementType!RoR), "The ElementyType of RoR '"
2979             , ElementType!(RoR).stringof, "' must be an InputRange "
2980             , "(isInputRange!(ElementType!(", RoR.stringof , "))).");
2981     static assert(isForwardRange!Separator, "The type of the Separator '"
2982             , Separator.stringof, "' must be a ForwardRange (isForwardRange!("
2983             , Separator.stringof, ")).");
2984     static assert(is(ElementType!Separator : ElementType!(ElementType!RoR))
2985             , "The type of the elements of the separator range does not match "
2986             , "the type of the elements that are joined. Separator type '"
2987             , ElementType!(Separator).stringof, "' is not implicitly"
2988             , "convertible to range element type '"
2989             , ElementType!(ElementType!RoR).stringof, "' (is(ElementType!"
2990             , Separator.stringof, " : ElementType!(ElementType!", RoR.stringof
2991             , "))).");
2992 
2993     static struct Result
2994     {
2995         private RoR _items;
2996         private ElementType!RoR _current;
2997         bool inputStartsWithEmpty = false;
2998         static if (isBidirectional)
2999         {
3000             private ElementType!RoR _currentBack;
3001             bool inputEndsWithEmpty = false;
3002         }
3003         enum isBidirectional = isBidirectionalRange!RoR &&
3004                                isBidirectionalRange!(ElementType!RoR);
3005         static if (isRandomAccessRange!Separator)
3006         {
3007             static struct CurrentSep
3008             {
3009                 private Separator _sep;
3010                 private size_t sepIndex;
3011                 private size_t sepLength; // cache the length for performance
3012                 auto front() { return _sep[sepIndex]; }
3013                 void popFront() { sepIndex++; }
3014                 auto empty() { return sepIndex >= sepLength; }
3015                 auto save()
3016                 {
3017                     auto copy = this;
3018                     copy._sep = _sep;
3019                     return copy;
3020                 }
3021                 void reset()
3022                 {
3023                     sepIndex = 0;
3024                 }
3025 
3026                 void initialize(Separator sep)
3027                 {
3028                     _sep = sep;
3029                     sepIndex = sepLength = _sep.length;
3030                 }
3031             }
3032         }
3033         else
3034         {
3035             static struct CurrentSep
3036             {
3037                 private Separator _sep;
3038                 Separator payload;
3039 
3040                 alias payload this;
3041 
3042                 auto save()
3043                 {
3044                     auto copy = this;
3045                     copy._sep = _sep;
3046                     return copy;
3047                 }
3048 
3049                 void reset()
3050                 {
3051                     payload = _sep.save;
3052                 }
3053 
3054                 void initialize(Separator sep)
3055                 {
3056                     _sep = sep;
3057                 }
3058             }
3059         }
3060 
3061         private CurrentSep _currentSep;
3062         static if (isBidirectional)
3063         {
3064             private CurrentSep _currentBackSep;
3065         }
3066 
3067         private void setItem()
3068         {
3069             if (!_items.empty)
3070             {
3071                 // If we're exporting .save, we must not consume any of the
3072                 // subranges, since RoR.save does not guarantee that the states
3073                 // of the subranges are also saved.
3074                 static if (isForwardRange!RoR &&
3075                            isForwardRange!(ElementType!RoR))
3076                     _current = _items.front.save;
3077                 else
3078                     _current = _items.front;
3079             }
3080         }
3081 
3082         private void useSeparator()
3083         {
3084             // Separator must always come after an item.
3085             assert(_currentSep.empty,
3086                     "Attempting to reset a non-empty separator");
3087             assert(!_items.empty,
3088                     "Attempting to use a separator in an empty joiner");
3089             _items.popFront();
3090 
3091             // If there are no more items, we're done, since separators are not
3092             // terminators.
3093             if (_items.empty) return;
3094 
3095             if (_currentSep._sep.empty)
3096             {
3097                 // Advance to the next range in the
3098                 // input
3099                 while (_items.front.empty)
3100                 {
3101                     _items.popFront();
3102                     if (_items.empty) return;
3103                 }
3104                 setItem;
3105             }
3106             else
3107             {
3108                 _currentSep.reset;
3109                 assert(!_currentSep.empty, "separator must not be empty");
3110             }
3111         }
3112 
3113         this(RoR items, Separator sep)
3114         {
3115             _items = items;
3116             _currentSep.initialize(sep);
3117             static if (isBidirectional)
3118                 _currentBackSep.initialize(sep);
3119 
3120             //mixin(useItem); // _current should be initialized in place
3121             if (_items.empty)
3122             {
3123                 _current = _current.init;   // set invalid state
3124                 static if (isBidirectional)
3125                     _currentBack = _currentBack.init;
3126             }
3127             else
3128             {
3129                 // If we're exporting .save, we must not consume any of the
3130                 // subranges, since RoR.save does not guarantee that the states
3131                 // of the subranges are also saved.
3132                 static if (isForwardRange!RoR &&
3133                            isForwardRange!(ElementType!RoR))
3134                     _current = _items.front.save;
3135                 else
3136                     _current = _items.front;
3137 
3138                 static if (isBidirectional)
3139                 {
3140                     _currentBack = _items.back.save;
3141 
3142                     if (_currentBack.empty)
3143                     {
3144                         // No data in the currentBack item - toggle to use
3145                         // the separator
3146                         inputEndsWithEmpty = true;
3147                     }
3148                 }
3149 
3150                 if (_current.empty)
3151                 {
3152                     // No data in the current item - toggle to use the separator
3153                     inputStartsWithEmpty = true;
3154 
3155                     // If RoR contains a single empty element,
3156                     // the returned Result will always be empty
3157                     import std.range : dropOne;
3158                     static if (hasLength!RoR)
3159                     {
3160                         if (_items.length == 1)
3161                             _items.popFront;
3162                     }
3163                     else static if (isForwardRange!RoR)
3164                     {
3165                         if (_items.save.dropOne.empty)
3166                             _items.popFront;
3167                     }
3168                     else
3169                     {
3170                         auto _itemsCopy = _items;
3171                         if (_itemsCopy.dropOne.empty)
3172                             _items.popFront;
3173                     }
3174                 }
3175             }
3176         }
3177 
3178         @property auto empty()
3179         {
3180             return _items.empty;
3181         }
3182 
3183         //no data in the first item of the initial range - use the separator
3184         private enum useSepIfFrontIsEmpty = q{
3185             if (inputStartsWithEmpty)
3186             {
3187                 useSeparator();
3188                 inputStartsWithEmpty = false;
3189             }
3190         };
3191 
3192         @property ElementType!(ElementType!RoR) front()
3193         {
3194             mixin(useSepIfFrontIsEmpty);
3195             if (!_currentSep.empty) return _currentSep.front;
3196             assert(!_current.empty, "Attempting to fetch the front of an empty joiner.");
3197             return _current.front;
3198         }
3199 
3200         void popFront()
3201         {
3202             assert(!_items.empty, "Attempting to popFront an empty joiner.");
3203             // Using separator?
3204             mixin(useSepIfFrontIsEmpty);
3205 
3206             if (!_currentSep.empty)
3207             {
3208                 _currentSep.popFront();
3209                 if (_currentSep.empty && !_items.empty)
3210                 {
3211                     setItem;
3212                     if (_current.empty)
3213                     {
3214                         // No data in the current item - toggle to use the separator
3215                         useSeparator();
3216                     }
3217                 }
3218             }
3219             else
3220             {
3221                 // we're using the range
3222                 _current.popFront();
3223                 if (_current.empty)
3224                     useSeparator();
3225             }
3226         }
3227 
3228         static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
3229         {
3230             @property auto save()
3231             {
3232                 Result copy = this;
3233                 copy._items = _items.save;
3234                 copy._current = _current.save;
3235                 copy._currentSep = _currentSep.save;
3236                 static if (isBidirectional)
3237                 {
3238                     copy._currentBack = _currentBack;
3239                     copy._currentBackSep = _currentBackSep;
3240                 }
3241                 return copy;
3242             }
3243         }
3244 
3245         static if (isBidirectional)
3246         {
3247             //no data in the last item of the initial range - use the separator
3248             private enum useSepIfBackIsEmpty = q{
3249                 if (inputEndsWithEmpty)
3250                 {
3251                     useBackSeparator;
3252                     inputEndsWithEmpty = false;
3253                 }
3254             };
3255 
3256             private void setBackItem()
3257             {
3258                 if (!_items.empty)
3259                 {
3260                     _currentBack = _items.back.save;
3261                 }
3262             }
3263 
3264             private void useBackSeparator()
3265             {
3266                 // Separator must always come after an item.
3267                 assert(_currentBackSep.empty,
3268                         "Attempting to reset a non-empty separator");
3269                 assert(!_items.empty,
3270                         "Attempting to use a separator in an empty joiner");
3271                 _items.popBack();
3272 
3273                 // If there are no more items, we're done, since separators are not
3274                 // terminators.
3275                 if (_items.empty) return;
3276 
3277                 if (_currentBackSep._sep.empty)
3278                 {
3279                     // Advance to the next range in the
3280                     // input
3281                     while (_items.back.empty)
3282                     {
3283                         _items.popBack();
3284                         if (_items.empty) return;
3285                     }
3286                     setBackItem;
3287                 }
3288                 else
3289                 {
3290                     _currentBackSep.reset;
3291                     assert(!_currentBackSep.empty, "separator must not be empty");
3292                 }
3293             }
3294 
3295             @property ElementType!(ElementType!RoR) back()
3296             {
3297                 mixin(useSepIfBackIsEmpty);
3298 
3299                 if (!_currentBackSep.empty) return _currentBackSep.front;
3300                 assert(!_currentBack.empty, "Attempting to fetch the back of an empty joiner.");
3301                 return _currentBack.back;
3302             }
3303 
3304             void popBack()
3305             {
3306                 assert(!_items.empty, "Attempting to popBack an empty joiner.");
3307 
3308                 mixin(useSepIfBackIsEmpty);
3309 
3310                 if (!_currentBackSep.empty)
3311                 {
3312                     _currentBackSep.popFront();
3313                     if (_currentBackSep.empty && !_items.empty)
3314                     {
3315                         setBackItem;
3316                         if (_currentBack.empty)
3317                         {
3318                             // No data in the current item - toggle to use the separator
3319                             useBackSeparator();
3320                         }
3321                     }
3322                 }
3323                 else
3324                 {
3325                     // we're using the range
3326                     _currentBack.popBack();
3327                     if (_currentBack.empty)
3328                         useBackSeparator();
3329                 }
3330             }
3331         }
3332     }
3333     return Result(r, sep);
3334 }
3335 
3336 ///
3337 @safe unittest
3338 {
3339     import std.algorithm.comparison : equal;
3340     import std.conv : text;
3341 
3342     assert(["abc", "def"].joiner.equal("abcdef"));
3343     assert(["Mary", "has", "a", "little", "lamb"]
3344         .joiner("...")
3345         .equal("Mary...has...a...little...lamb"));
3346     assert(["", "abc"].joiner("xyz").equal("xyzabc"));
3347     assert([""].joiner("xyz").equal(""));
3348     assert(["", ""].joiner("xyz").equal("xyz"));
3349 }
3350 
3351 @safe pure nothrow unittest
3352 {
3353     //joiner with separator can return a bidirectional range
3354     assert(isBidirectionalRange!(typeof(["abc", "def"].joiner("..."))));
3355 }
3356 
3357 @system unittest
3358 {
3359     import std.algorithm.comparison : equal;
3360     import std.range.interfaces;
3361     import std.range.primitives;
3362     // joiner() should work for non-forward ranges too.
3363     auto r = inputRangeObject(["abc", "def"]);
3364     assert(equal(joiner(r, "xyz"), "abcxyzdef"));
3365 }
3366 
3367 @system unittest
3368 {
3369     import std.algorithm.comparison : equal;
3370     import std.range;
3371 
3372     // Related to https://issues.dlang.org/show_bug.cgi?id=8061
3373     auto r = joiner([
3374         inputRangeObject("abc"),
3375         inputRangeObject("def"),
3376     ], "-*-");
3377 
3378     assert(equal(r, "abc-*-def"));
3379 
3380     // Test case where separator is specified but is empty.
3381     auto s = joiner([
3382         inputRangeObject("abc"),
3383         inputRangeObject("def"),
3384     ], "");
3385 
3386     assert(equal(s, "abcdef"));
3387 
3388     // Test empty separator with some empty elements
3389     auto t = joiner([
3390         inputRangeObject("abc"),
3391         inputRangeObject(""),
3392         inputRangeObject("def"),
3393         inputRangeObject(""),
3394     ], "");
3395 
3396     assert(equal(t, "abcdef"));
3397 
3398     // Test empty elements with non-empty separator
3399     auto u = joiner([
3400         inputRangeObject(""),
3401         inputRangeObject("abc"),
3402         inputRangeObject(""),
3403         inputRangeObject("def"),
3404         inputRangeObject(""),
3405     ], "+-");
3406 
3407     assert(equal(u, "+-abc+-+-def+-"));
3408 
3409     // https://issues.dlang.org/show_bug.cgi?id=13441: only(x) as separator
3410     string[][] lines = [null];
3411     lines
3412         .joiner(only("b"))
3413         .array();
3414 }
3415 
3416 @safe unittest
3417 {
3418     import std.algorithm.comparison : equal;
3419 
3420     // Transience correctness test
3421     struct TransientRange
3422     {
3423     @safe:
3424         int[][] src;
3425         int[] buf;
3426 
3427         this(int[][] _src)
3428         {
3429             src = _src;
3430             buf.length = 100;
3431         }
3432         @property bool empty() { return src.empty; }
3433         @property int[] front()
3434         {
3435             assert(src.front.length <= buf.length);
3436             buf[0 .. src.front.length] = src.front[0..$];
3437             return buf[0 .. src.front.length];
3438         }
3439         void popFront() { src.popFront(); }
3440     }
3441 
3442     // Test embedded empty elements
3443     auto tr1 = TransientRange([[], [1,2,3], [], [4]]);
3444     assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4]));
3445 
3446     // Test trailing empty elements
3447     auto tr2 = TransientRange([[], [1,2,3], []]);
3448     assert(equal(joiner(tr2, [0]), [0,1,2,3,0]));
3449 
3450     // Test no empty elements
3451     auto tr3 = TransientRange([[1,2], [3,4]]);
3452     assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4]));
3453 
3454     // Test consecutive empty elements
3455     auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]);
3456     assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4]));
3457 
3458     // Test consecutive trailing empty elements
3459     auto tr5 = TransientRange([[1,2], [3,4], [], []]);
3460     assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1]));
3461 }
3462 
3463 @safe unittest
3464 {
3465     static assert(isInputRange!(typeof(joiner([""], ""))));
3466     static assert(isForwardRange!(typeof(joiner([""], ""))));
3467 }
3468 
3469 @safe pure unittest
3470 {
3471     {
3472         import std.algorithm.comparison : equal;
3473         auto r = joiner(["abc", "def", "ghi"], "?!");
3474         char[] res;
3475         while (!r.empty)
3476         {
3477             res ~= r.back;
3478             r.popBack;
3479         }
3480         assert(res.equal("ihg?!fed?!cba"));
3481     }
3482 
3483     {
3484         wchar[] sep = ['Ș', 'Ț'];
3485         auto r = joiner(["","abc",""],sep);
3486         wchar[] resFront;
3487         wchar[] resBack;
3488 
3489         auto rCopy = r.save;
3490         while (!r.empty)
3491         {
3492             resFront ~= r.front;
3493             r.popFront;
3494         }
3495 
3496         while (!rCopy.empty)
3497         {
3498             resBack ~= rCopy.back;
3499             rCopy.popBack;
3500         }
3501 
3502         import std.algorithm.comparison : equal;
3503 
3504         assert(resFront.equal("ȘȚabcȘȚ"));
3505         assert(resBack.equal("ȘȚcbaȘȚ"));
3506     }
3507 
3508     {
3509         import std.algorithm.comparison : equal;
3510         auto r = [""];
3511         r.popBack;
3512         assert(r.joiner("AB").equal(""));
3513     }
3514 
3515     {
3516         auto r = ["", "", "", "abc", ""].joiner("../");
3517         auto rCopy = r.save;
3518 
3519         char[] resFront;
3520         char[] resBack;
3521 
3522         while (!r.empty)
3523         {
3524             resFront ~= r.front;
3525             r.popFront;
3526         }
3527 
3528         while (!rCopy.empty)
3529         {
3530             resBack ~= rCopy.back;
3531             rCopy.popBack;
3532         }
3533 
3534         import std.algorithm.comparison : equal;
3535 
3536         assert(resFront.equal("../../../abc../"));
3537         assert(resBack.equal("../cba../../../"));
3538     }
3539 
3540     {
3541         auto r = ["", "abc", ""].joiner("./");
3542         auto rCopy = r.save;
3543         r.popBack;
3544         rCopy.popFront;
3545 
3546         auto rRev = r.save;
3547         auto rCopyRev = rCopy.save;
3548 
3549         char[] r1, r2, r3, r4;
3550 
3551         while (!r.empty)
3552         {
3553             r1 ~= r.back;
3554             r.popBack;
3555         }
3556 
3557         while (!rCopy.empty)
3558         {
3559             r2 ~= rCopy.front;
3560             rCopy.popFront;
3561         }
3562 
3563         while (!rRev.empty)
3564         {
3565             r3 ~= rRev.front;
3566             rRev.popFront;
3567         }
3568 
3569         while (!rCopyRev.empty)
3570         {
3571             r4 ~= rCopyRev.back;
3572             rCopyRev.popBack;
3573         }
3574 
3575         import std.algorithm.comparison : equal;
3576 
3577         assert(r1.equal("/cba./"));
3578         assert(r2.equal("/abc./"));
3579         assert(r3.equal("./abc"));
3580         assert(r4.equal("./cba"));
3581     }
3582 }
3583 
3584 @system unittest
3585 {
3586     import std.range;
3587     import std.algorithm.comparison : equal;
3588 
3589     assert(inputRangeObject([""]).joiner("lz").equal(""));
3590 }
3591 
3592 @safe pure unittest
3593 {
3594     struct inputRangeStrings
3595     {
3596         private string[] strings;
3597 
3598         string front()
3599         {
3600             return strings[0];
3601         }
3602 
3603         void popFront()
3604         {
3605             strings = strings[1..$];
3606         }
3607 
3608         bool empty() const
3609         {
3610            return strings.length == 0;
3611         }
3612     }
3613 
3614     auto arr = inputRangeStrings([""]);
3615 
3616     import std.algorithm.comparison : equal;
3617 
3618     assert(arr.joiner("./").equal(""));
3619 }
3620 
3621 @safe pure unittest
3622 {
3623     auto r = joiner(["", "", "abc", "", ""], "");
3624     char[] res;
3625     while (!r.empty)
3626     {
3627         res ~= r.back;
3628         r.popBack;
3629     }
3630 
3631     import std.algorithm.comparison : equal;
3632 
3633     assert(res.equal("cba"));
3634 }
3635 
3636 /// Ditto
3637 auto joiner(RoR)(RoR r)
3638 if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)))
3639 {
3640     static struct Result
3641     {
3642     private:
3643         RoR _items;
3644         Unqual!(ElementType!RoR) _current;
3645         enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) &&
3646                                isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR);
3647         static if (isBidirectional)
3648         {
3649             Unqual!(ElementType!RoR) _currentBack;
3650             bool reachedFinalElement;
3651         }
3652 
3653         this(RoR items, ElementType!RoR current)
3654         {
3655             _items = items;
3656             _current = current;
3657             static if (isBidirectional && hasNested!Result)
3658                 _currentBack = typeof(_currentBack).init;
3659         }
3660 
3661         void replaceCurrent(typeof(_current) current) @trusted
3662         {
3663             import core.lifetime : move;
3664 
3665             current.move(_current);
3666         }
3667 
3668         static if (isBidirectional)
3669         {
3670             void replaceCurrentBack(typeof(_currentBack) currentBack) @trusted
3671             {
3672                 import core.lifetime : move;
3673 
3674                 currentBack.move(_currentBack);
3675             }
3676         }
3677 
3678     public:
3679         this(RoR r)
3680         {
3681             _items = r;
3682             // field _current must be initialized in constructor, because it is nested struct
3683             _current = typeof(_current).init;
3684 
3685             static if (isBidirectional && hasNested!Result)
3686                 _currentBack = typeof(_currentBack).init;
3687             mixin(popFrontEmptyElements);
3688             static if (isBidirectional)
3689                 mixin(popBackEmptyElements);
3690         }
3691         static if (isInfinite!RoR)
3692         {
3693             enum bool empty = false;
3694         }
3695         else
3696         {
3697             @property auto empty()
3698             {
3699                 return _items.empty;
3700             }
3701         }
3702         @property auto ref front()
3703         {
3704             assert(!empty, "Attempting to fetch the front of an empty joiner.");
3705             return _current.front;
3706         }
3707         void popFront()
3708         {
3709             assert(!_current.empty, "Attempting to popFront an empty joiner.");
3710             _current.popFront();
3711             if (_current.empty)
3712             {
3713                 assert(!_items.empty, "Attempting to popFront an empty joiner.");
3714                 _items.popFront();
3715                 mixin(popFrontEmptyElements);
3716             }
3717         }
3718 
3719         private enum popFrontEmptyElements = q{
3720             // Skip over empty subranges.
3721             while (!_items.empty && _items.front.empty)
3722             {
3723                 _items.popFront();
3724             }
3725             if (!_items.empty)
3726             {
3727                 // We cannot export .save method unless we ensure subranges are not
3728                 // consumed when a .save'd copy of ourselves is iterated over. So
3729                 // we need to .save each subrange we traverse.
3730                 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
3731                     replaceCurrent(_items.front.save);
3732                 else
3733                     replaceCurrent(_items.front);
3734             }
3735             else
3736             {
3737                 replaceCurrent(typeof(_current).init);
3738             }
3739         };
3740 
3741         static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
3742         {
3743             @property auto save()
3744             {
3745                 // the null check is important if it is a class range, since null.save will segfault; issue #22359
3746                 // could not just compare x is y here without static if due to a compiler assertion failure
3747                 static if (is(typeof(null) : typeof(_current)))
3748                     auto r = Result(_items.save, _current is null ? null : _current.save);
3749                 else
3750                     auto r = Result(_items.save, _current.save);
3751                 static if (isBidirectional)
3752                 {
3753                     static if (is(typeof(null) : typeof(_currentBack)))
3754                         r.replaceCurrentBack(_currentBack is null ? null : _currentBack.save);
3755                     else
3756                         r.replaceCurrentBack(_currentBack.save);
3757                     r.reachedFinalElement = reachedFinalElement;
3758                 }
3759                 return r;
3760             }
3761         }
3762 
3763         static if (hasAssignableElements!(ElementType!RoR))
3764         {
3765             @property void front(ElementType!(ElementType!RoR) element)
3766             {
3767                 assert(!empty, "Attempting to assign to front of an empty joiner.");
3768                 _current.front = element;
3769             }
3770 
3771             @property void front(ref ElementType!(ElementType!RoR) element)
3772             {
3773                 assert(!empty, "Attempting to assign to front of an empty joiner.");
3774                 _current.front = element;
3775             }
3776         }
3777 
3778         static if (isBidirectional)
3779         {
3780             bool checkFinalElement()
3781             {
3782                 import std.range : dropOne;
3783 
3784                 if (reachedFinalElement)
3785                     return true;
3786 
3787                 static if (hasLength!(typeof(_items)))
3788                 {
3789                     if (_items.length == 1)
3790                         reachedFinalElement = true;
3791                 }
3792                 else
3793                 {
3794                     if (_items.save.dropOne.empty)
3795                         reachedFinalElement = true;
3796                 }
3797 
3798                 return false;
3799             }
3800 
3801             @property auto ref back()
3802             {
3803                 assert(!empty, "Attempting to fetch the back of an empty joiner.");
3804                 if (reachedFinalElement)
3805                     return _current.back;
3806                 else
3807                     return _currentBack.back;
3808             }
3809 
3810             void popBack()
3811             {
3812                 assert(!_current.empty, "Attempting to popBack an empty joiner.");
3813                 if (checkFinalElement)
3814                     _current.popBack();
3815                 else
3816                     _currentBack.popBack();
3817 
3818                 bool isEmpty = reachedFinalElement ? _current.empty : _currentBack.empty;
3819                 if (isEmpty)
3820                 {
3821                     assert(!_items.empty, "Attempting to popBack an empty joiner.");
3822                     _items.popBack();
3823                     mixin(popBackEmptyElements);
3824                 }
3825             }
3826 
3827             private enum popBackEmptyElements = q{
3828                 // Skip over empty subranges.
3829                 while (!_items.empty && _items.back.empty)
3830                 {
3831                     _items.popBack();
3832                 }
3833                 if (!_items.empty)
3834                 {
3835                     checkFinalElement;
3836                     // We cannot export .save method unless we ensure subranges are not
3837                     // consumed when a .save'd copy of ourselves is iterated over. So
3838                     // we need to .save each subrange we traverse.
3839                     static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
3840                     {
3841                         if (reachedFinalElement)
3842                             replaceCurrent(_items.back.save);
3843                         else
3844                             replaceCurrentBack(_items.back.save);
3845                     }
3846                     else
3847                     {
3848                         if (reachedFinalElement)
3849                             replaceCurrent(_items.back);
3850                         else
3851                             replaceCurrentBack(_items.back);
3852                     }
3853                 }
3854                 else
3855                 {
3856                     replaceCurrent(typeof(_current).init);
3857                     replaceCurrentBack(typeof(_currentBack).init);
3858                 }
3859             };
3860 
3861             static if (hasAssignableElements!(ElementType!RoR))
3862             {
3863                 @property void back(ElementType!(ElementType!RoR) element)
3864                 {
3865                     assert(!empty, "Attempting to assign to back of an empty joiner.");
3866                     if (reachedFinalElement)
3867                         _current.back = element;
3868                     else
3869                         _currentBack.back = element;
3870                 }
3871 
3872                 @property void back(ref ElementType!(ElementType!RoR) element)
3873                 {
3874                     assert(!empty, "Attempting to assign to back of an empty joiner.");
3875                     if (reachedFinalElement)
3876                         _current.back = element;
3877                     else
3878                         _currentBack.back = element;
3879                 }
3880             }
3881         }
3882     }
3883     return Result(r);
3884 }
3885 
3886 ///
3887 @safe unittest
3888 {
3889     import std.algorithm.comparison : equal;
3890     import std.range : repeat;
3891 
3892     assert([""].joiner.equal(""));
3893     assert(["", ""].joiner.equal(""));
3894     assert(["", "abc"].joiner.equal("abc"));
3895     assert(["abc", ""].joiner.equal("abc"));
3896     assert(["abc", "def"].joiner.equal("abcdef"));
3897     assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb"));
3898     assert("abc".repeat(3).joiner.equal("abcabcabc"));
3899 }
3900 
3901 /// joiner allows in-place mutation!
3902 @safe unittest
3903 {
3904     import std.algorithm.comparison : equal;
3905     auto a = [ [1, 2, 3], [42, 43] ];
3906     auto j = joiner(a);
3907     j.front = 44;
3908     assert(a == [ [44, 2, 3], [42, 43] ]);
3909     assert(equal(j, [44, 2, 3, 42, 43]));
3910 }
3911 
3912 /// insert characters fully lazily into a string
3913 @safe pure unittest
3914 {
3915     import std.algorithm.comparison : equal;
3916     import std.range : chain, cycle, iota, only, retro, take, zip;
3917     import std.format : format;
3918 
3919     static immutable number = "12345678";
3920     static immutable delimiter = ",";
3921     auto formatted = number.retro
3922         .zip(3.iota.cycle.take(number.length))
3923         .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null))
3924         .joiner
3925         .retro;
3926     static immutable expected = "12,345,678";
3927     assert(formatted.equal(expected));
3928 }
3929 
3930 @safe unittest
3931 {
3932     import std.range.interfaces : inputRangeObject;
3933     static assert(isInputRange!(typeof(joiner([""]))));
3934     static assert(isForwardRange!(typeof(joiner([""]))));
3935 }
3936 
3937 @system unittest
3938 {
3939     // this test is system because the virtual interface call to save
3940     // is flexible and thus cannot be inferred safe automatically
3941 
3942     // https://issues.dlang.org/show_bug.cgi?id=22359
3943     import std.range;
3944     ForwardRange!int bug(int[][] r)
3945     {
3946         import std.range : inputRangeObject;
3947         import std.algorithm.iteration : map, joiner;
3948 
3949         auto range = inputRangeObject(r);
3950 
3951         return range.map!(a =>inputRangeObject(a)).joiner.inputRangeObject;
3952     }
3953     auto f = bug([[]]);
3954     f.save(); // should not segfault
3955 }
3956 
3957 @safe unittest
3958 {
3959     // Initial version of PR #6115 caused a compilation failure for
3960     // https://github.com/BlackEdder/ggplotd/blob/d4428c08db5ffdc05dfd29690bf7da9073ea1dc5/source/ggplotd/stat.d#L562-L583
3961     import std.range : zip;
3962     int[] xCoords = [1, 2, 3];
3963     int[] yCoords = [4, 5, 6];
3964     auto coords = zip(xCoords, xCoords[1..$]).map!( (xr) {
3965             return zip(yCoords, yCoords[1..$]).map!( (yr) {
3966                     return [
3967                     [[xr[0], xr[0], xr[1]],
3968                      [yr[0], yr[1], yr[1]]],
3969                     [[xr[0], xr[1], xr[1]],
3970                      [yr[0], yr[0], yr[1]]]
3971                      ];
3972             }).joiner;
3973     }).joiner;
3974 }
3975 
3976 @system unittest
3977 {
3978     import std.algorithm.comparison : equal;
3979     import std.range.interfaces : inputRangeObject;
3980     import std.range : retro;
3981 
3982     // https://issues.dlang.org/show_bug.cgi?id=8240
3983     assert(equal(joiner([inputRangeObject("")]), ""));
3984     assert(equal(joiner([inputRangeObject("")]).retro, ""));
3985 
3986     // https://issues.dlang.org/show_bug.cgi?id=8792
3987     auto b = [[1], [2], [3]];
3988     auto jb = joiner(b);
3989     auto js = jb.save;
3990     assert(equal(jb, js));
3991 
3992     auto js2 = jb.save;
3993     jb.popFront();
3994     assert(!equal(jb, js));
3995     assert(equal(js2, js));
3996     js.popFront();
3997     assert(equal(jb, js));
3998     assert(!equal(js2, js));
3999 }
4000 
4001 // https://issues.dlang.org/show_bug.cgi?id=19213
4002 @system unittest
4003 {
4004     auto results = [[1,2], [3,4]].map!(q => q.chunkBy!"a").joiner;
4005     int i = 1;
4006     foreach (ref e; results)
4007         assert(e[0] == i++);
4008 }
4009 
4010 /// joiner can be bidirectional
4011 @safe unittest
4012 {
4013     import std.algorithm.comparison : equal;
4014     import std.range : retro;
4015 
4016     auto a = [[1, 2, 3], [4, 5]];
4017     auto j = a.joiner;
4018     j.back = 44;
4019     assert(a == [[1, 2, 3], [4, 44]]);
4020     assert(equal(j.retro, [44, 4, 3, 2, 1]));
4021 }
4022 
4023 // bidirectional joiner: test for filtering empty elements
4024 @safe unittest
4025 {
4026     import std.algorithm.comparison : equal;
4027     import std.range : retro;
4028 
4029     alias El = (e) => new int(e);
4030     auto a = [null, [null, El(1), null, El(2), null, El(3), null], null, [null, El(4), null, El(5), null]];
4031     auto j = a.joiner;
4032 
4033     alias deref = a => a is null ? -1 : *a;
4034     auto expected = [-1, 5, -1, 4, -1, -1, 3, -1, 2, -1, 1, -1];
4035     // works with .save.
4036     assert(j.save.retro.map!deref.equal(expected));
4037     // and without .save
4038     assert(j.retro.map!deref.equal(expected));
4039     assert(j.retro.map!deref.equal(expected));
4040 }
4041 
4042 // bidirectional joiner is @nogc
4043 @safe @nogc unittest
4044 {
4045     import std.algorithm.comparison : equal;
4046     import std.range : iota, only, retro;
4047 
4048     auto a = only(iota(1, 4), iota(4, 6));
4049     auto j = a.joiner;
4050     static immutable expected = [5 , 4, 3, 2, 1];
4051     assert(equal(j.retro, expected));
4052 }
4053 
4054 // bidirectional joiner supports assignment to the back
4055 @safe unittest
4056 {
4057     import std.algorithm.comparison : equal;
4058     import std.range : popBackN;
4059 
4060     auto a = [[1, 2, 3], [4, 5]];
4061     auto j = a.joiner;
4062     j.back = 55;
4063     assert(a == [[1, 2, 3], [4, 55]]);
4064     j.popBackN(2);
4065     j.back = 33;
4066     assert(a == [[1, 2, 33], [4, 55]]);
4067 }
4068 
4069 // bidirectional joiner works with auto-decoding
4070 @safe unittest
4071 {
4072     import std.algorithm.comparison : equal;
4073     import std.range : retro;
4074 
4075     auto a = ["😀😐", "😠"];
4076     auto j = a.joiner;
4077     assert(j.retro.equal("😠😐😀"));
4078 }
4079 
4080 // test two-side iteration
4081 @safe unittest
4082 {
4083     import std.algorithm.comparison : equal;
4084     import std.range : popBackN;
4085 
4086     auto arrs = [
4087         [[1], [2], [3], [4], [5]],
4088         [[1], [2, 3, 4], [5]],
4089         [[1, 2, 3, 4, 5]],
4090     ];
4091     foreach (arr; arrs)
4092     {
4093         auto a = arr.joiner;
4094         assert(a.front == 1);
4095         assert(a.back == 5);
4096         a.popFront;
4097         assert(a.front == 2);
4098         assert(a.back == 5);
4099         a.popBack;
4100         assert(a.front == 2);
4101         assert(a.back == 4);
4102         a.popFront;
4103         assert(a.front == 3);
4104         assert(a.back == 4);
4105         a.popBack;
4106         assert(a.front == 3);
4107         assert(a.back == 3);
4108         a.popBack;
4109         assert(a.empty);
4110     }
4111 }
4112 
4113 @safe unittest
4114 {
4115     import std.algorithm.comparison : equal;
4116 
4117     struct TransientRange
4118     {
4119     @safe:
4120         int[] _buf;
4121         int[][] _values;
4122         this(int[][] values)
4123         {
4124             _values = values;
4125             _buf = new int[128];
4126         }
4127         @property bool empty()
4128         {
4129             return _values.length == 0;
4130         }
4131         @property auto front()
4132         {
4133             foreach (i; 0 .. _values.front.length)
4134             {
4135                 _buf[i] = _values[0][i];
4136             }
4137             return _buf[0 .. _values.front.length];
4138         }
4139         void popFront()
4140         {
4141             _values = _values[1 .. $];
4142         }
4143     }
4144 
4145     auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]);
4146 
4147     // Can't use array() or equal() directly because they fail with transient
4148     // .front.
4149     int[] result;
4150     foreach (c; rr.joiner())
4151     {
4152         result ~= c;
4153     }
4154 
4155     assert(equal(result, [1,2,3,4,5,6,7]));
4156 }
4157 
4158 @safe unittest
4159 {
4160     import std.algorithm.comparison : equal;
4161     import std.algorithm.internal : algoFormat;
4162 
4163     struct TransientRange
4164     {
4165     @safe:
4166         dchar[] _buf;
4167         dstring[] _values;
4168         this(dstring[] values)
4169         {
4170             _buf.length = 128;
4171             _values = values;
4172         }
4173         @property bool empty()
4174         {
4175             return _values.length == 0;
4176         }
4177         @property auto front()
4178         {
4179             foreach (i; 0 .. _values.front.length)
4180             {
4181                 _buf[i] = _values[0][i];
4182             }
4183             return _buf[0 .. _values.front.length];
4184         }
4185         void popFront()
4186         {
4187             _values = _values[1 .. $];
4188         }
4189     }
4190 
4191     auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]);
4192 
4193     // Can't use array() or equal() directly because they fail with transient
4194     // .front.
4195     dchar[] result;
4196     foreach (c; rr.joiner())
4197     {
4198         result ~= c;
4199     }
4200 
4201     import std.conv : to;
4202     assert(equal(result, "abc12def34"d),
4203         //Convert to string for assert's message
4204         to!string("Unexpected result: '%s'"d.algoFormat(result)));
4205 }
4206 
4207 // https://issues.dlang.org/show_bug.cgi?id=8061
4208 @system unittest
4209 {
4210     import std.conv : to;
4211     import std.range.interfaces;
4212 
4213     auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]);
4214     assert(isForwardRange!(typeof(r)));
4215 
4216     auto str = to!string(r);
4217     assert(str == "abcd");
4218 }
4219 
4220 @safe unittest
4221 {
4222     import std.range : repeat;
4223 
4224     class AssignableRange
4225     {
4226     @safe:
4227         int element;
4228         @property int front()
4229         {
4230             return element;
4231         }
4232         alias back = front;
4233 
4234         enum empty = false;
4235 
4236         auto save()
4237         {
4238             return this;
4239         }
4240 
4241         void popFront() {}
4242         alias popBack = popFront;
4243 
4244         @property void front(int newValue)
4245         {
4246             element = newValue;
4247         }
4248         alias back = front;
4249     }
4250 
4251     static assert(isInputRange!AssignableRange);
4252     static assert(is(ElementType!AssignableRange == int));
4253     static assert(hasAssignableElements!AssignableRange);
4254     static assert(!hasLvalueElements!AssignableRange);
4255 
4256     auto range = new AssignableRange();
4257     assert(range.element == 0);
4258     {
4259         auto joined = joiner(repeat(range));
4260         joined.front = 5;
4261         assert(range.element == 5);
4262         assert(joined.front == 5);
4263 
4264         joined.popFront;
4265         int byRef = 7;
4266         joined.front = byRef;
4267         assert(range.element == byRef);
4268         assert(joined.front == byRef);
4269     }
4270     {
4271         auto joined = joiner(repeat(range));
4272         joined.back = 5;
4273         assert(range.element == 5);
4274         assert(joined.back == 5);
4275 
4276         joined.popBack;
4277         int byRef = 7;
4278         joined.back = byRef;
4279         assert(range.element == byRef);
4280         assert(joined.back == byRef);
4281     }
4282 }
4283 
4284 // https://issues.dlang.org/show_bug.cgi?id=19850
4285 @safe pure unittest
4286 {
4287     assert([[0]].joiner.save.back == 0);
4288 }
4289 
4290 // https://issues.dlang.org/show_bug.cgi?id=22561
4291 @safe pure unittest
4292 {
4293     import std.range : only;
4294 
4295     static immutable struct S { int[] array; }
4296     assert([only(S(null))].joiner.front == S(null));
4297 }
4298 
4299 // https://issues.dlang.org/show_bug.cgi?id=22785
4300 @safe unittest
4301 {
4302 
4303     import std.algorithm.iteration : joiner, map;
4304     import std.array : array;
4305 
4306     static immutable struct S
4307     {
4308         int value;
4309     }
4310 
4311     static immutable struct T
4312     {
4313         S[] arr;
4314     }
4315 
4316     auto range = [T([S(3)]), T([S(4), S(5)])];
4317 
4318     assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]);
4319 }
4320 
4321 /++
4322 Implements the homonym function (also known as `accumulate`, $(D
4323 compress), `inject`, or `foldl`) present in various programming
4324 languages of functional flavor. There is also $(LREF fold) which does
4325 the same thing but with the opposite parameter order.
4326 The call `reduce!(fun)(seed, range)` first assigns `seed` to
4327 an internal variable `result`, also called the accumulator.
4328 Then, for each element `x` in `range`, `result = fun(result, x)`
4329 gets evaluated. Finally, `result` is returned.
4330 The one-argument version `reduce!(fun)(range)`
4331 works similarly, but it uses the first element of the range as the
4332 seed (the range must be non-empty).
4333 
4334 Returns:
4335     the accumulated `result`
4336 
4337 Params:
4338     fun = one or more functions
4339 
4340 See_Also:
4341     $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
4342 
4343     $(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument
4344     order reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons)
4345     for multiple seeds. This makes it easier to use in UFCS chains.
4346 
4347     $(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers
4348     pairwise summing of floating point numbers.
4349 +/
4350 template reduce(fun...)
4351 if (fun.length >= 1)
4352 {
4353     import std.meta : staticMap;
4354 
4355     alias binfuns = staticMap!(binaryFun, fun);
4356     static if (fun.length > 1)
4357         import std.typecons : tuple, isTuple;
4358 
4359     /++
4360     No-seed version. The first element of `r` is used as the seed's value.
4361 
4362     For each function `f` in `fun`, the corresponding
4363     seed type `S` is `Unqual!(typeof(f(e, e)))`, where `e` is an
4364     element of `r`: `ElementType!R` for ranges,
4365     and `ForeachType!R` otherwise.
4366 
4367     Once S has been determined, then `S s = e;` and `s = f(s, e);`
4368     must both be legal.
4369 
4370     Params:
4371         r = an iterable value as defined by `isIterable`
4372 
4373     Returns:
4374         the final result of the accumulator applied to the iterable
4375 
4376     Throws: `Exception` if `r` is empty
4377     +/
4378     auto reduce(R)(R r)
4379     if (isIterable!R)
4380     {
4381         import std.exception : enforce;
4382         alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
4383         alias Args = staticMap!(ReduceSeedType!E, binfuns);
4384 
4385         static if (isInputRange!R)
4386         {
4387             // no need to throw if range is statically known to be non-empty
4388             static if (!__traits(compiles,
4389             {
4390                 static assert(r.length > 0);
4391             }))
4392                 enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value.");
4393 
4394             Args result = r.front;
4395             r.popFront();
4396             return reduceImpl!false(r, result);
4397         }
4398         else
4399         {
4400             auto result = Args.init;
4401             return reduceImpl!true(r, result);
4402         }
4403     }
4404 
4405     /++
4406     Seed version. The seed should be a single value if `fun` is a
4407     single function. If `fun` is multiple functions, then `seed`
4408     should be a $(REF Tuple, std,typecons), with one field per function in `f`.
4409 
4410     For convenience, if the seed is const, or has qualified fields, then
4411     `reduce` will operate on an unqualified copy. If this happens
4412     then the returned type will not perfectly match `S`.
4413 
4414     Use `fold` instead of `reduce` to use the seed version in a UFCS chain.
4415 
4416     Params:
4417         seed = the initial value of the accumulator
4418         r = an iterable value as defined by `isIterable`
4419 
4420     Returns:
4421         the final result of the accumulator applied to the iterable
4422     +/
4423     auto reduce(S, R)(S seed, R r)
4424     if (isIterable!R)
4425     {
4426         static if (fun.length == 1)
4427             return reducePreImpl(r, seed);
4428         else
4429         {
4430             import std.algorithm.internal : algoFormat;
4431             static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof));
4432             return reducePreImpl(r, seed.expand);
4433         }
4434     }
4435 
4436     private auto reducePreImpl(R, Args...)(R r, ref Args args)
4437     {
4438         alias Result = staticMap!(Unqual, Args);
4439         static if (is(Result == Args))
4440             alias result = args;
4441         else
4442             Result result = args;
4443         return reduceImpl!false(r, result);
4444     }
4445 
4446     private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args)
4447     if (isIterable!R)
4448     {
4449         import std.algorithm.internal : algoFormat;
4450         static assert(Args.length == fun.length,
4451             algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length));
4452         alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
4453 
4454         static if (mustInitialize) bool initialized = false;
4455         foreach (/+auto ref+/ E e; r) // https://issues.dlang.org/show_bug.cgi?id=4707
4456         {
4457             foreach (i, f; binfuns)
4458             {
4459                 static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))),
4460                     algoFormat(
4461                         "Incompatible function/seed/element: %s/%s/%s",
4462                         fullyQualifiedName!f,
4463                         Args[i].stringof,
4464                         E.stringof
4465                     )
4466                 );
4467             }
4468 
4469             static if (mustInitialize) if (initialized == false)
4470             {
4471                 import core.internal.lifetime : emplaceRef;
4472                 foreach (i, f; binfuns)
4473                     emplaceRef!(Args[i])(args[i], e);
4474                 initialized = true;
4475                 continue;
4476             }
4477 
4478             foreach (i, f; binfuns)
4479                 args[i] = f(args[i], e);
4480         }
4481         static if (mustInitialize)
4482         // no need to throw if range is statically known to be non-empty
4483         static if (!__traits(compiles,
4484         {
4485             static assert(r.length > 0);
4486         }))
4487         {
4488             if (!initialized)
4489                 throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value.");
4490         }
4491 
4492         static if (Args.length == 1)
4493             return args[0];
4494         else
4495             return tuple(args);
4496     }
4497 }
4498 
4499 /**
4500 Many aggregate range operations turn out to be solved with `reduce`
4501 quickly and easily. The example below illustrates `reduce`'s
4502 remarkable power and flexibility.
4503 */
4504 @safe unittest
4505 {
4506     import std.algorithm.comparison : max, min;
4507     import std.math.operations : isClose;
4508     import std.range;
4509 
4510     int[] arr = [ 1, 2, 3, 4, 5 ];
4511     // Sum all elements
4512     auto sum = reduce!((a,b) => a + b)(0, arr);
4513     assert(sum == 15);
4514 
4515     // Sum again, using a string predicate with "a" and "b"
4516     sum = reduce!"a + b"(0, arr);
4517     assert(sum == 15);
4518 
4519     // Compute the maximum of all elements
4520     auto largest = reduce!(max)(arr);
4521     assert(largest == 5);
4522 
4523     // Max again, but with Uniform Function Call Syntax (UFCS)
4524     largest = arr.reduce!(max);
4525     assert(largest == 5);
4526 
4527     // Compute the number of odd elements
4528     auto odds = reduce!((a,b) => a + (b & 1))(0, arr);
4529     assert(odds == 3);
4530 
4531     // Compute the sum of squares
4532     auto ssquares = reduce!((a,b) => a + b * b)(0, arr);
4533     assert(ssquares == 55);
4534 
4535     // Chain multiple ranges into seed
4536     int[] a = [ 3, 4 ];
4537     int[] b = [ 100 ];
4538     auto r = reduce!("a + b")(chain(a, b));
4539     assert(r == 107);
4540 
4541     // Mixing convertible types is fair game, too
4542     double[] c = [ 2.5, 3.0 ];
4543     auto r1 = reduce!("a + b")(chain(a, b, c));
4544     assert(isClose(r1, 112.5));
4545 
4546     // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
4547     auto r2 = chain(a, b, c).reduce!("a + b");
4548     assert(isClose(r2, 112.5));
4549 }
4550 
4551 /**
4552 Sometimes it is very useful to compute multiple aggregates in one pass.
4553 One advantage is that the computation is faster because the looping overhead
4554 is shared. That's why `reduce` accepts multiple functions.
4555 If two or more functions are passed, `reduce` returns a
4556 $(REF Tuple, std,typecons) object with one member per passed-in function.
4557 The number of seeds must be correspondingly increased.
4558 */
4559 @safe unittest
4560 {
4561     import std.algorithm.comparison : max, min;
4562     import std.math.operations : isClose;
4563     import std.math.algebraic : sqrt;
4564     import std.typecons : tuple, Tuple;
4565 
4566     double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
4567     // Compute minimum and maximum in one pass
4568     auto r = reduce!(min, max)(a);
4569     // The type of r is Tuple!(int, int)
4570     assert(isClose(r[0], 2));  // minimum
4571     assert(isClose(r[1], 11)); // maximum
4572 
4573     // Compute sum and sum of squares in one pass
4574     r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
4575     assert(isClose(r[0], 35));  // sum
4576     assert(isClose(r[1], 233)); // sum of squares
4577     // Compute average and standard deviation from the above
4578     auto avg = r[0] / a.length;
4579     assert(avg == 5);
4580     auto stdev = sqrt(r[1] / a.length - avg * avg);
4581     assert(cast(int) stdev == 2);
4582 }
4583 
4584 @safe unittest
4585 {
4586     import std.algorithm.comparison : max, min;
4587     import std.range : chain;
4588     import std.typecons : tuple, Tuple;
4589 
4590     double[] a = [ 3, 4 ];
4591     auto r = reduce!("a + b")(0.0, a);
4592     assert(r == 7);
4593     r = reduce!("a + b")(a);
4594     assert(r == 7);
4595     r = reduce!(min)(a);
4596     assert(r == 3);
4597     double[] b = [ 100 ];
4598     auto r1 = reduce!("a + b")(chain(a, b));
4599     assert(r1 == 107);
4600 
4601     // two funs
4602     auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a);
4603     assert(r2[0] == 7 && r2[1] == -7);
4604     auto r3 = reduce!("a + b", "a - b")(a);
4605     assert(r3[0] == 7 && r3[1] == -1);
4606 
4607     a = [ 1, 2, 3, 4, 5 ];
4608     // Stringize with commas
4609     string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a);
4610     assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]");
4611 }
4612 
4613 @safe unittest
4614 {
4615     import std.algorithm.comparison : max, min;
4616     import std.exception : assertThrown;
4617     import std.range : iota;
4618     import std.typecons : tuple, Tuple;
4619 
4620     // Test the opApply case.
4621     static struct OpApply
4622     {
4623         bool actEmpty;
4624 
4625         int opApply(scope int delegate(ref int) @safe dg)
4626         {
4627             int res;
4628             if (actEmpty) return res;
4629 
4630             foreach (i; 0 .. 100)
4631             {
4632                 res = dg(i);
4633                 if (res) break;
4634             }
4635             return res;
4636         }
4637     }
4638 
4639     OpApply oa;
4640     auto hundredSum = reduce!"a + b"(iota(100));
4641     assert(reduce!"a + b"(5, oa) == hundredSum + 5);
4642     assert(reduce!"a + b"(oa) == hundredSum);
4643     assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99));
4644     assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99));
4645 
4646     // Test for throwing on empty range plus no seed.
4647     assertThrown(reduce!"a + b"([1, 2][0 .. 0]));
4648 
4649     oa.actEmpty = true;
4650     assertThrown(reduce!"a + b"(oa));
4651 }
4652 
4653 @safe unittest
4654 {
4655     const float a = 0.0;
4656     const float[] b = [ 1.2, 3, 3.3 ];
4657     float[] c = [ 1.2, 3, 3.3 ];
4658     auto r = reduce!"a + b"(a, b);
4659     r = reduce!"a + b"(a, c);
4660     assert(r == 7.5);
4661 }
4662 
4663 @safe unittest
4664 {
4665     // https://issues.dlang.org/show_bug.cgi?id=10408
4666     // Two-function reduce of a const array.
4667     import std.algorithm.comparison : max, min;
4668     import std.typecons : tuple, Tuple;
4669 
4670     const numbers = [10, 30, 20];
4671     immutable m = reduce!(min)(numbers);
4672     assert(m == 10);
4673     immutable minmax = reduce!(min, max)(numbers);
4674     assert(minmax == tuple(10, 30));
4675 }
4676 
4677 @safe unittest
4678 {
4679     // https://issues.dlang.org/show_bug.cgi?id=10709
4680     import std.typecons : tuple, Tuple;
4681 
4682     enum foo = "a + 0.5 * b";
4683     auto r = [0, 1, 2, 3];
4684     auto r1 = reduce!foo(r);
4685     auto r2 = reduce!(foo, foo)(r);
4686     assert(r1 == 3);
4687     assert(r2 == tuple(3, 3));
4688 }
4689 
4690 @safe unittest
4691 {
4692     static struct OpApply
4693     {
4694         int opApply(int delegate(ref int) @safe dg)
4695         {
4696             int[] a = [1, 2, 3];
4697 
4698             int res = 0;
4699             foreach (ref e; a)
4700             {
4701                 res = dg(e);
4702                 if (res) break;
4703             }
4704             return res;
4705         }
4706     }
4707     //test CTFE and functions with context
4708     int fun(int a, int b) @safe {return a + b + 1;}
4709     auto foo()
4710     {
4711         import std.algorithm.comparison : max;
4712         import std.typecons : tuple, Tuple;
4713 
4714         auto a = reduce!(fun)([1, 2, 3]);
4715         auto b = reduce!(fun, fun)([1, 2, 3]);
4716         auto c = reduce!(fun)(0, [1, 2, 3]);
4717         auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]);
4718         auto e = reduce!(fun)(0, OpApply());
4719         auto f = reduce!(fun, fun)(tuple(0, 0), OpApply());
4720 
4721         return max(a, b.expand, c, d.expand, e, f.expand);
4722     }
4723     auto a = foo();
4724     assert(a == 9);
4725     enum b = foo();
4726     assert(b == 9);
4727 }
4728 
4729 @safe unittest
4730 {
4731     import std.algorithm.comparison : max, min;
4732     import std.typecons : tuple, Tuple;
4733 
4734     //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org
4735     //Seed is tuple of const.
4736     static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
4737     @safe pure nothrow
4738     if (isInputRange!R)
4739     {
4740         return reduce!(F, G)(tuple(ElementType!R.max,
4741                                    ElementType!R.min), range);
4742     }
4743     assert(minmaxElement([1, 2, 3]) == tuple(1, 3));
4744 }
4745 
4746 // https://issues.dlang.org/show_bug.cgi?id=12569
4747 @safe unittest
4748 {
4749     import std.algorithm.comparison : max, min;
4750     import std.typecons : tuple;
4751     dchar c = 'a';
4752     reduce!(min, max)(tuple(c, c), "hello"); // OK
4753     static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
4754     static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
4755 
4756 
4757     //"Seed dchar should be a Tuple"
4758     static assert(!is(typeof(reduce!(min, max)(c, "hello"))));
4759     //"Seed (dchar) does not have the correct amount of fields (should be 2)"
4760     static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
4761     //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
4762     static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
4763     //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar"
4764     static assert(!is(typeof(reduce!all(1, "hello"))));
4765     static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello"))));
4766 }
4767 
4768 // https://issues.dlang.org/show_bug.cgi?id=13304
4769 @safe unittest
4770 {
4771     int[] data;
4772     static assert(is(typeof(reduce!((a, b) => a + b)(data))));
4773     assert(data.length == 0);
4774 }
4775 
4776 // https://issues.dlang.org/show_bug.cgi?id=13880
4777 // reduce shouldn't throw if the length is statically known
4778 pure nothrow @safe @nogc unittest
4779 {
4780     import std.algorithm.comparison : min;
4781     int[5] arr;
4782     arr[2] = -1;
4783     assert(arr.reduce!min == -1);
4784 
4785     int[0] arr0;
4786     assert(reduce!min(42, arr0) == 42);
4787 }
4788 
4789 //Helper for Reduce
4790 private template ReduceSeedType(E)
4791 {
4792     static template ReduceSeedType(alias fun)
4793     {
4794         import std.algorithm.internal : algoFormat;
4795 
4796         alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E)));
4797 
4798         //Check the Seed type is useable.
4799         ReduceSeedType s = ReduceSeedType.init;
4800         static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) &&
4801             is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))),
4802             algoFormat(
4803                 "Unable to deduce an acceptable seed type for %s with element type %s.",
4804                 fullyQualifiedName!fun,
4805                 E.stringof
4806             )
4807         );
4808     }
4809 }
4810 
4811 
4812 /++
4813 Implements the homonym function (also known as `accumulate`, $(D
4814 compress), `inject`, or `foldl`) present in various programming
4815 languages of functional flavor, iteratively calling one or more predicates.
4816 
4817 $(P Each predicate in `fun` must take two arguments:)
4818 * An accumulator value
4819 * An element of the range `r`
4820 $(P Each predicate must return a value which implicitly converts to the
4821 type of the accumulator.)
4822 
4823 $(P For a single predicate,
4824 the call `fold!(fun)(range, seed)` will:)
4825 
4826 * Use `seed` to initialize an internal variable `result` (also called
4827   the accumulator).
4828 * For each element `e` in $(D range), evaluate `result = fun(result, e)`.
4829 * Return $(D result).
4830 
4831 $(P The one-argument version `fold!(fun)(range)`
4832 works similarly, but it uses the first element of the range as the
4833 seed (the range must be non-empty) and iterates over the remaining
4834 elements.)
4835 
4836 Multiple results are produced when using multiple predicates.
4837 
4838 Params:
4839     fun = the predicate function(s) to apply to the elements
4840 
4841 See_Also:
4842     * $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
4843 
4844     * $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
4845       precise summing of floating point numbers.
4846 
4847     * `fold` is functionally equivalent to $(LREF reduce) with the argument order
4848       reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons)
4849       for multiple seeds.
4850 +/
4851 template fold(fun...)
4852 if (fun.length >= 1)
4853 {
4854     /**
4855     Params:
4856         r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to fold
4857         seeds = the initial values of each accumulator (optional), one for each predicate
4858     Returns:
4859         Either the accumulated result for a single predicate, or a
4860         $(REF_ALTTEXT `Tuple`,Tuple,std,typecons) of results.
4861      */
4862     auto fold(R, S...)(R r, S seeds)
4863     {
4864         static if (S.length < 2)
4865         {
4866             return reduce!fun(seeds, r);
4867         }
4868         else
4869         {
4870             import std.typecons : tuple;
4871             return reduce!fun(tuple(seeds), r);
4872         }
4873     }
4874 }
4875 
4876 ///
4877 @safe pure unittest
4878 {
4879     immutable arr = [1, 2, 3, 4, 5];
4880 
4881     // Sum all elements
4882     assert(arr.fold!((a, e) => a + e) == 15);
4883 
4884     // Sum all elements with explicit seed
4885     assert(arr.fold!((a, e) => a + e)(6) == 21);
4886 
4887     import std.algorithm.comparison : min, max;
4888     import std.typecons : tuple;
4889 
4890     // Compute minimum and maximum at the same time
4891     assert(arr.fold!(min, max) == tuple(1, 5));
4892 
4893     // Compute minimum and maximum at the same time with seeds
4894     assert(arr.fold!(min, max)(0, 7) == tuple(0, 7));
4895 
4896     // Can be used in a UFCS chain
4897     assert(arr.map!(a => a + 1).fold!((a, e) => a + e) == 20);
4898 
4899     // Return the last element of any range
4900     assert(arr.fold!((a, e) => e) == 5);
4901 }
4902 
4903 @safe @nogc pure nothrow unittest
4904 {
4905     int[1] arr;
4906     static assert(!is(typeof(arr.fold!())));
4907     static assert(!is(typeof(arr.fold!(a => a))));
4908     static assert(is(typeof(arr.fold!((a, b) => a))));
4909     static assert(is(typeof(arr.fold!((a, b) => a)(1))));
4910     assert(arr.length == 1);
4911 }
4912 
4913 /++
4914 Similar to `fold`, but returns a range containing the successive reduced values.
4915 The call `cumulativeFold!(fun)(range, seed)` first assigns `seed` to an
4916 internal variable `result`, also called the accumulator.
4917 The returned range contains the values `result = fun(result, x)` lazily
4918 evaluated for each element `x` in `range`. Finally, the last element has the
4919 same value as `fold!(fun)(seed, range)`.
4920 The one-argument version `cumulativeFold!(fun)(range)` works similarly, but
4921 it returns the first element unchanged and uses it as seed for the next
4922 elements.
4923 This function is also known as
4924     $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum),
4925     $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate),
4926     $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan),
4927     $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum).
4928 
4929 Params:
4930     fun = one or more functions to use as fold operation
4931 
4932 Returns:
4933     The function returns a range containing the consecutive reduced values. If
4934     there is more than one `fun`, the element type will be $(REF Tuple,
4935     std,typecons) containing one element for each `fun`.
4936 
4937 See_Also:
4938     $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum)
4939 
4940 Note:
4941 
4942     In functional programming languages this is typically called `scan`, `scanl`,
4943     `scanLeft` or `reductions`.
4944 +/
4945 template cumulativeFold(fun...)
4946 if (fun.length >= 1)
4947 {
4948     import std.meta : staticMap;
4949     private alias binfuns = staticMap!(binaryFun, fun);
4950 
4951     /++
4952     No-seed version. The first element of `r` is used as the seed's value.
4953     For each function `f` in `fun`, the corresponding seed type `S` is
4954     `Unqual!(typeof(f(e, e)))`, where `e` is an element of `r`:
4955     `ElementType!R`.
4956     Once `S` has been determined, then `S s = e;` and `s = f(s, e);` must
4957     both be legal.
4958 
4959     Params:
4960         range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
4961     Returns:
4962         a range containing the consecutive reduced values.
4963     +/
4964     auto cumulativeFold(R)(R range)
4965     if (isInputRange!(Unqual!R))
4966     {
4967         return cumulativeFoldImpl(range);
4968     }
4969 
4970     /++
4971     Seed version. The seed should be a single value if `fun` is a single
4972     function. If `fun` is multiple functions, then `seed` should be a
4973     $(REF Tuple, std,typecons), with one field per function in `f`.
4974     For convenience, if the seed is `const`, or has qualified fields, then
4975     `cumulativeFold` will operate on an unqualified copy. If this happens
4976     then the returned type will not perfectly match `S`.
4977 
4978     Params:
4979         range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
4980         seed = the initial value of the accumulator
4981     Returns:
4982         a range containing the consecutive reduced values.
4983     +/
4984     auto cumulativeFold(R, S)(R range, S seed)
4985     if (isInputRange!(Unqual!R))
4986     {
4987         static if (fun.length == 1)
4988             return cumulativeFoldImpl(range, seed);
4989         else
4990             return cumulativeFoldImpl(range, seed.expand);
4991     }
4992 
4993     private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args)
4994     {
4995         import std.algorithm.internal : algoFormat;
4996 
4997         static assert(Args.length == 0 || Args.length == fun.length,
4998             algoFormat("Seed %s does not have the correct amount of fields (should be %s)",
4999                 Args.stringof, fun.length));
5000 
5001         static if (args.length)
5002             alias State = staticMap!(Unqual, Args);
5003         else
5004             alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns);
5005 
5006         foreach (i, f; binfuns)
5007         {
5008             static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles,
5009                     { args[i] = f(args[i], e); }()),
5010                 algoFormat("Incompatible function/seed/element: %s/%s/%s",
5011                     fullyQualifiedName!f, Args[i].stringof, E.stringof));
5012         }
5013 
5014         static struct Result
5015         {
5016         private:
5017             R source;
5018             State state;
5019 
5020             this(R range, ref Args args)
5021             {
5022                 source = range;
5023                 if (source.empty)
5024                     return;
5025 
5026                 foreach (i, f; binfuns)
5027                 {
5028                     static if (args.length)
5029                         state[i] = f(args[i], source.front);
5030                     else
5031                         state[i] = source.front;
5032                 }
5033             }
5034 
5035         public:
5036             @property bool empty()
5037             {
5038                 return source.empty;
5039             }
5040 
5041             @property auto front()
5042             {
5043                 assert(!empty, "Attempting to fetch the front of an empty cumulativeFold.");
5044                 static if (fun.length > 1)
5045                 {
5046                     import std.typecons : tuple;
5047                     return tuple(state);
5048                 }
5049                 else
5050                 {
5051                     return state[0];
5052                 }
5053             }
5054 
5055             void popFront()
5056             {
5057                 assert(!empty, "Attempting to popFront an empty cumulativeFold.");
5058                 source.popFront;
5059 
5060                 if (source.empty)
5061                     return;
5062 
5063                 foreach (i, f; binfuns)
5064                     state[i] = f(state[i], source.front);
5065             }
5066 
5067             static if (isForwardRange!R)
5068             {
5069                 @property auto save()
5070                 {
5071                     auto result = this;
5072                     result.source = source.save;
5073                     return result;
5074                 }
5075             }
5076 
5077             mixin ImplementLength!source;
5078         }
5079 
5080         return Result(range, args);
5081     }
5082 }
5083 
5084 ///
5085 @safe unittest
5086 {
5087     import std.algorithm.comparison : max, min;
5088     import std.array : array;
5089     import std.math.operations : isClose;
5090     import std.range : chain;
5091 
5092     int[] arr = [1, 2, 3, 4, 5];
5093     // Partial sum of all elements
5094     auto sum = cumulativeFold!((a, b) => a + b)(arr, 0);
5095     assert(sum.array == [1, 3, 6, 10, 15]);
5096 
5097     // Partial sum again, using a string predicate with "a" and "b"
5098     auto sum2 = cumulativeFold!"a + b"(arr, 0);
5099     assert(sum2.array == [1, 3, 6, 10, 15]);
5100 
5101     // Compute the partial maximum of all elements
5102     auto largest = cumulativeFold!max(arr);
5103     assert(largest.array == [1, 2, 3, 4, 5]);
5104 
5105     // Partial max again, but with Uniform Function Call Syntax (UFCS)
5106     largest = arr.cumulativeFold!max;
5107     assert(largest.array == [1, 2, 3, 4, 5]);
5108 
5109     // Partial count of odd elements
5110     auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0);
5111     assert(odds.array == [1, 1, 2, 2, 3]);
5112 
5113     // Compute the partial sum of squares
5114     auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0);
5115     assert(ssquares.array == [1, 5, 14, 30, 55]);
5116 
5117     // Chain multiple ranges into seed
5118     int[] a = [3, 4];
5119     int[] b = [100];
5120     auto r = cumulativeFold!"a + b"(chain(a, b));
5121     assert(r.array == [3, 7, 107]);
5122 
5123     // Mixing convertible types is fair game, too
5124     double[] c = [2.5, 3.0];
5125     auto r1 = cumulativeFold!"a + b"(chain(a, b, c));
5126     assert(isClose(r1, [3, 7, 107, 109.5, 112.5]));
5127 
5128     // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
5129     auto r2 = chain(a, b, c).cumulativeFold!"a + b";
5130     assert(isClose(r2, [3, 7, 107, 109.5, 112.5]));
5131 }
5132 
5133 /**
5134 Sometimes it is very useful to compute multiple aggregates in one pass.
5135 One advantage is that the computation is faster because the looping overhead
5136 is shared. That's why `cumulativeFold` accepts multiple functions.
5137 If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple,
5138 std,typecons) object with one member per passed-in function.
5139 The number of seeds must be correspondingly increased.
5140 */
5141 @safe unittest
5142 {
5143     import std.algorithm.comparison : max, min;
5144     import std.algorithm.iteration : map;
5145     import std.math.operations : isClose;
5146     import std.typecons : tuple;
5147 
5148     double[] a = [3.0, 4, 7, 11, 3, 2, 5];
5149     // Compute minimum and maximum in one pass
5150     auto r = a.cumulativeFold!(min, max);
5151     // The type of r is Tuple!(int, int)
5152     assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2]));     // minimum
5153     assert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
5154 
5155     // Compute sum and sum of squares in one pass
5156     auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0));
5157     assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35]));      // sum
5158     assert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
5159 }
5160 
5161 @safe unittest
5162 {
5163     import std.algorithm.comparison : equal, max, min;
5164     import std.conv : to;
5165     import std.range : chain;
5166     import std.typecons : tuple;
5167 
5168     double[] a = [3, 4];
5169     auto r = a.cumulativeFold!("a + b")(0.0);
5170     assert(r.equal([3, 7]));
5171     auto r2 = cumulativeFold!("a + b")(a);
5172     assert(r2.equal([3, 7]));
5173     auto r3 = cumulativeFold!(min)(a);
5174     assert(r3.equal([3, 3]));
5175     double[] b = [100];
5176     auto r4 = cumulativeFold!("a + b")(chain(a, b));
5177     assert(r4.equal([3, 7, 107]));
5178 
5179     // two funs
5180     auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0));
5181     assert(r5.equal([tuple(3, -3), tuple(7, -7)]));
5182     auto r6 = cumulativeFold!("a + b", "a - b")(a);
5183     assert(r6.equal([tuple(3, 3), tuple(7, -1)]));
5184 
5185     a = [1, 2, 3, 4, 5];
5186     // Stringize with commas
5187     auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, "");
5188     assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"]));
5189 
5190     // Test for empty range
5191     a = [];
5192     assert(a.cumulativeFold!"a + b".empty);
5193     assert(a.cumulativeFold!"a + b"(2.0).empty);
5194 }
5195 
5196 @safe unittest
5197 {
5198     import std.algorithm.comparison : max, min;
5199     import std.array : array;
5200     import std.math.operations : isClose;
5201     import std.typecons : tuple;
5202 
5203     const float a = 0.0;
5204     const float[] b = [1.2, 3, 3.3];
5205     float[] c = [1.2, 3, 3.3];
5206 
5207     auto r = cumulativeFold!"a + b"(b, a);
5208     assert(isClose(r, [1.2, 4.2, 7.5]));
5209 
5210     auto r2 = cumulativeFold!"a + b"(c, a);
5211     assert(isClose(r2, [1.2, 4.2, 7.5]));
5212 
5213     const numbers = [10, 30, 20];
5214     enum m = numbers.cumulativeFold!(min).array;
5215     assert(m == [10, 10, 10]);
5216     enum minmax = numbers.cumulativeFold!(min, max).array;
5217     assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]);
5218 }
5219 
5220 @safe unittest
5221 {
5222     import std.math.operations : isClose;
5223     import std.typecons : tuple;
5224 
5225     enum foo = "a + 0.5 * b";
5226     auto r = [0, 1, 2, 3];
5227     auto r1 = r.cumulativeFold!foo;
5228     auto r2 = r.cumulativeFold!(foo, foo);
5229     assert(isClose(r1, [0, 0.5, 1.5, 3]));
5230     assert(isClose(r2.map!"a[0]", [0, 0.5, 1.5, 3]));
5231     assert(isClose(r2.map!"a[1]", [0, 0.5, 1.5, 3]));
5232 }
5233 
5234 @safe unittest
5235 {
5236     import std.algorithm.comparison : equal, max, min;
5237     import std.array : array;
5238     import std.typecons : tuple;
5239 
5240     //Seed is tuple of const.
5241     static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
5242     @safe pure nothrow
5243     if (isInputRange!R)
5244     {
5245         return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min));
5246     }
5247 
5248     assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)]));
5249 }
5250 
5251 // https://issues.dlang.org/show_bug.cgi?id=12569
5252 @safe unittest
5253 {
5254     import std.algorithm.comparison : equal, max, min;
5255     import std.typecons : tuple;
5256 
5257     dchar c = 'a';
5258 
5259     assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'),
5260         tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')]));
5261     static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
5262     static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
5263 
5264     //"Seed dchar should be a Tuple"
5265     static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c)));
5266     //"Seed (dchar) does not have the correct amount of fields (should be 2)"
5267     static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
5268     //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
5269     static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
5270     //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar"
5271     static assert(!__traits(compiles, cumulativeFold!all("hello", 1)));
5272     static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1))));
5273 }
5274 
5275 // https://issues.dlang.org/show_bug.cgi?id=13304
5276 @safe unittest
5277 {
5278     int[] data;
5279     assert(data.cumulativeFold!((a, b) => a + b).empty);
5280 }
5281 
5282 @safe unittest
5283 {
5284     import std.algorithm.comparison : equal;
5285     import std.internal.test.dummyrange : AllDummyRanges, propagatesLength,
5286         propagatesRangeType, RangeType;
5287 
5288     foreach (DummyType; AllDummyRanges)
5289     {
5290         DummyType d;
5291         auto m = d.cumulativeFold!"a * b";
5292 
5293         static assert(propagatesLength!(typeof(m), DummyType));
5294         static if (DummyType.rt <= RangeType.Forward)
5295             static assert(propagatesRangeType!(typeof(m), DummyType));
5296 
5297         assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800]));
5298     }
5299 }
5300 
5301 // splitter
5302 /**
5303 Lazily splits a range using an element or range as a separator.
5304 Separator ranges can be any narrow string type or sliceable range type.
5305 
5306 Two adjacent separators are considered to surround an empty element in
5307 the split range. Use `filter!(a => !a.empty)` on the result to compress
5308 empty elements.
5309 
5310 The predicate is passed to $(REF binaryFun, std,functional) and accepts
5311 any callable function that can be executed via `pred(element, s)`.
5312 
5313 Notes:
5314     If splitting a string on whitespace and token compression is desired,
5315     consider using `splitter` without specifying a separator.
5316 
5317     If no separator is passed, the $(REF_ALTTEXT, unary, unaryFun, std,functional)
5318     predicate `isTerminator` decides whether to accept an element of `r`.
5319 
5320 Params:
5321     pred = The predicate for comparing each element with the separator,
5322         defaulting to `"a == b"`.
5323     r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
5324         split. Must support slicing and `.length` or be a narrow string type.
5325     s = The element (or range) to be treated as the separator
5326         between range segments to be split.
5327     isTerminator = The predicate for deciding where to split the range when no separator is passed
5328     keepSeparators = The flag for deciding if the separators are kept
5329 
5330 Constraints:
5331     The predicate `pred` needs to accept an element of `r` and the
5332     separator `s`.
5333 
5334 Returns:
5335     An input range of the subranges of elements between separators. If `r`
5336     is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
5337     or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
5338     the returned range will be likewise.
5339     When a range is used a separator, bidirectionality isn't possible.
5340 
5341     If keepSeparators is equal to Yes.keepSeparators the output will also contain the
5342     separators.
5343 
5344     If an empty range is given, the result is an empty range. If a range with
5345     one separator is given, the result is a range with two empty elements.
5346 
5347 See_Also:
5348  $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator,
5349  $(REF _split, std,array) for a version that splits eagerly and
5350  $(LREF splitWhen), which compares adjacent elements instead of element against separator.
5351 */
5352 auto splitter(alias pred = "a == b",
5353               Flag!"keepSeparators" keepSeparators = No.keepSeparators,
5354               Range,
5355               Separator)(Range r, Separator s)
5356 if (is(typeof(binaryFun!pred(r.front, s)) : bool)
5357         && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
5358 {
5359     import std.algorithm.searching : find;
5360     import std.conv : unsigned;
5361 
5362     struct Result
5363     {
5364     private:
5365         Range _input;
5366         Separator _separator;
5367         // Do we need hasLength!Range? popFront uses _input.length...
5368         enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max;
5369         size_t _frontLength = _unComputed;
5370         size_t _backLength = _unComputed;
5371 
5372         static if (isNarrowString!Range)
5373         {
5374             size_t _separatorLength;
5375         }
5376         else
5377         {
5378             enum _separatorLength = 1;
5379         }
5380 
5381         static if (keepSeparators)
5382         {
5383             bool _wasSeparator = true;
5384         }
5385 
5386         static if (isBidirectionalRange!Range)
5387         {
5388             size_t lastIndexOf(Range haystack, Separator needle)
5389             {
5390                 import std.range : retro;
5391                 auto r = haystack.retro().find!pred(needle);
5392                 return r.retro().length - 1;
5393             }
5394         }
5395 
5396     public:
5397         this(Range input, Separator separator)
5398         {
5399             _input = input;
5400             _separator = separator;
5401 
5402             static if (isNarrowString!Range)
5403             {
5404                 import std.utf : codeLength;
5405 
5406                 _separatorLength = codeLength!(ElementEncodingType!Range)(separator);
5407             }
5408             if (_input.empty)
5409                 _frontLength = _atEnd;
5410         }
5411 
5412         static if (isInfinite!Range)
5413         {
5414             enum bool empty = false;
5415         }
5416         else
5417         {
5418             @property bool empty()
5419             {
5420                 return _frontLength == _atEnd;
5421             }
5422         }
5423 
5424         @property Range front()
5425         {
5426             assert(!empty, "Attempting to fetch the front of an empty splitter.");
5427             static if (keepSeparators)
5428             {
5429                 if (!_wasSeparator)
5430                 {
5431                     _frontLength = _separatorLength;
5432                     _wasSeparator = true;
5433                 }
5434                 else if (_frontLength == _unComputed)
5435                 {
5436                     auto r = _input.find!pred(_separator);
5437                     _frontLength = _input.length - r.length;
5438                     _wasSeparator = false;
5439                 }
5440             }
5441             else
5442             {
5443                 if (_frontLength == _unComputed)
5444                 {
5445                     auto r = _input.find!pred(_separator);
5446                     _frontLength = _input.length - r.length;
5447                 }
5448             }
5449             return _input[0 .. _frontLength];
5450         }
5451 
5452         void popFront()
5453         {
5454             assert(!empty, "Attempting to popFront an empty splitter.");
5455             if (_frontLength == _unComputed)
5456             {
5457                 front;
5458             }
5459             assert(_frontLength <= _input.length, "The front position must"
5460                     ~ " not exceed the input.length");
5461             static if (keepSeparators)
5462             {
5463                 if (_frontLength == _input.length && !_wasSeparator)
5464                 {
5465                     _frontLength = _atEnd;
5466 
5467                     _backLength = _atEnd;
5468                 }
5469                 else
5470                 {
5471                     _input = _input[_frontLength .. _input.length];
5472                     _frontLength = _unComputed;
5473                 }
5474             }
5475             else
5476             {
5477                 if (_frontLength == _input.length)
5478                 {
5479                     // no more input and need to fetch => done
5480                     _frontLength = _atEnd;
5481 
5482                     // Probably don't need this, but just for consistency:
5483                     _backLength = _atEnd;
5484                 }
5485                 else
5486                 {
5487                     _input = _input[_frontLength + _separatorLength .. _input.length];
5488                     _frontLength = _unComputed;
5489                 }
5490             }
5491         }
5492 
5493         static if (isForwardRange!Range)
5494         {
5495             @property typeof(this) save()
5496             {
5497                 auto ret = this;
5498                 ret._input = _input.save;
5499                 return ret;
5500             }
5501         }
5502 
5503         static if (isBidirectionalRange!Range)
5504         {
5505             @property Range back()
5506             {
5507                 assert(!empty, "Attempting to fetch the back of an empty splitter.");
5508                 static if (keepSeparators)
5509                 {
5510                     if (!_wasSeparator)
5511                     {
5512                         _backLength = _separatorLength;
5513                         _wasSeparator = true;
5514                     }
5515                     else if (_backLength == _unComputed)
5516                     {
5517                         immutable lastIndex = lastIndexOf(_input, _separator);
5518                         if (lastIndex == -1)
5519                         {
5520                             _backLength = _input.length;
5521                         }
5522                         else
5523                         {
5524                             _backLength = _input.length - lastIndex - 1;
5525                         }
5526                         _wasSeparator = false;
5527                     }
5528                 }
5529                 else
5530                 {
5531                     if (_backLength == _unComputed)
5532                     {
5533                         immutable lastIndex = lastIndexOf(_input, _separator);
5534                         if (lastIndex == -1)
5535                         {
5536                             _backLength = _input.length;
5537                         }
5538                         else
5539                         {
5540                             _backLength = _input.length - lastIndex - 1;
5541                         }
5542                     }
5543                 }
5544                 return _input[_input.length - _backLength .. _input.length];
5545             }
5546 
5547             void popBack()
5548             {
5549                 assert(!empty, "Attempting to popBack an empty splitter.");
5550                 if (_backLength == _unComputed)
5551                 {
5552                     // evaluate back to make sure it's computed
5553                     back;
5554                 }
5555                 assert(_backLength <= _input.length, "The end index must not"
5556                         ~ " exceed the length of the input");
5557                 static if (keepSeparators)
5558                 {
5559                     if (_backLength == _input.length && !_wasSeparator)
5560                     {
5561                         _frontLength = _atEnd;
5562                         _backLength = _atEnd;
5563                     }
5564                     else
5565                     {
5566                         _input = _input[0 .. _input.length - _backLength];
5567                         _backLength = _unComputed;
5568                     }
5569                 }
5570                 else
5571                 {
5572                     if (_backLength == _input.length)
5573                     {
5574                         // no more input and need to fetch => done
5575                         _frontLength = _atEnd;
5576                         _backLength = _atEnd;
5577                     }
5578                     else
5579                     {
5580                         _input = _input[0 .. _input.length - _backLength - _separatorLength];
5581                         _backLength = _unComputed;
5582                     }
5583                 }
5584             }
5585         }
5586     }
5587 
5588     return Result(r, s);
5589 }
5590 
5591 /// Basic splitting with characters and numbers.
5592 @safe unittest
5593 {
5594     import std.algorithm.comparison : equal;
5595 
5596     assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ]));
5597 
5598     int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
5599     int[][] w = [ [1], [2, 3], [4, 5, 6] ];
5600     assert(a.splitter(0).equal(w));
5601 }
5602 
5603 /// Basic splitting with characters and numbers and keeping sentinels.
5604 @safe unittest
5605 {
5606     import std.algorithm.comparison : equal;
5607     import std.typecons : Yes;
5608 
5609     assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
5610         .equal([ "a", "|", "bc", "|", "def" ]));
5611 
5612     int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
5613     int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];
5614     assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
5615 }
5616 
5617 /// Adjacent separators.
5618 @safe unittest
5619 {
5620     import std.algorithm.comparison : equal;
5621 
5622     assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
5623     assert("ab".splitter('|').equal([ "ab" ]));
5624 
5625     assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ]));
5626     assert("hello  world".splitter(' ').equal([ "hello", "", "world" ]));
5627 
5628     auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5629     auto w = [ [1, 2], [], [3], [4, 5], [] ];
5630     assert(a.splitter(0).equal(w));
5631 }
5632 
5633 /// Adjacent separators and keeping sentinels.
5634 @safe unittest
5635 {
5636     import std.algorithm.comparison : equal;
5637     import std.typecons : Yes;
5638 
5639     assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
5640         .equal([ "", "|", "ab", "|", "" ]));
5641     assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
5642         .equal([ "ab" ]));
5643 
5644     assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|')
5645         .equal([ "a", "|", "b", "|", "", "|", "c" ]));
5646     assert("hello  world".splitter!("a == b", Yes.keepSeparators)(' ')
5647         .equal([ "hello", " ", "", " ", "world" ]));
5648 
5649     auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5650     auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];
5651     assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
5652 }
5653 
5654 /// Empty and separator-only ranges.
5655 @safe unittest
5656 {
5657     import std.algorithm.comparison : equal;
5658     import std.range : empty;
5659 
5660     assert("".splitter('|').empty);
5661     assert("|".splitter('|').equal([ "", "" ]));
5662     assert("||".splitter('|').equal([ "", "", "" ]));
5663 }
5664 
5665 /// Empty and separator-only ranges and keeping sentinels.
5666 @safe unittest
5667 {
5668     import std.algorithm.comparison : equal;
5669     import std.typecons : Yes;
5670     import std.range : empty;
5671 
5672     assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);
5673     assert("|".splitter!("a == b", Yes.keepSeparators)('|')
5674         .equal([ "", "|", "" ]));
5675     assert("||".splitter!("a == b", Yes.keepSeparators)('|')
5676         .equal([ "", "|", "", "|", "" ]));
5677 }
5678 
5679 /// Use a range for splitting
5680 @safe unittest
5681 {
5682     import std.algorithm.comparison : equal;
5683 
5684     assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ]));
5685     assert("a|b||c".splitter("||").equal([ "a|b", "c" ]));
5686     assert("hello  world".splitter("  ").equal([ "hello", "world" ]));
5687 
5688     int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5689     int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
5690     assert(a.splitter([0, 0]).equal(w));
5691 
5692     a = [ 0, 0 ];
5693     assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));
5694 
5695     a = [ 0, 0, 1 ];
5696     assert(a.splitter([0, 0]).equal([ [], [1] ]));
5697 }
5698 
5699 /// Use a range for splitting
5700 @safe unittest
5701 {
5702     import std.algorithm.comparison : equal;
5703     import std.typecons : Yes;
5704 
5705     assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>")
5706         .equal([ "a", "=>", "bc", "=>", "def" ]));
5707     assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||")
5708         .equal([ "a|b", "||", "c" ]));
5709     assert("hello  world".splitter!("a == b", Yes.keepSeparators)("  ")
5710         .equal([ "hello", "  ",  "world" ]));
5711 
5712     int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5713     int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];
5714     assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));
5715 
5716     a = [ 0, 0 ];
5717     assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
5718         .equal([ (int[]).init, [0, 0], (int[]).init ]));
5719 
5720     a = [ 0, 0, 1 ];
5721     assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
5722         .equal([ [], [0, 0], [1] ]));
5723 }
5724 
5725 /// Custom predicate functions.
5726 @safe unittest
5727 {
5728     import std.algorithm.comparison : equal;
5729     import std.ascii : toLower;
5730 
5731     assert("abXcdxef".splitter!"a.toLower == b"('x').equal(
5732                  [ "ab", "cd", "ef" ]));
5733 
5734     auto w = [ [0], [1], [2] ];
5735     assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));
5736 }
5737 
5738 /// Custom predicate functions.
5739 @safe unittest
5740 {
5741     import std.algorithm.comparison : equal;
5742     import std.typecons : Yes;
5743     import std.ascii : toLower;
5744 
5745     assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x')
5746         .equal([ "ab", "X", "cd", "x", "ef" ]));
5747 
5748     auto w = [ [0], [1], [2] ];
5749     assert(w.splitter!("a.front == b", Yes.keepSeparators)(1)
5750         .equal([ [[0]], [[1]], [[2]] ]));
5751 }
5752 
5753 /// Use splitter without a separator
5754 @safe unittest
5755 {
5756     import std.algorithm.comparison : equal;
5757     import std.range.primitives : front;
5758 
5759     assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ]));
5760     assert(equal(splitter!(a => a == ' ')("hello  world"), [ "hello", "", "world" ]));
5761 
5762     int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5763     int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
5764     assert(equal(splitter!(a => a == 0)(a), w));
5765 
5766     a = [ 0 ];
5767     assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
5768 
5769     a = [ 0, 1 ];
5770     assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
5771 
5772     w = [ [0], [1], [2] ];
5773     assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
5774 }
5775 
5776 /// Leading separators, trailing separators, or no separators.
5777 @safe unittest
5778 {
5779     import std.algorithm.comparison : equal;
5780 
5781     assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
5782     assert("ab".splitter('|').equal([ "ab" ]));
5783 }
5784 
5785 /// Leading separators, trailing separators, or no separators.
5786 @safe unittest
5787 {
5788     import std.algorithm.comparison : equal;
5789     import std.typecons : Yes;
5790 
5791     assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
5792         .equal([ "", "|", "ab", "|", "" ]));
5793     assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
5794         .equal([ "ab" ]));
5795 }
5796 
5797 /// Splitter returns bidirectional ranges if the delimiter is a single element
5798 @safe unittest
5799 {
5800     import std.algorithm.comparison : equal;
5801     import std.range : retro;
5802     assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ]));
5803 }
5804 
5805 /// Splitter returns bidirectional ranges if the delimiter is a single element
5806 @safe unittest
5807 {
5808     import std.algorithm.comparison : equal;
5809     import std.typecons : Yes;
5810     import std.range : retro;
5811     assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
5812         .retro.equal([ "def", "|", "bc", "|", "a" ]));
5813 }
5814 
5815 /// Splitting by word lazily
5816 @safe unittest
5817 {
5818     import std.ascii : isWhite;
5819     import std.algorithm.comparison : equal;
5820     import std.algorithm.iteration : splitter;
5821 
5822     string str = "Hello World!";
5823     assert(str.splitter!(isWhite).equal(["Hello", "World!"]));
5824 }
5825 
5826 @safe unittest
5827 {
5828     import std.algorithm;
5829     import std.array : array;
5830     import std.internal.test.dummyrange;
5831     import std.range : retro;
5832 
5833     assert(equal(splitter("hello  world", ' '), [ "hello", "", "world" ]));
5834     assert(equal(splitter("žlutoučkýřkůň", 'ř'), [ "žlutoučký", "kůň" ]));
5835     int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
5836     int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
5837     static assert(isForwardRange!(typeof(splitter(a, 0))));
5838 
5839     assert(equal(splitter(a, 0), w));
5840     a = null;
5841     assert(equal(splitter(a, 0),  (int[][]).init));
5842     a = [ 0 ];
5843     assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][]));
5844     a = [ 0, 1 ];
5845     assert(equal(splitter(a, 0), [ [], [1] ]));
5846     assert(equal(splitter(a, 0), [ [], [1] ][]));
5847 
5848     // Thoroughly exercise the bidirectional stuff.
5849     auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada";
5850     assert(equal(
5851         retro(splitter(str, 'a')),
5852         retro(array(splitter(str, 'a')))
5853     ));
5854 
5855     // Test interleaving front and back.
5856     auto split = splitter(str, 'a');
5857     assert(split.front == "");
5858     assert(split.back == "");
5859     split.popBack();
5860     assert(split.back == "d");
5861     split.popFront();
5862     assert(split.front == "bc ");
5863     assert(split.back == "d");
5864     split.popFront();
5865     split.popBack();
5866     assert(split.back == "t ");
5867     split.popBack();
5868     split.popBack();
5869     split.popFront();
5870     split.popFront();
5871     assert(split.front == "b ");
5872     assert(split.back == "r ");
5873 
5874     // https://issues.dlang.org/show_bug.cgi?id=4408
5875     foreach (DummyType; AllDummyRanges)
5876     {
5877         static if (isRandomAccessRange!DummyType)
5878         {
5879             static assert(isBidirectionalRange!DummyType);
5880             DummyType d;
5881             auto s = splitter(d, 5);
5882             assert(equal(s.front, [1,2,3,4]));
5883             assert(equal(s.back, [6,7,8,9,10]));
5884 
5885             auto s2 = splitter(d, [4, 5]);
5886             assert(equal(s2.front, [1,2,3]));
5887         }
5888     }
5889 }
5890 @safe unittest
5891 {
5892     import std.algorithm;
5893     import std.range;
5894     auto L = retro(iota(1L, 10L));
5895     auto s = splitter(L, 5L);
5896     assert(equal(s.front, [9L, 8L, 7L, 6L]));
5897     s.popFront();
5898     assert(equal(s.front, [4L, 3L, 2L, 1L]));
5899     s.popFront();
5900     assert(s.empty);
5901 }
5902 
5903 // https://issues.dlang.org/show_bug.cgi?id=18470
5904 @safe unittest
5905 {
5906     import std.algorithm.comparison : equal;
5907 
5908     const w = [[0], [1], [2]];
5909     assert(w.splitter!((a, b) => a.front() == b)(1).equal([[[0]], [[2]]]));
5910 }
5911 
5912 // https://issues.dlang.org/show_bug.cgi?id=18470
5913 @safe unittest
5914 {
5915     import std.algorithm.comparison : equal;
5916     import std.ascii : toLower;
5917 
5918     assert("abXcdxef".splitter!"a.toLower == b"('x').equal(["ab", "cd", "ef"]));
5919     assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"]));
5920 }
5921 
5922 /// ditto
5923 auto splitter(alias pred = "a == b",
5924               Flag!"keepSeparators" keepSeparators = No.keepSeparators,
5925               Range,
5926               Separator)(Range r, Separator s)
5927 if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
5928         && (hasSlicing!Range || isNarrowString!Range)
5929         && isForwardRange!Separator
5930         && (hasLength!Separator || isNarrowString!Separator))
5931 {
5932     import std.algorithm.searching : find;
5933     import std.conv : unsigned;
5934 
5935     static struct Result
5936     {
5937     private:
5938         Range _input;
5939         Separator _separator;
5940         // _frontLength == size_t.max means empty
5941         size_t _frontLength = size_t.max;
5942 
5943         static if (keepSeparators)
5944         {
5945             bool _wasSeparator = true;
5946         }
5947 
5948         @property auto separatorLength() { return _separator.length; }
5949 
5950         void ensureFrontLength()
5951         {
5952             if (_frontLength != _frontLength.max) return;
5953             static if (keepSeparators)
5954             {
5955                 assert(!_input.empty || _wasSeparator, "The input must not be empty");
5956                 if (_wasSeparator)
5957                 {
5958                     _frontLength = _input.length -
5959                         find!pred(_input, _separator).length;
5960                     _wasSeparator = false;
5961                 }
5962                 else
5963                 {
5964                     _frontLength = separatorLength();
5965                     _wasSeparator = true;
5966                 }
5967             }
5968             else
5969             {
5970                 assert(!_input.empty, "The input must not be empty");
5971                 // compute front length
5972                 _frontLength = (_separator.empty) ? 1 :
5973                            _input.length - find!pred(_input, _separator).length;
5974             }
5975         }
5976 
5977     public:
5978         this(Range input, Separator separator)
5979         {
5980             _input = input;
5981             _separator = separator;
5982         }
5983 
5984         @property Range front()
5985         {
5986             assert(!empty, "Attempting to fetch the front of an empty splitter.");
5987             ensureFrontLength();
5988             return _input[0 .. _frontLength];
5989         }
5990 
5991         static if (isInfinite!Range)
5992         {
5993             enum bool empty = false;  // Propagate infiniteness
5994         }
5995         else
5996         {
5997             @property bool empty()
5998             {
5999                 static if (keepSeparators)
6000                 {
6001                     return _frontLength == size_t.max && _input.empty && !_wasSeparator;
6002                 }
6003                 else
6004                 {
6005                     return _frontLength == size_t.max && _input.empty;
6006                 }
6007             }
6008         }
6009 
6010         void popFront()
6011         {
6012             assert(!empty, "Attempting to popFront an empty splitter.");
6013             ensureFrontLength();
6014 
6015             static if (keepSeparators)
6016             {
6017                 _input = _input[_frontLength .. _input.length];
6018             }
6019             else
6020             {
6021                 if (_frontLength == _input.length)
6022                 {
6023                     // done, there's no separator in sight
6024                     _input = _input[_frontLength .. _frontLength];
6025                     _frontLength = _frontLength.max;
6026                     return;
6027                 }
6028                 if (_frontLength + separatorLength == _input.length)
6029                 {
6030                     // Special case: popping the first-to-last item; there is
6031                     // an empty item right after this.
6032                     _input = _input[_input.length .. _input.length];
6033                     _frontLength = 0;
6034                     return;
6035                 }
6036                 // Normal case, pop one item and the separator, get ready for
6037                 // reading the next item
6038                 _input = _input[_frontLength + separatorLength .. _input.length];
6039             }
6040             // mark _frontLength as uninitialized
6041             _frontLength = _frontLength.max;
6042         }
6043 
6044         static if (isForwardRange!Range)
6045         {
6046             @property typeof(this) save()
6047             {
6048                 auto ret = this;
6049                 ret._input = _input.save;
6050                 return ret;
6051             }
6052         }
6053     }
6054 
6055     return Result(r, s);
6056 }
6057 
6058 @safe unittest
6059 {
6060     import std.algorithm.comparison : equal;
6061     import std.typecons : Tuple;
6062 
6063     alias C = Tuple!(int, "x", int, "y");
6064     auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
6065     assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ]));
6066 }
6067 
6068 @safe unittest
6069 {
6070     import std.algorithm.comparison : equal;
6071     import std.array : split;
6072     import std.conv : text;
6073 
6074     auto s = ",abc, de, fg,hi,";
6075     auto sp0 = splitter(s, ',');
6076     assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][]));
6077 
6078     auto s1 = ", abc, de,  fg, hi, ";
6079     auto sp1 = splitter(s1, ", ");
6080     assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][]));
6081     static assert(isForwardRange!(typeof(sp1)));
6082 
6083     int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
6084     int[][] w = [ [1, 2], [3], [4, 5], [] ];
6085     uint i;
6086     foreach (e; splitter(a, 0))
6087     {
6088         assert(i < w.length);
6089         assert(e == w[i++]);
6090     }
6091     assert(i == w.length);
6092 
6093     wstring names = ",peter,paul,jerry,";
6094     auto words = split(names, ",");
6095     assert(walkLength(words) == 5, text(walkLength(words)));
6096 }
6097 
6098 @safe unittest
6099 {
6100     int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ];
6101     int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ];
6102     uint i;
6103     foreach (e; splitter!"a.front == 0"(a, 0))
6104     {
6105         assert(i < w.length);
6106         assert(e == w[i++]);
6107     }
6108     assert(i == w.length);
6109 }
6110 
6111 @safe unittest
6112 {
6113     import std.algorithm.comparison : equal;
6114     auto s6 = ",";
6115     auto sp6 = splitter(s6, ',');
6116     foreach (e; sp6) {}
6117     assert(equal(sp6, ["", ""][]));
6118 }
6119 
6120 // https://issues.dlang.org/show_bug.cgi?id=10773
6121 @safe unittest
6122 {
6123     import std.algorithm.comparison : equal;
6124 
6125     auto s = splitter("abc", "");
6126     assert(s.equal(["a", "b", "c"]));
6127 }
6128 
6129 @safe unittest
6130 {
6131     import std.algorithm.comparison : equal;
6132 
6133     // Test by-reference separator
6134     static class RefSep {
6135     @safe:
6136         string _impl;
6137         this(string s) { _impl = s; }
6138         @property empty() { return _impl.empty; }
6139         @property auto front() { return _impl.front; }
6140         void popFront() { _impl = _impl[1..$]; }
6141         @property RefSep save() scope { return new RefSep(_impl); }
6142         @property auto length() { return _impl.length; }
6143     }
6144     auto sep = new RefSep("->");
6145     auto data = "i->am->pointing";
6146     auto words = splitter(data, sep);
6147     assert(words.equal([ "i", "am", "pointing" ]));
6148 }
6149 
6150 /// ditto
6151 auto splitter(alias isTerminator, Range)(Range r)
6152 if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(r.front))))
6153 {
6154     return SplitterResult!(unaryFun!isTerminator, Range)(r);
6155 }
6156 
6157 private struct SplitterResult(alias isTerminator, Range)
6158 {
6159     import std.algorithm.searching : find;
6160     enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
6161 
6162     private Range _input;
6163     private size_t _end = 0;
6164     static if (!fullSlicing)
6165         private Range _next;
6166 
6167     private void findTerminator()
6168     {
6169         static if (fullSlicing)
6170         {
6171             auto r = find!isTerminator(_input.save);
6172             _end = _input.length - r.length;
6173         }
6174         else
6175             for ( _end = 0; !_next.empty ; _next.popFront)
6176             {
6177                 if (isTerminator(_next.front))
6178                     break;
6179                 ++_end;
6180             }
6181     }
6182 
6183     this(Range input)
6184     {
6185         _input = input;
6186         static if (!fullSlicing)
6187             _next = _input.save;
6188 
6189         if (!_input.empty)
6190             findTerminator();
6191         else
6192             _end = size_t.max;
6193     }
6194 
6195     static if (fullSlicing)
6196     {
6197         private this(Range input, size_t end)
6198         {
6199             _input = input;
6200             _end = end;
6201         }
6202     }
6203     else
6204     {
6205         private this(Range input, size_t end, Range next)
6206         {
6207             _input = input;
6208             _end = end;
6209             _next = next;
6210         }
6211     }
6212 
6213     static if (isInfinite!Range)
6214     {
6215         enum bool empty = false;  // Propagate infiniteness.
6216     }
6217     else
6218     {
6219         @property bool empty()
6220         {
6221             return _end == size_t.max;
6222         }
6223     }
6224 
6225     @property auto front()
6226     {
6227         version (assert)
6228         {
6229             import core.exception : RangeError;
6230             if (empty)
6231                 throw new RangeError();
6232         }
6233         static if (fullSlicing)
6234             return _input[0 .. _end];
6235         else
6236         {
6237             import std.range : takeExactly;
6238             return _input.takeExactly(_end);
6239         }
6240     }
6241 
6242     void popFront()
6243     {
6244         version (assert)
6245         {
6246             import core.exception : RangeError;
6247             if (empty)
6248                 throw new RangeError();
6249         }
6250 
6251         static if (fullSlicing)
6252         {
6253             _input = _input[_end .. _input.length];
6254             if (_input.empty)
6255             {
6256                 _end = size_t.max;
6257                 return;
6258             }
6259             _input.popFront();
6260         }
6261         else
6262         {
6263             if (_next.empty)
6264             {
6265                 _input = _next;
6266                 _end = size_t.max;
6267                 return;
6268             }
6269             _next.popFront();
6270             _input = _next.save;
6271         }
6272         findTerminator();
6273     }
6274 
6275     @property typeof(this) save()
6276     {
6277         static if (fullSlicing)
6278             return SplitterResult(_input.save, _end);
6279         else
6280             return SplitterResult(_input.save, _end, _next.save);
6281     }
6282 }
6283 
6284 @safe unittest
6285 {
6286     import std.algorithm.comparison : equal;
6287     import std.range : iota;
6288 
6289     auto L = iota(1L, 10L);
6290     auto s = splitter(L, [5L, 6L]);
6291     assert(equal(s.front, [1L, 2L, 3L, 4L]));
6292     s.popFront();
6293     assert(equal(s.front, [7L, 8L, 9L]));
6294     s.popFront();
6295     assert(s.empty);
6296 }
6297 
6298 @safe unittest
6299 {
6300     import std.algorithm.comparison : equal;
6301     import std.algorithm.internal : algoFormat;
6302     import std.internal.test.dummyrange;
6303 
6304     void compare(string sentence, string[] witness)
6305     {
6306         auto r = splitter!"a == ' '"(sentence);
6307         assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness));
6308     }
6309 
6310     compare(" Mary  has a little lamb.   ",
6311             ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
6312     compare("Mary  has a little lamb.   ",
6313             ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
6314     compare("Mary  has a little lamb.",
6315             ["Mary", "", "has", "a", "little", "lamb."]);
6316     compare("", (string[]).init);
6317     compare(" ", ["", ""]);
6318 
6319     static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC"))));
6320 
6321     foreach (DummyType; AllDummyRanges)
6322     {
6323         static if (isRandomAccessRange!DummyType)
6324         {
6325             auto rangeSplit = splitter!"a == 5"(DummyType.init);
6326             assert(equal(rangeSplit.front, [1,2,3,4]));
6327             rangeSplit.popFront();
6328             assert(equal(rangeSplit.front, [6,7,8,9,10]));
6329         }
6330     }
6331 }
6332 
6333 @safe unittest
6334 {
6335     import std.algorithm.comparison : equal;
6336     import std.algorithm.internal : algoFormat;
6337     import std.range;
6338 
6339     struct Entry
6340     {
6341         int low;
6342         int high;
6343         int[][] result;
6344     }
6345     Entry[] entries = [
6346         Entry(0, 0, []),
6347         Entry(0, 1, [[0]]),
6348         Entry(1, 2, [[], []]),
6349         Entry(2, 7, [[2], [4], [6]]),
6350         Entry(1, 8, [[], [2], [4], [6], []]),
6351     ];
6352     foreach ( entry ; entries )
6353     {
6354         auto a = iota(entry.low, entry.high).filter!"true"();
6355         auto b = splitter!"a%2"(a);
6356         assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result));
6357     }
6358 }
6359 
6360 @safe unittest
6361 {
6362     import std.algorithm.comparison : equal;
6363     import std.uni : isWhite;
6364 
6365     // https://issues.dlang.org/show_bug.cgi?id=6791
6366     assert(equal(
6367         splitter("là dove terminava quella valle"),
6368         ["là", "dove", "terminava", "quella", "valle"]
6369     ));
6370     assert(equal(
6371         splitter!(isWhite)("là dove terminava quella valle"),
6372         ["là", "dove", "terminava", "quella", "valle"]
6373     ));
6374     assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"]));
6375 }
6376 
6377 // https://issues.dlang.org/show_bug.cgi?id=18657
6378 pure @safe unittest
6379 {
6380     import std.algorithm.comparison : equal;
6381     import std.range : refRange;
6382     string s = "foobar";
6383     auto r = refRange(&s).splitter!(c => c == 'b');
6384     assert(equal!equal(r.save, ["foo", "ar"]));
6385     assert(equal!equal(r.save, ["foo", "ar"]));
6386 }
6387 
6388 /++
6389 Lazily splits the character-based range `s` into words, using whitespace as the
6390 delimiter.
6391 
6392 This function is character-range specific and, contrary to
6393 `splitter!(std.uni.isWhite)`, runs of whitespace will be merged together
6394 (no empty tokens will be produced).
6395 
6396 Params:
6397     s = The character-based range to be split. Must be a string, or a
6398     random-access range of character types.
6399 
6400 Returns:
6401     An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of
6402     the original range split by whitespace.
6403  +/
6404 auto splitter(Range)(Range s)
6405 if (isSomeString!Range ||
6406     isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range &&
6407     !isConvertibleToString!Range &&
6408     isSomeChar!(ElementEncodingType!Range))
6409 {
6410     import std.algorithm.searching : find;
6411     static struct Result
6412     {
6413     private:
6414         import core.exception : RangeError;
6415         Range _s;
6416         size_t _frontLength;
6417 
6418         void getFirst()
6419         {
6420             import std.uni : isWhite;
6421             import std.traits : Unqual;
6422 
6423             static if (is(immutable ElementEncodingType!Range == immutable wchar) &&
6424                        is(immutable ElementType!Range == immutable dchar))
6425             {
6426                 // all unicode whitespace characters fit into a wchar. However,
6427                 // this range is a wchar array, so we will treat it like a
6428                 // wchar array instead of decoding each code point.
6429                 _frontLength = _s.length; // default condition, no spaces
6430                 foreach (i; 0 .. _s.length)
6431                     if (isWhite(_s[i]))
6432                     {
6433                         _frontLength = i;
6434                         break;
6435                     }
6436             }
6437             else static if (is(immutable ElementType!Range == immutable dchar) ||
6438                             is(immutable ElementType!Range == immutable wchar))
6439             {
6440                 // dchar or wchar range, we can just use find.
6441                 auto r = find!(isWhite)(_s.save);
6442                 _frontLength = _s.length - r.length;
6443             }
6444             else
6445             {
6446                 // need to decode the characters until we find a space. This is
6447                 // ported from std.string.stripLeft.
6448                 static import std.ascii;
6449                 static import std.uni;
6450                 import std.utf : decodeFront;
6451 
6452                 auto input = _s.save;
6453                 size_t iLength = input.length;
6454 
6455                 while (!input.empty)
6456                 {
6457                     auto c = input.front;
6458                     if (std.ascii.isASCII(c))
6459                     {
6460                         if (std.ascii.isWhite(c))
6461                             break;
6462                         input.popFront();
6463                         --iLength;
6464                     }
6465                     else
6466                     {
6467                         auto dc = decodeFront(input);
6468                         if (std.uni.isWhite(dc))
6469                             break;
6470                         iLength = input.length;
6471                     }
6472                 }
6473 
6474                 // sanity check
6475                 assert(iLength <= _s.length, "The current index must not"
6476                         ~ " exceed the length of the input");
6477 
6478                 _frontLength = _s.length - iLength;
6479             }
6480         }
6481 
6482     public:
6483         this(Range s)
6484         {
6485             import std.string : stripLeft;
6486             _s = s.stripLeft();
6487             getFirst();
6488         }
6489 
6490         @property auto front()
6491         {
6492             version (assert) if (empty) throw new RangeError();
6493             return _s[0 .. _frontLength];
6494         }
6495 
6496         void popFront()
6497         {
6498             import std.string : stripLeft;
6499             version (assert) if (empty) throw new RangeError();
6500             _s = _s[_frontLength .. $].stripLeft();
6501             getFirst();
6502         }
6503 
6504         @property bool empty() const
6505         {
6506             return _s.empty;
6507         }
6508 
6509         @property inout(Result) save() inout @safe pure nothrow
6510         {
6511             return this;
6512         }
6513     }
6514     return Result(s);
6515 }
6516 
6517 ///
6518 @safe pure unittest
6519 {
6520     import std.algorithm.comparison : equal;
6521     auto a = " a     bcd   ef gh ";
6522     assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
6523 }
6524 
6525 @safe pure unittest
6526 {
6527     import std.algorithm.comparison : equal;
6528     import std.meta : AliasSeq;
6529     static foreach (S; AliasSeq!(string, wstring, dstring))
6530     {{
6531         import std.conv : to;
6532         S a = " a  \u2028   bcd   ef gh ";
6533         assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")]));
6534         a = "";
6535         assert(splitter(a).empty);
6536     }}
6537 
6538     immutable string s = " a     bcd   ef gh ";
6539     assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][]));
6540 }
6541 
6542 @safe unittest
6543 {
6544     import std.conv : to;
6545     import std.string : strip;
6546 
6547     // TDPL example, page 8
6548     uint[string] dictionary;
6549     char[][3] lines;
6550     lines[0] = "line one".dup;
6551     lines[1] = "line \ttwo".dup;
6552     lines[2] = "yah            last   line\ryah".dup;
6553     foreach (line; lines)
6554     {
6555        foreach (word; splitter(strip(line)))
6556        {
6557             if (word in dictionary) continue; // Nothing to do
6558             auto newID = dictionary.length;
6559             dictionary[to!string(word)] = cast(uint) newID;
6560         }
6561     }
6562     assert(dictionary.length == 5);
6563     assert(dictionary["line"]== 0);
6564     assert(dictionary["one"]== 1);
6565     assert(dictionary["two"]== 2);
6566     assert(dictionary["yah"]== 3);
6567     assert(dictionary["last"]== 4);
6568 
6569 }
6570 
6571 @safe unittest
6572 {
6573     // do it with byCodeUnit
6574     import std.conv : to;
6575     import std.string : strip;
6576     import std.utf : byCodeUnit;
6577 
6578     alias BCU = typeof("abc".byCodeUnit());
6579 
6580     // TDPL example, page 8
6581     uint[BCU] dictionary;
6582     BCU[3] lines;
6583     lines[0] = "line one".byCodeUnit;
6584     lines[1] = "line \ttwo".byCodeUnit;
6585     lines[2] = "yah            last   line\ryah".byCodeUnit;
6586     foreach (line; lines)
6587     {
6588        foreach (word; splitter(strip(line)))
6589        {
6590            static assert(is(typeof(word) == BCU));
6591             if (word in dictionary) continue; // Nothing to do
6592             auto newID = dictionary.length;
6593             dictionary[word] = cast(uint) newID;
6594         }
6595     }
6596     assert(dictionary.length == 5);
6597     assert(dictionary["line".byCodeUnit]== 0);
6598     assert(dictionary["one".byCodeUnit]== 1);
6599     assert(dictionary["two".byCodeUnit]== 2);
6600     assert(dictionary["yah".byCodeUnit]== 3);
6601     assert(dictionary["last".byCodeUnit]== 4);
6602 }
6603 
6604 // https://issues.dlang.org/show_bug.cgi?id=19238
6605 @safe pure unittest
6606 {
6607     import std.utf : byCodeUnit;
6608     import std.algorithm.comparison : equal;
6609     auto range = "hello    world".byCodeUnit.splitter;
6610     static assert(is(typeof(range.front()) == typeof("hello".byCodeUnit())));
6611     assert(range.equal(["hello".byCodeUnit, "world".byCodeUnit]));
6612 
6613     // test other space types, including unicode
6614     auto u = " a\t\v\r bcd\u3000 \u2028\t\nef\U00010001 gh";
6615     assert(equal(splitter(u), ["a", "bcd", "ef\U00010001", "gh"][]));
6616     assert(equal(splitter(u.byCodeUnit), ["a".byCodeUnit, "bcd".byCodeUnit,
6617                  "ef\U00010001".byCodeUnit, "gh".byCodeUnit][]));
6618 }
6619 
6620 @safe unittest
6621 {
6622     import std.algorithm.comparison : equal;
6623     import std.algorithm.internal : algoFormat;
6624     import std.array : split;
6625     import std.conv : text;
6626 
6627     // Check consistency:
6628     // All flavors of split should produce the same results
6629     foreach (input; [(int[]).init,
6630                      [0],
6631                      [0, 1, 0],
6632                      [1, 1, 0, 0, 1, 1],
6633                     ])
6634     {
6635         foreach (s; [0, 1])
6636         {
6637             auto result = split(input, s);
6638 
6639             assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s])));
6640             //assert(equal(result, split(input, [s].filter!"true"())));                          //Not yet implemented
6641             assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input)));
6642 
6643             //assert(equal!equal(result, split(input.filter!"true"(), s)));                      //Not yet implemented
6644             //assert(equal!equal(result, split(input.filter!"true"(), [s])));                    //Not yet implemented
6645             //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"())));    //Not yet implemented
6646             assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
6647 
6648             assert(equal(result, splitter(input, s)));
6649             assert(equal(result, splitter(input, [s])));
6650             //assert(equal(result, splitter(input, [s].filter!"true"())));                       //Not yet implemented
6651             assert(equal(result, splitter!((a) => a == s)(input)));
6652 
6653             //assert(equal!equal(result, splitter(input.filter!"true"(), s)));                   //Not yet implemented
6654             //assert(equal!equal(result, splitter(input.filter!"true"(), [s])));                 //Not yet implemented
6655             //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
6656             assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
6657         }
6658     }
6659     foreach (input; [string.init,
6660                      " ",
6661                      "  hello ",
6662                      "hello   hello",
6663                      " hello   what heck   this ?  "
6664                     ])
6665     {
6666         foreach (s; [' ', 'h'])
6667         {
6668             auto result = split(input, s);
6669 
6670             assert(equal(result, split(input, [s])));
6671             //assert(equal(result, split(input, [s].filter!"true"())));                          //Not yet implemented
6672             assert(equal(result, split!((a) => a == s)(input)));
6673 
6674             //assert(equal!equal(result, split(input.filter!"true"(), s)));                      //Not yet implemented
6675             //assert(equal!equal(result, split(input.filter!"true"(), [s])));                    //Not yet implemented
6676             //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"())));    //Not yet implemented
6677             assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
6678 
6679             assert(equal(result, splitter(input, s)));
6680             assert(equal(result, splitter(input, [s])));
6681             //assert(equal(result, splitter(input, [s].filter!"true"())));                       //Not yet implemented
6682             assert(equal(result, splitter!((a) => a == s)(input)));
6683 
6684             //assert(equal!equal(result, splitter(input.filter!"true"(), s)));                   //Not yet implemented
6685             //assert(equal!equal(result, splitter(input.filter!"true"(), [s])));                 //Not yet implemented
6686             //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
6687             assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
6688         }
6689     }
6690 }
6691 
6692 // In same combinations substitute needs to calculate the auto-decoded length
6693 // of its needles
6694 private template hasDifferentAutodecoding(Range, Needles...)
6695 {
6696     import std.meta : anySatisfy;
6697     /* iff
6698        - the needles needs auto-decoding, but the incoming range doesn't (or vice versa)
6699        - both (range, needle) need auto-decoding and don't share the same common type
6700     */
6701     enum needlesAreNarrow = anySatisfy!(isNarrowString, Needles);
6702     enum sourceIsNarrow = isNarrowString!Range;
6703     enum hasDifferentAutodecoding = sourceIsNarrow != needlesAreNarrow ||
6704                                     (sourceIsNarrow && needlesAreNarrow &&
6705                                     is(CommonType!(Range, Needles) == void));
6706 }
6707 
6708 @safe nothrow @nogc pure unittest
6709 {
6710     import std.meta : AliasSeq; // used for better clarity
6711 
6712     static assert(!hasDifferentAutodecoding!(string, AliasSeq!(string, string)));
6713     static assert(!hasDifferentAutodecoding!(wstring, AliasSeq!(wstring, wstring)));
6714     static assert(!hasDifferentAutodecoding!(dstring, AliasSeq!(dstring, dstring)));
6715 
6716     // the needles needs auto-decoding, but the incoming range doesn't (or vice versa)
6717     static assert(hasDifferentAutodecoding!(string, AliasSeq!(wstring, wstring)));
6718     static assert(hasDifferentAutodecoding!(string, AliasSeq!(dstring, dstring)));
6719     static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(string, string)));
6720     static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(dstring, dstring)));
6721     static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(string, string)));
6722     static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(wstring, wstring)));
6723 
6724     // both (range, needle) need auto-decoding and don't share the same common type
6725     static foreach (T; AliasSeq!(string, wstring, dstring))
6726     {
6727         static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, string)));
6728         static assert(hasDifferentAutodecoding!(T, AliasSeq!(dstring, string)));
6729         static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, dstring)));
6730     }
6731 }
6732 
6733 // substitute
6734 /**
6735 Returns a range with all occurrences of `substs` in `r`.
6736 replaced with their substitution.
6737 
6738 Single value replacements (`'ö'.substitute!('ä', 'a', 'ö', 'o', 'ü', 'u)`) are
6739 supported as well and in $(BIGOH 1).
6740 
6741 Params:
6742     r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
6743     value = a single value which can be substituted in $(BIGOH 1)
6744     substs = a set of replacements/substitutions
6745     pred = the equality function to test if element(s) are equal to
6746     a substitution
6747 
6748 Returns: a range with the substitutions replaced.
6749 
6750 See_Also:
6751 $(REF replace, std, array) for an eager replace algorithm or
6752 $(REF translate, std, string), and $(REF tr, std, string)
6753 for string algorithms with translation tables.
6754 */
6755 template substitute(substs...)
6756 if (substs.length >= 2 && isExpressions!substs)
6757 {
6758     import std.range.primitives : ElementType;
6759     import std.traits : CommonType;
6760 
6761     static assert(!(substs.length & 1), "The number of substitution parameters must be even");
6762 
6763     /**
6764       Substitute single values with compile-time substitution mappings.
6765       Complexity: $(BIGOH 1) due to D's `switch` guaranteeing $(BIGOH 1);
6766     */
6767     auto substitute(Value)(Value value)
6768     if (isInputRange!Value || !is(CommonType!(Value, typeof(substs[0])) == void))
6769     {
6770         static if (isInputRange!Value)
6771         {
6772             static if (!is(CommonType!(ElementType!Value, typeof(substs[0])) == void))
6773             {
6774                 // Substitute single range elements with compile-time substitution mappings
6775                 return value.map!(a => substitute(a));
6776             }
6777             else static if (isInputRange!Value &&
6778                     !is(CommonType!(ElementType!Value, ElementType!(typeof(substs[0]))) == void))
6779             {
6780                 // not implemented yet, fallback to runtime variant for now
6781                 return .substitute(value, substs);
6782             }
6783             else
6784             {
6785                 static assert(0, `Compile-time substitutions must be elements or ranges of the same type of ` ~
6786                     Value.stringof ~ `.`);
6787             }
6788         }
6789         // Substitute single values with compile-time substitution mappings.
6790         else // static if (!is(CommonType!(Value, typeof(substs[0])) == void))
6791         {
6792             switch (value)
6793             {
6794                 static foreach (i; 0 .. substs.length / 2)
6795                     case substs[2 * i]:
6796                         return substs[2 * i + 1];
6797 
6798                 default: return value;
6799             }
6800         }
6801     }
6802 }
6803 
6804 /// ditto
6805 auto substitute(alias pred = (a, b) => a == b, R, Substs...)(R r, Substs substs)
6806 if (isInputRange!R && Substs.length >= 2 && !is(CommonType!(Substs) == void))
6807 {
6808     import std.range.primitives : ElementType;
6809     import std.meta : allSatisfy;
6810     import std.traits : CommonType;
6811 
6812     static assert(!(Substs.length & 1), "The number of substitution parameters must be even");
6813 
6814     enum n = Substs.length / 2;
6815 
6816     // Substitute individual elements
6817     static if (!is(CommonType!(ElementType!R, Substs) == void))
6818     {
6819         import std.functional : binaryFun;
6820 
6821         // Imitate a value closure to be @nogc
6822         static struct ReplaceElement
6823         {
6824             private Substs substs;
6825 
6826             this(Substs substs)
6827             {
6828                 this.substs = substs;
6829             }
6830 
6831             auto opCall(E)(E e)
6832             {
6833                 static foreach (i; 0 .. n)
6834                     if (binaryFun!pred(e, substs[2 * i]))
6835                         return substs[2 * i + 1];
6836 
6837                 return e;
6838             }
6839         }
6840         auto er = ReplaceElement(substs);
6841         return r.map!er;
6842     }
6843     // Substitute subranges
6844     else static if (!is(CommonType!(ElementType!R, ElementType!(Substs[0])) == void)  &&
6845                         allSatisfy!(isForwardRange, Substs))
6846     {
6847         import std.range : choose, take;
6848         import std.meta : Stride;
6849 
6850         auto replaceElement(E)(E e)
6851         {
6852             alias ReturnA = typeof(e[0]);
6853             alias ReturnB = typeof(substs[0 .. 1].take(1));
6854 
6855             // 1-based index
6856             const auto hitNr = e[1];
6857             switch (hitNr)
6858             {
6859                 // no hit
6860                 case 0:
6861                     // use choose trick for non-common range
6862                     static if (is(CommonType!(ReturnA, ReturnB) == void))
6863                         return choose(1, e[0], ReturnB.init);
6864                     else
6865                         return e[0];
6866 
6867                 // all replacements
6868                 static foreach (i; 0 .. n)
6869                     case i + 1:
6870                         // use choose trick for non-common ranges
6871                         static if (is(CommonType!(ReturnA, ReturnB) == void))
6872                             return choose(0, e[0], substs[2 * i + 1].take(size_t.max));
6873                         else
6874                             return substs[2 * i + 1].take(size_t.max);
6875                 default:
6876                     assert(0, "hitNr should always be found.");
6877             }
6878         }
6879 
6880         alias Ins = Stride!(2, Substs);
6881 
6882         static struct SubstituteSplitter
6883         {
6884             import std.range : drop;
6885             import std.typecons : Tuple;
6886 
6887             private
6888             {
6889                 typeof(R.init.drop(0)) rest;
6890                 Ins needles;
6891 
6892                 typeof(R.init.take(0)) skip; // skip before next hit
6893                 alias Hit = size_t; // 0 iff no hit, otherwise hit in needles[index-1]
6894                 alias E = Tuple!(typeof(skip), Hit);
6895                 Hit hitNr; // hit number: 0 means no hit, otherwise index+1 to needles that matched
6896                 bool hasHit; // is there a replacement hit which should be printed?
6897 
6898                 enum hasDifferentAutodecoding = .hasDifferentAutodecoding!(typeof(rest), Ins);
6899 
6900                 // calculating the needle length for narrow strings might be expensive -> cache it
6901                  static if (hasDifferentAutodecoding)
6902                      ptrdiff_t[n] needleLengths = -1;
6903             }
6904 
6905             this(R haystack, Ins needles)
6906             {
6907                 this.rest = haystack.drop(0);
6908                 this.needles = needles;
6909                 if (!haystack.empty)
6910                 {
6911                     hasHit = true;
6912                     popFront;
6913                 }
6914                 static if (hasNested!(typeof(skip)))
6915                     skip = rest.take(0);
6916             }
6917 
6918             /*  If `skip` is non-empty, it's returned as (skip, 0) tuple
6919                 otherwise a similar (<empty>, hitNr) tuple is returned.
6920                 `replaceElement` maps based on the second item (`hitNr`).
6921             */
6922             @property auto ref front()
6923             {
6924                 assert(!empty, "Attempting to fetch the front of an empty substitute.");
6925                 return !skip.empty ? E(skip, 0) : E(typeof(skip).init, hitNr);
6926             }
6927 
6928             static if (isInfinite!R)
6929                 enum empty = false; // propagate infiniteness
6930             else
6931                 @property bool empty()
6932                 {
6933                     return skip.empty && !hasHit;
6934                 }
6935 
6936             /* If currently in a skipping phase => reset.
6937                Otherwise try to find the next occurrence of `needles`
6938                   If valid match
6939                     - if there are elements before the match, set skip with these elements
6940                       (on the next popFront, the range will be in the skip state once)
6941                     - `rest`: advance to the end of the match
6942                     - set hasHit
6943                Otherwise skip to the end
6944             */
6945             void popFront()
6946             {
6947                 assert(!empty, "Attempting to popFront an empty substitute.");
6948                 if (!skip.empty)
6949                 {
6950                     skip = typeof(skip).init; // jump over skip
6951                 }
6952                 else
6953                 {
6954                     import std.algorithm.searching : countUntil, find;
6955 
6956                     auto match = rest.find!pred(needles);
6957 
6958                     static if (needles.length >= 2) // variadic version of find (returns a tuple)
6959                     {
6960                         // find with variadic needles returns a (range, needleNr) tuple
6961                         // needleNr is a 1-based index
6962                         auto hitValue = match[0];
6963                         hitNr = match[1];
6964                     }
6965                     else
6966                     {
6967                         // find with one needle returns the range
6968                         auto hitValue = match;
6969                         hitNr = match.empty ? 0 : 1;
6970                     }
6971 
6972                     if (hitNr == 0) // no more hits
6973                     {
6974                         skip = rest.take(size_t.max);
6975                         hasHit = false;
6976                         rest = typeof(rest).init;
6977                     }
6978                     else
6979                     {
6980                         auto hitLength = size_t.max;
6981                         switchL: switch (hitNr - 1)
6982                         {
6983                             static foreach (i; 0 .. n)
6984                             {
6985                                 case i:
6986                                     static if (hasDifferentAutodecoding)
6987                                     {
6988                                         import std.utf : codeLength;
6989 
6990                                         // cache calculated needle length
6991                                         if (needleLengths[i] != -1)
6992                                             hitLength = needleLengths[i];
6993                                         else
6994                                             hitLength = needleLengths[i] = codeLength!dchar(needles[i]);
6995                                     }
6996                                     else
6997                                     {
6998                                         hitLength = needles[i].length;
6999                                     }
7000                                     break switchL;
7001                             }
7002                             default:
7003                                 assert(0, "hitNr should always be found");
7004                         }
7005 
7006                         const pos = rest.countUntil(hitValue);
7007                         if (pos > 0) // match not at start of rest
7008                             skip = rest.take(pos);
7009 
7010                         hasHit = true;
7011 
7012                         // iff the source range and the substitutions are narrow strings,
7013                         // we can avoid calling the auto-decoding `popFront` (via drop)
7014                         static if (isNarrowString!(typeof(hitValue)) && !hasDifferentAutodecoding)
7015                             rest = hitValue[hitLength .. $];
7016                         else
7017                             rest = hitValue.drop(hitLength);
7018                     }
7019                 }
7020             }
7021         }
7022 
7023         // extract inputs
7024         Ins ins;
7025         static foreach (i; 0 .. n)
7026             ins[i] = substs[2 * i];
7027 
7028         return SubstituteSplitter(r, ins)
7029                 .map!(a => replaceElement(a))
7030                 .joiner;
7031     }
7032     else
7033     {
7034         static assert(0, "The substitutions must either substitute a single element or a save-able subrange.");
7035     }
7036 }
7037 
7038 ///
7039 @safe pure unittest
7040 {
7041     import std.algorithm.comparison : equal;
7042 
7043     // substitute single elements
7044     assert("do_it".substitute('_', ' ').equal("do it"));
7045 
7046     // substitute multiple, single elements
7047     assert("do_it".substitute('_', ' ',
7048                                'd', 'g',
7049                                'i', 't',
7050                                't', 'o')
7051                   .equal("go to"));
7052 
7053     // substitute subranges
7054     assert("do_it".substitute("_", " ",
7055                               "do", "done")
7056                   .equal("done it"));
7057 
7058     // substitution works for any ElementType
7059     int[] x = [1, 2, 3];
7060     auto y = x.substitute(1, 0.1);
7061     assert(y.equal([0.1, 2, 3]));
7062     static assert(is(typeof(y.front) == double));
7063 
7064     import std.range : retro;
7065     assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1]));
7066 }
7067 
7068 /// Use the faster compile-time overload
7069 @safe pure unittest
7070 {
7071     import std.algorithm.comparison : equal;
7072 
7073     // substitute subranges of a range
7074     assert("apple_tree".substitute!("apple", "banana",
7075                                     "tree", "shrub").equal("banana_shrub"));
7076 
7077     // substitute subranges of a range
7078     assert("apple_tree".substitute!('a', 'b',
7079                                     't', 'f').equal("bpple_free"));
7080 
7081     // substitute values
7082     assert('a'.substitute!('a', 'b', 't', 'f') == 'b');
7083 }
7084 
7085 /// Multiple substitutes
7086 @safe pure unittest
7087 {
7088     import std.algorithm.comparison : equal;
7089     import std.range.primitives : ElementType;
7090 
7091     int[3] x = [1, 2, 3];
7092     auto y = x[].substitute(1, 0.1)
7093                 .substitute(0.1, 0.2);
7094     static assert(is(typeof(y.front) == double));
7095     assert(y.equal([0.2, 2, 3]));
7096 
7097     auto z = "42".substitute('2', '3')
7098                  .substitute('3', '1');
7099     static assert(is(ElementType!(typeof(z)) == dchar));
7100     assert(equal(z, "41"));
7101 }
7102 
7103 // Test the first example with compile-time overloads
7104 @safe pure unittest
7105 {
7106     import std.algorithm.comparison : equal;
7107 
7108     // substitute single elements
7109     assert("do_it".substitute!('_', ' ').equal("do it"));
7110 
7111     // substitute multiple, single elements
7112     assert("do_it".substitute!('_', ' ',
7113                                'd', 'g',
7114                                'i', 't',
7115                                't', 'o')
7116                   .equal(`go to`));
7117 
7118     // substitute subranges
7119     assert("do_it".substitute!("_", " ",
7120                                "do", "done")
7121                   .equal("done it"));
7122 
7123     // substitution works for any ElementType
7124     int[3] x = [1, 2, 3];
7125     auto y = x[].substitute!(1, 0.1);
7126     assert(y.equal([0.1, 2, 3]));
7127     static assert(is(typeof(y.front) == double));
7128 
7129     import std.range : retro;
7130     assert([1, 2, 3].substitute!(1, 0.1).retro.equal([3, 2, 0.1]));
7131 }
7132 
7133 // test infinite ranges
7134 @safe pure nothrow unittest
7135 {
7136     import std.algorithm.comparison : equal;
7137     import std.range : cycle, take;
7138 
7139     int[] x = [1, 2, 3];
7140     assert(x.cycle.substitute!(1, 0.1).take(4).equal([0.1, 2, 3, 0.1]));
7141     assert(x.cycle.substitute(1, 0.1).take(4).equal([0.1, 2, 3, 0.1]));
7142 }
7143 
7144 // test infinite ranges
7145 @safe pure nothrow unittest
7146 {
7147     import std.algorithm.comparison : equal;
7148     import std.internal.test.dummyrange : AllDummyRanges;
7149 
7150     foreach (R; AllDummyRanges)
7151     {
7152         assert(R.init
7153                 .substitute!(2, 22, 3, 33, 5, 55, 9, 99)
7154                 .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10]));
7155 
7156         assert(R.init
7157                 .substitute(2, 22, 3, 33, 5, 55, 9, 99)
7158                 .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10]));
7159     }
7160 }
7161 
7162 // test multiple replacements
7163 @safe pure unittest
7164 {
7165     import std.algorithm.comparison : equal;
7166 
7167     assert("alpha.beta.gamma"
7168             .substitute("alpha", "1",
7169                         "gamma", "3",
7170                         "beta", "2").equal("1.2.3"));
7171 
7172     assert("alpha.beta.gamma."
7173             .substitute("alpha", "1",
7174                         "gamma", "3",
7175                         "beta", "2").equal("1.2.3."));
7176 
7177     assert("beta.beta.beta"
7178             .substitute("alpha", "1",
7179                         "gamma", "3",
7180                         "beta", "2").equal("2.2.2"));
7181 
7182     assert("alpha.alpha.alpha"
7183             .substitute("alpha", "1",
7184                         "gamma", "3",
7185                         "beta", "2").equal("1.1.1"));
7186 }
7187 
7188 // test combination of subrange + element replacement
7189 @safe pure unittest
7190 {
7191     import std.algorithm.comparison : equal;
7192 
7193     assert(("abcDe".substitute("a", "AA",
7194                                "b", "DD")
7195                    .substitute('A', 'y',
7196                                'D', 'x',
7197                                'e', '1'))
7198            .equal("yyxxcx1"));
7199 }
7200 
7201 // test const + immutable storage groups
7202 @safe pure unittest
7203 {
7204     import std.algorithm.comparison : equal;
7205 
7206     auto xyz_abc(T)(T value)
7207     {
7208         immutable a = "a";
7209         const b = "b";
7210         auto c = "c";
7211         return value.substitute!("x", a,
7212                                  "y", b,
7213                                  "z", c);
7214     }
7215     assert(xyz_abc("_x").equal("_a"));
7216     assert(xyz_abc(".y.").equal(".b."));
7217     assert(xyz_abc("z").equal("c"));
7218     assert(xyz_abc("w").equal("w"));
7219 }
7220 
7221 // test with narrow strings (auto-decoding) and subranges
7222 @safe pure unittest
7223 {
7224     import std.algorithm.comparison : equal;
7225 
7226     assert("äöü€".substitute("ä", "b", "ü", "u").equal("böu€"));
7227     assert("äöü€".substitute!("ä", "b", "ü", "u").equal("böu€"));
7228     assert("ä...öü€".substitute("ä", "b", "ü", "u").equal("b...öu€"));
7229 
7230     auto expected = "emoticons😄😅.😇😈Rock";
7231     assert("emoticons😄😅😆😇😈rock"
7232             .substitute("r", "R", "😆", ".").equal(expected));
7233     assert("emoticons😄😅😆😇😈rock"
7234             .substitute!("r", "R", "😆", ".").equal(expected));
7235 }
7236 
7237 // test with narrow strings (auto-decoding) and single elements
7238 @safe pure unittest
7239 {
7240     import std.algorithm.comparison : equal;
7241 
7242     assert("äöü€".substitute('ä', 'b', 'ü', 'u').equal("böu€"));
7243     assert("äöü€".substitute!('ä', 'b', 'ü', 'u').equal("böu€"));
7244 
7245     auto expected = "emoticons😄😅.😇😈Rock";
7246     assert("emoticons😄😅😆😇😈rock"
7247             .substitute('r', 'R', '😆', '.').equal(expected));
7248     assert("emoticons😄😅😆😇😈rock"
7249             .substitute!('r', 'R', '😆', '.').equal(expected));
7250 }
7251 
7252 // test auto-decoding {n,w,d} strings X {n,w,d} strings
7253 @safe pure unittest
7254 {
7255     import std.algorithm.comparison : equal;
7256 
7257     assert("ääöü€".substitute("ä", "b", "ü", "u").equal("bböu€"));
7258     assert("ääöü€".substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
7259     assert("ääöü€".substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
7260 
7261     assert("ääöü€"w.substitute("ä", "b", "ü", "u").equal("bböu€"));
7262     assert("ääöü€"w.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
7263     assert("ääöü€"w.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
7264 
7265     assert("ääöü€"d.substitute("ä", "b", "ü", "u").equal("bböu€"));
7266     assert("ääöü€"d.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
7267     assert("ääöü€"d.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
7268 
7269     // auto-decoding is done before by a different range
7270     assert("ääöü€".filter!(a => true).substitute("ä", "b", "ü", "u").equal("bböu€"));
7271     assert("ääöü€".filter!(a => true).substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
7272     assert("ääöü€".filter!(a => true).substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
7273 }
7274 
7275 // test repeated replacement
7276 @safe pure nothrow unittest
7277 {
7278     import std.algorithm.comparison : equal;
7279 
7280     assert([1, 2, 3, 1, 1, 2].substitute(1, 0).equal([0, 2, 3, 0, 0, 2]));
7281     assert([1, 2, 3, 1, 1, 2].substitute!(1, 0).equal([0, 2, 3, 0, 0, 2]));
7282     assert([1, 2, 3, 1, 1, 2].substitute(1, 2, 2, 9).equal([2, 9, 3, 2, 2, 9]));
7283 }
7284 
7285 // test @nogc for single element replacements
7286 @safe @nogc unittest
7287 {
7288     import std.algorithm.comparison : equal;
7289 
7290     static immutable arr = [1, 2, 3, 1, 1, 2];
7291     static immutable expected = [0, 2, 3, 0, 0, 2];
7292 
7293     assert(arr.substitute!(1, 0).equal(expected));
7294     assert(arr.substitute(1, 0).equal(expected));
7295 }
7296 
7297 // test different range types
7298 @safe pure nothrow unittest
7299 {
7300     import std.algorithm.comparison : equal;
7301     import std.internal.test.dummyrange : AllDummyRanges;
7302 
7303     static foreach (DummyType; AllDummyRanges)
7304     {{
7305         DummyType dummyRange;
7306 
7307         // single substitution
7308         dummyRange.substitute (2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]);
7309         dummyRange.substitute!(2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]);
7310 
7311         // multiple substitution
7312         dummyRange.substitute (2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]);
7313         dummyRange.substitute!(2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]);
7314     }}
7315 }
7316 
7317 // https://issues.dlang.org/show_bug.cgi?id=19207
7318 @safe pure nothrow unittest
7319 {
7320     import std.algorithm.comparison : equal;
7321     assert([1, 2, 3, 4].substitute([1], [7]).equal([7, 2, 3, 4]));
7322     assert([1, 2, 3, 4].substitute([2], [7]).equal([1, 7, 3, 4]));
7323     assert([1, 2, 3, 4].substitute([4], [7]).equal([1, 2, 3, 7]));
7324     assert([1, 2, 3, 4].substitute([2, 3], [7]).equal([1, 7, 4]));
7325     assert([1, 2, 3, 4].substitute([3, 4], [7, 8]).equal([1, 2, 7, 8]));
7326 }
7327 
7328 // tests recognizing empty base ranges
7329 nothrow pure @safe unittest
7330 {
7331     import std.utf : byCodeUnit;
7332     import std.algorithm.comparison : equal;
7333 
7334     assert("".byCodeUnit.substitute('4', 'A').empty);
7335     assert("".byCodeUnit.substitute('0', 'O', '5', 'S', '1', 'l').empty);
7336     assert("".byCodeUnit.substitute("PKM".byCodeUnit, "PoKeMon".byCodeUnit).empty);
7337     assert("".byCodeUnit.substitute
7338     (   "ding".byCodeUnit,
7339         "dong".byCodeUnit,
7340         "click".byCodeUnit,
7341         "clack".byCodeUnit,
7342         "ping".byCodeUnit,
7343         "latency".byCodeUnit
7344     ).empty);
7345 }
7346 
7347 // sum
7348 /**
7349 Sums elements of `r`, which must be a finite
7350 $(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although
7351 conceptually `sum(r)` is equivalent to $(LREF fold)!((a, b) => a +
7352 b)(r, 0), `sum` uses specialized algorithms to maximize accuracy,
7353 as follows.
7354 
7355 $(UL
7356 $(LI If $(REF ElementType, std,range,primitives)!R is a floating-point
7357 type and `R` is a
7358 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with
7359 length and slicing, then `sum` uses the
7360 $(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation)
7361 algorithm.)
7362 $(LI If `ElementType!R` is a floating-point type and `R` is a
7363 finite input range (but not a random-access range with slicing), then
7364 `sum` uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation,
7365 Kahan summation) algorithm.)
7366 $(LI In all other cases, a simple element by element addition is done.)
7367 )
7368 
7369 For floating point inputs, calculations are made in
7370 $(DDLINK spec/type, Types, `real`)
7371 precision for `real` inputs and in `double` precision otherwise
7372 (Note this is a special case that deviates from `fold`'s behavior,
7373 which would have kept `float` precision for a `float` range).
7374 For all other types, the calculations are done in the same type obtained
7375 from from adding two elements of the range, which may be a different
7376 type from the elements themselves (for example, in case of
7377 $(DDSUBLINK spec/type,integer-promotions, integral promotion)).
7378 
7379 A seed may be passed to `sum`. Not only will this seed be used as an initial
7380 value, but its type will override all the above, and determine the algorithm
7381 and precision used for summation. If a seed is not passed, one is created with
7382 the value of `typeof(r.front + r.front)(0)`, or `typeof(r.front + r.front).zero`
7383 if no constructor exists that takes an int.
7384 
7385 Note that these specialized summing algorithms execute more primitive operations
7386 than vanilla summation. Therefore, if in certain cases maximum speed is required
7387 at expense of precision, one can use `fold!((a, b) => a + b)(r, 0)`, which
7388 is not specialized for summation.
7389 
7390 Params:
7391     seed = the initial value of the summation
7392     r = a finite input range
7393 
7394 Returns:
7395     The sum of all the elements in the range r.
7396  */
7397 auto sum(R)(R r)
7398 if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))
7399 {
7400     alias E = Unqual!(ElementType!R);
7401     static if (isFloatingPoint!E)
7402         alias Seed = typeof(E.init  + 0.0); //biggest of double/real
7403     else
7404         alias Seed = typeof(r.front + r.front);
7405     static if (is(typeof(Unqual!Seed(0))))
7406         enum seedValue = Unqual!Seed(0);
7407     else static if (is(typeof({ Unqual!Seed tmp = Seed.zero; })))
7408         enum Unqual!Seed seedValue = Seed.zero;
7409     else
7410         static assert(false,
7411             "Could not initiate an initial value for " ~ (Unqual!Seed).stringof
7412             ~ ". Please supply an initial value manually.");
7413     return sum(r, seedValue);
7414 }
7415 /// ditto
7416 auto sum(R, E)(R r, E seed)
7417 if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
7418 {
7419     static if (isFloatingPoint!E)
7420     {
7421         static if (hasLength!R && hasSlicing!R)
7422         {
7423             if (r.empty) return seed;
7424             return seed + sumPairwise!E(r);
7425         }
7426         else
7427             return sumKahan!E(seed, r);
7428     }
7429     else
7430     {
7431         return reduce!"a + b"(seed, r);
7432     }
7433 }
7434 
7435 /// Ditto
7436 @safe pure nothrow unittest
7437 {
7438     import std.range;
7439 
7440     //simple integral sumation
7441     assert(sum([ 1, 2, 3, 4]) == 10);
7442 
7443     //with integral promotion
7444     assert(sum([false, true, true, false, true]) == 3);
7445     assert(sum(ubyte.max.repeat(100)) == 25500);
7446 
7447     //The result may overflow
7448     assert(uint.max.repeat(3).sum()           ==  4294967293U );
7449     //But a seed can be used to change the sumation primitive
7450     assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
7451 
7452     //Floating point sumation
7453     assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
7454 
7455     //Floating point operations have double precision minimum
7456     static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
7457     assert(sum([1F, 2, 3, 4]) == 10);
7458 
7459     //Force pair-wise floating point sumation on large integers
7460     import std.math.operations : isClose;
7461     assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
7462                .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
7463 }
7464 
7465 // Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
7466 private auto sumPairwise(F, R)(R data)
7467 if (isInputRange!R && !isInfinite!R)
7468 {
7469     import core.bitop : bsf;
7470     // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t
7471     // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes
7472     // from the manual unrolling in sumPairWise16
7473     F[64] store = void;
7474     size_t idx = 0;
7475 
7476     void collapseStore(T)(T k)
7477     {
7478         auto lastToKeep = idx - cast(uint) bsf(k+1);
7479         while (idx > lastToKeep)
7480         {
7481             store[idx - 1] += store[idx];
7482             --idx;
7483         }
7484     }
7485 
7486     static if (hasLength!R)
7487     {
7488         foreach (k; 0 .. data.length / 16)
7489         {
7490             static if (isRandomAccessRange!R && hasSlicing!R)
7491             {
7492                 store[idx] = sumPairwise16!F(data);
7493                 data = data[16 .. data.length];
7494             }
7495             else store[idx] = sumPairwiseN!(16, false, F)(data);
7496 
7497             collapseStore(k);
7498             ++idx;
7499         }
7500 
7501         size_t i = 0;
7502         foreach (el; data)
7503         {
7504             store[idx] = el;
7505             collapseStore(i);
7506             ++idx;
7507             ++i;
7508         }
7509     }
7510     else
7511     {
7512         size_t k = 0;
7513         while (!data.empty)
7514         {
7515             store[idx] = sumPairwiseN!(16, true, F)(data);
7516             collapseStore(k);
7517             ++idx;
7518             ++k;
7519         }
7520     }
7521 
7522     F s = store[idx - 1];
7523     foreach_reverse (j; 0 .. idx - 1)
7524         s += store[j];
7525 
7526     return s;
7527 }
7528 
7529 private auto sumPairwise16(F, R)(R r)
7530 if (isRandomAccessRange!R)
7531 {
7532     return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3]))
7533           + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7])))
7534          + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11]))
7535           + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15])));
7536 }
7537 
7538 private auto sumPair(bool needEmptyChecks, F, R)(ref R r)
7539 if (isForwardRange!R && !isRandomAccessRange!R)
7540 {
7541     static if (needEmptyChecks) if (r.empty) return F(0);
7542     F s0 = r.front;
7543     r.popFront();
7544     static if (needEmptyChecks) if (r.empty) return s0;
7545     s0 += r.front;
7546     r.popFront();
7547     return s0;
7548 }
7549 
7550 private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
7551 if (isForwardRange!R && !isRandomAccessRange!R)
7552 {
7553     import std.math.traits : isPowerOf2;
7554     static assert(isPowerOf2(N), "N must be a power of 2");
7555     static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
7556     else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
7557         + sumPairwiseN!(N/2, needEmptyChecks, F)(r);
7558 }
7559 
7560 // Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm
7561 private auto sumKahan(Result, R)(Result result, R r)
7562 {
7563     static assert(isFloatingPoint!Result && isMutable!Result, "The type of"
7564             ~ " Result must be a mutable floating point, not "
7565             ~ Result.stringof);
7566     Result c = 0;
7567     for (; !r.empty; r.popFront())
7568     {
7569         immutable y = r.front - c;
7570         immutable t = result + y;
7571         c = (t - result) - y;
7572         result = t;
7573     }
7574     return result;
7575 }
7576 
7577 @safe pure nothrow unittest
7578 {
7579     static assert(is(typeof(sum([cast( byte) 1])) ==  int));
7580     static assert(is(typeof(sum([cast(ubyte) 1])) ==  int));
7581     static assert(is(typeof(sum([  1,   2,   3,   4])) ==  int));
7582     static assert(is(typeof(sum([ 1U,  2U,  3U,  4U])) == uint));
7583     static assert(is(typeof(sum([ 1L,  2L,  3L,  4L])) ==  long));
7584     static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong));
7585 
7586     int[] empty;
7587     assert(sum(empty) == 0);
7588     assert(sum([42]) == 42);
7589     assert(sum([42, 43]) == 42 + 43);
7590     assert(sum([42, 43, 44]) == 42 + 43 + 44);
7591     assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45);
7592 }
7593 
7594 @safe pure nothrow unittest
7595 {
7596     static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double));
7597     static assert(is(typeof(sum([ 1F,  2F,  3F,  4F])) == double));
7598     const(float[]) a = [1F, 2F, 3F, 4F];
7599     assert(sum(a) == 10F);
7600     static assert(is(typeof(sum(a)) == double));
7601 
7602     double[] empty;
7603     assert(sum(empty) == 0);
7604     assert(sum([42.]) == 42);
7605     assert(sum([42., 43.]) == 42 + 43);
7606     assert(sum([42., 43., 44.]) == 42 + 43 + 44);
7607     assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5);
7608 }
7609 
7610 @safe pure nothrow unittest
7611 {
7612     import std.container;
7613     static assert(is(typeof(sum(SList!float()[])) == double));
7614     static assert(is(typeof(sum(SList!double()[])) == double));
7615     static assert(is(typeof(sum(SList!real()[])) == real));
7616 
7617     assert(sum(SList!double()[]) == 0);
7618     assert(sum(SList!double(1)[]) == 1);
7619     assert(sum(SList!double(1, 2)[]) == 1 + 2);
7620     assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3);
7621     assert(sum(SList!double(1, 2, 3, 4)[]) == 10);
7622 }
7623 
7624 // https://issues.dlang.org/show_bug.cgi?id=12434
7625 @safe pure nothrow unittest
7626 {
7627     immutable a = [10, 20];
7628     auto s1 = sum(a);
7629     assert(s1 == 30);
7630     auto s2 = a.map!(x => x).sum;
7631     assert(s2 == 30);
7632 }
7633 
7634 @system unittest
7635 {
7636     import std.bigint;
7637     import std.range;
7638 
7639     immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array();
7640     immutable ulong[]  b = (ulong.max/2).repeat(10).array();
7641     auto sa = a.sum();
7642     auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint
7643     assert(sa == BigInt("10_000_000_000_000_000_000"));
7644     assert(sb == (BigInt(ulong.max/2) * 10));
7645 }
7646 
7647 @safe pure nothrow @nogc unittest
7648 {
7649     import std.range;
7650     foreach (n; iota(50))
7651         assert(repeat(1.0, n).sum == n);
7652 }
7653 
7654 // Issue 19525
7655 @safe unittest
7656 {
7657     import std.datetime : Duration, minutes;
7658     assert([1.minutes].sum() == 1.minutes);
7659 }
7660 
7661 /**
7662 Finds the mean (colloquially known as the average) of a range.
7663 
7664 For built-in numerical types, accurate Knuth & Welford mean calculation
7665 is used. For user-defined types, element by element summation is used.
7666 Additionally an extra parameter `seed` is needed in order to correctly
7667 seed the summation with the equivalent to `0`.
7668 
7669 The first overload of this function will return `T.init` if the range
7670 is empty. However, the second overload will return `seed` on empty ranges.
7671 
7672 This function is $(BIGOH r.length).
7673 
7674 Params:
7675     T = The type of the return value.
7676     r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
7677     seed = For user defined types. Should be equivalent to `0`.
7678 
7679 Returns:
7680     The mean of `r` when `r` is non-empty.
7681 */
7682 T mean(T = double, R)(R r)
7683 if (isInputRange!R &&
7684     isNumeric!(ElementType!R) &&
7685     !isInfinite!R)
7686 {
7687     if (r.empty)
7688         return T.init;
7689 
7690     Unqual!T meanRes = 0;
7691     size_t i = 1;
7692 
7693     // Knuth & Welford mean calculation
7694     // division per element is slower, but more accurate
7695     for (; !r.empty; r.popFront())
7696     {
7697         T delta = r.front - meanRes;
7698         meanRes += delta / i++;
7699     }
7700 
7701     return meanRes;
7702 }
7703 
7704 /// ditto
7705 auto mean(R, T)(R r, T seed)
7706 if (isInputRange!R &&
7707     !isNumeric!(ElementType!R) &&
7708     is(typeof(r.front + seed)) &&
7709     is(typeof(r.front / size_t(1))) &&
7710     !isInfinite!R)
7711 {
7712     import std.algorithm.iteration : sum, reduce;
7713 
7714     // per item division vis-a-vis the previous overload is too
7715     // inaccurate for integer division, which the user defined
7716     // types might be representing
7717     static if (hasLength!R)
7718     {
7719         if (r.length == 0)
7720             return seed;
7721 
7722         return sum(r, seed) / r.length;
7723     }
7724     else
7725     {
7726         import std.typecons : tuple;
7727 
7728         if (r.empty)
7729             return seed;
7730 
7731         auto pair = reduce!((a, b) => tuple(a[0] + 1, a[1] + b))
7732             (tuple(size_t(0), seed), r);
7733         return pair[1] / pair[0];
7734     }
7735 }
7736 
7737 ///
7738 @safe @nogc pure nothrow unittest
7739 {
7740     import std.math.operations : isClose;
7741     import std.math.traits : isNaN;
7742 
7743     static immutable arr1 = [1, 2, 3];
7744     static immutable arr2 = [1.5, 2.5, 12.5];
7745 
7746     assert(arr1.mean.isClose(2));
7747     assert(arr2.mean.isClose(5.5));
7748 
7749     assert(arr1[0 .. 0].mean.isNaN);
7750 }
7751 
7752 @safe pure nothrow unittest
7753 {
7754     import std.internal.test.dummyrange : ReferenceInputRange;
7755     import std.math.operations : isClose;
7756 
7757     auto r1 = new ReferenceInputRange!int([1, 2, 3]);
7758     assert(r1.mean.isClose(2));
7759 
7760     auto r2 = new ReferenceInputRange!double([1.5, 2.5, 12.5]);
7761     assert(r2.mean.isClose(5.5));
7762 }
7763 
7764 // Test user defined types
7765 @system pure unittest
7766 {
7767     import std.bigint : BigInt;
7768     import std.internal.test.dummyrange : ReferenceInputRange;
7769     import std.math.operations : isClose;
7770 
7771     auto bigint_arr = [BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")];
7772     auto bigint_arr2 = new ReferenceInputRange!BigInt([
7773         BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")
7774     ]);
7775     assert(bigint_arr.mean(BigInt(0)) == BigInt("3"));
7776     assert(bigint_arr2.mean(BigInt(0)) == BigInt("3"));
7777 
7778     BigInt[] bigint_arr3 = [];
7779     assert(bigint_arr3.mean(BigInt(0)) == BigInt(0));
7780 
7781     struct MyFancyDouble
7782     {
7783        double v;
7784        alias v this;
7785     }
7786 
7787     // both overloads
7788     auto d_arr = [MyFancyDouble(10), MyFancyDouble(15), MyFancyDouble(30)];
7789     assert(mean!(double)(cast(double[]) d_arr).isClose(18.33333333));
7790     assert(mean(d_arr, MyFancyDouble(0)).isClose(18.33333333));
7791 }
7792 
7793 // uniq
7794 /**
7795 Lazily iterates unique consecutive elements of the given range, which is
7796 assumed to be sorted (functionality akin to the
7797 $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
7798 utility). Equivalence of elements is assessed by using the predicate
7799 `pred`, by default `"a == b"`. The predicate is passed to
7800 $(REF binaryFun, std,functional), and can either accept a string, or any callable
7801 that can be executed via `pred(element, element)`. If the given range is
7802 bidirectional, `uniq` also yields a
7803 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
7804 
7805 Params:
7806     pred = Predicate for determining equivalence between range elements.
7807     r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
7808         elements to filter.
7809 
7810 Returns:
7811     An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
7812     consecutively unique elements in the original range. If `r` is also a
7813     forward range or bidirectional range, the returned range will be likewise.
7814 */
7815 auto uniq(alias pred = "a == b", Range)(Range r)
7816 if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool))
7817 {
7818     return UniqResult!(binaryFun!pred, Range)(r);
7819 }
7820 
7821 ///
7822 @safe unittest
7823 {
7824     import std.algorithm.comparison : equal;
7825     import std.algorithm.mutation : copy;
7826 
7827     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
7828     assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
7829 
7830     // Filter duplicates in-place using copy
7831     arr.length -= arr.uniq().copy(arr).length;
7832     assert(arr == [ 1, 2, 3, 4, 5 ]);
7833 
7834     // Note that uniqueness is only determined consecutively; duplicated
7835     // elements separated by an intervening different element will not be
7836     // eliminated:
7837     assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
7838 }
7839 
7840 private struct UniqResult(alias pred, Range)
7841 {
7842     Range _input;
7843 
7844     this(Range input)
7845     {
7846         _input = input;
7847     }
7848 
7849     auto opSlice()
7850     {
7851         return this;
7852     }
7853 
7854     void popFront()
7855     {
7856         assert(!empty, "Attempting to popFront an empty uniq.");
7857         auto last = _input.front;
7858         do
7859         {
7860             _input.popFront();
7861         }
7862         while (!_input.empty && pred(last, _input.front));
7863     }
7864 
7865     @property ElementType!Range front()
7866     {
7867         assert(!empty, "Attempting to fetch the front of an empty uniq.");
7868         return _input.front;
7869     }
7870 
7871     static if (isBidirectionalRange!Range)
7872     {
7873         void popBack()
7874         {
7875             assert(!empty, "Attempting to popBack an empty uniq.");
7876             auto last = _input.back;
7877             do
7878             {
7879                 _input.popBack();
7880             }
7881             while (!_input.empty && pred(last, _input.back));
7882         }
7883 
7884         @property ElementType!Range back()
7885         {
7886             assert(!empty, "Attempting to fetch the back of an empty uniq.");
7887             return _input.back;
7888         }
7889     }
7890 
7891     static if (isInfinite!Range)
7892     {
7893         enum bool empty = false;  // Propagate infiniteness.
7894     }
7895     else
7896     {
7897         @property bool empty() { return _input.empty; }
7898     }
7899 
7900     static if (isForwardRange!Range)
7901     {
7902         @property typeof(this) save() {
7903             return typeof(this)(_input.save);
7904         }
7905     }
7906 }
7907 
7908 @safe unittest
7909 {
7910     import std.algorithm.comparison : equal;
7911     import std.internal.test.dummyrange;
7912     import std.range;
7913 
7914     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
7915     auto r = uniq(arr);
7916     static assert(isForwardRange!(typeof(r)));
7917 
7918     assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
7919     assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
7920 
7921     foreach (DummyType; AllDummyRanges)
7922     {
7923         DummyType d;
7924         auto u = uniq(d);
7925         assert(equal(u, [1,2,3,4,5,6,7,8,9,10]));
7926 
7927         static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u)));
7928 
7929         static if (d.rt >= RangeType.Bidirectional)
7930         {
7931             assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
7932         }
7933     }
7934 }
7935 
7936 // https://issues.dlang.org/show_bug.cgi?id=17264
7937 @safe unittest
7938 {
7939     import std.algorithm.comparison : equal;
7940 
7941     const(int)[] var = [0, 1, 1, 2];
7942     assert(var.uniq.equal([0, 1, 2]));
7943 }
7944 
7945 /**
7946 Lazily computes all _permutations of `r` using $(HTTP
7947 en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
7948 
7949 Params:
7950     Range = the range type
7951     r = the $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives)
7952     to find the permutations for.
7953 Returns:
7954     A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
7955     of elements of which are an $(REF indexed, std,range) view into `r`.
7956 
7957 See_Also:
7958 $(REF nextPermutation, std,algorithm,sorting).
7959 */
7960 Permutations!Range permutations(Range)(Range r)
7961 {
7962     static assert(isRandomAccessRange!Range, Range.stringof,
7963             " must be a RandomAccessRange");
7964     static assert(hasLength!Range, Range.stringof
7965             , " must have a length");
7966 
7967     return typeof(return)(r);
7968 }
7969 
7970 /// ditto
7971 struct Permutations(Range)
7972 {
7973     static assert(isRandomAccessRange!Range, Range.stringof,
7974             " must be a RandomAccessRange");
7975     static assert(hasLength!Range, Range.stringof
7976             , " must have a length");
7977 
7978     private size_t[] _indices, _state;
7979     private Range _r;
7980     private bool _empty;
7981 
7982     ///
7983     this(Range r)
7984     {
7985         import std.array : array;
7986         import std.range : iota;
7987 
7988         this._r = r;
7989         _state = r.length ? new size_t[r.length-1] : null;
7990         _indices = iota(size_t(r.length)).array;
7991         _empty = r.length == 0;
7992     }
7993     private this(size_t[] indices, size_t[] state, Range r, bool empty_)
7994     {
7995         _indices = indices;
7996         _state = state;
7997         _r = r;
7998         _empty = empty_;
7999     }
8000     /// Returns: `true` if the range is empty, `false` otherwise.
8001     @property bool empty() const pure nothrow @safe @nogc
8002     {
8003         return _empty;
8004     }
8005 
8006     /// Returns: the front of the range
8007     @property auto front()
8008     {
8009         import std.range : indexed;
8010         return _r.indexed(_indices);
8011     }
8012 
8013     ///
8014     void popFront()
8015     {
8016         void next(int n)
8017         {
8018             import std.algorithm.mutation : swap;
8019 
8020             if (n > _indices.length)
8021             {
8022                 _empty = true;
8023                 return;
8024             }
8025 
8026             if (n % 2 == 1)
8027                 swap(_indices[0], _indices[n-1]);
8028             else
8029                 swap(_indices[_state[n-2]], _indices[n-1]);
8030 
8031             if (++_state[n-2] == n)
8032             {
8033                 _state[n-2] = 0;
8034                 next(n+1);
8035             }
8036         }
8037 
8038         next(2);
8039     }
8040     /// Returns: an independent copy of the permutations range.
8041     auto save()
8042     {
8043         return typeof(this)(_indices.dup, _state.dup, _r.save, _empty);
8044     }
8045 }
8046 
8047 ///
8048 @safe unittest
8049 {
8050     import std.algorithm.comparison : equal;
8051     import std.range : iota;
8052     assert(equal!equal(iota(3).permutations,
8053         [[0, 1, 2],
8054          [1, 0, 2],
8055          [2, 0, 1],
8056          [0, 2, 1],
8057          [1, 2, 0],
8058          [2, 1, 0]]));
8059 }
8060 
8061 @safe unittest
8062 {
8063     import std.algorithm.comparison : equal;
8064     import std.range : ElementType;
8065     import std.array : array;
8066     auto p = [1, 2, 3].permutations;
8067     auto x = p.save.front;
8068     p.popFront;
8069     auto y = p.front;
8070     assert(x != y);
8071 }