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