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