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