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