1 /** Extensions to std.algorithm.
2     Copyright: Per Nordlöw 2018-.
3     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4     Authors: $(WEB Per Nordlöw)
5 
6     TODO: merge stuff from algorithm_ex_2.d
7 */
8 module nxt.algorithm_ex;
9 
10 /* version = show; */
11 
12 import std.traits : isArray, Unqual, isIntegral, CommonType, arity, isSomeString, isExpressionTuple;
13 import std.range.primitives : ElementType, isInputRange, isForwardRange, isBidirectionalRange, isRandomAccessRange, front;
14 import nxt.traits_ex : allSame;
15 import std.functional : binaryFun;
16 import std.algorithm.searching : find;
17 
18 version(show)
19 {
20     import nxt.dbgio : dbg;
21 }
22 
23 import std.range : dropOne;
24 alias tail = dropOne;
25 
26 /** This overload enables, when possible, lvalue return.
27  *
28  * BUG: this overload is not chosen over `std.algorithm.either` so function
29  * must currently be called `eitherRef` instead of `either`.
30  */
31 ref Ts[0] eitherRef(Ts...)(ref Ts a)
32 if (a.length != 0 &&
33     allSame!Ts)         // TODO: better trait for this?
34 {
35     static if (Ts.length == 1)
36         return a[0];
37     else
38         return a[0] ? a[0] : eitherRef(a[1 .. $]); // recurse
39 }
40 
41 ///
42 @safe pure /*TODO: nothrow*/ unittest
43 {
44     int x = 1, y = 2;
45     eitherRef(x, y) = 3;
46     assert(x == 3);
47     assert(y == 2);
48 }
49 
50 /** Returns: last argument if all arguments implicitly bool-convert to `true`
51  * otherwise `CommonType!T.init`.
52  *
53  * Similar to behaviour of `and` operator in dynamic languages such as of Lisp's
54  * `(and a...)` and Python's `a and ....`.
55  *
56  * TODO: Is inout Conversion!T the correct return value?
57 */
58 CommonType!T every(T...)(lazy T a)
59 if (T.length != 0)
60 {
61     auto a0 = a[0]();           // evaluate only once
62     static if (T.length == 1)
63         return a0;
64     else
65         return a0 ? every(a[1 .. $]) : CommonType!T.init; // recurse
66 }
67 
68 ///
69 @safe pure /*TODO: nothrow*/ unittest
70 {
71     assert(every(3) == 3);
72     assert(every(3, 4) == 4);
73     assert(every(0, 4) == 0);
74     assert(every(0, 0) == 0);
75     assert(every([0, 1], [1, 2]) == [1, 2]);
76     assert(every([0, 1], [1]) == [1]);
77     assert(every(`a`, `b`) == `b`);
78     assert(every(``, `b`) == `b`);
79     assert(every(cast(string)null, `b`) == cast(string)null);
80 }
81 
82 version(none) // WARNING disabled because I don't see any use of this for.
83 {
84     /** This overload enables, when possible, lvalue return.
85     */
86     auto ref every(T...)(ref T a)
87     if (T.length != 0 && allSame!T)
88     {
89         static if (T.length == 1)
90             return a[0];
91         else
92             return a[0] ? every(a[1 .. $]) : a[0]; // recurse
93     }
94 
95     ///
96     unittest
97     {
98         immutable p = 1, q = 2;
99         assert(every(p, q) == 2);
100 
101         int x = 1, y = 2;
102         every(x, y) = 3;
103         assert(x == 1);
104         assert(y == 3);
105     }
106 }
107 
108 /** Evaluate all `parts` possibly digesting `whole`.
109  *
110  * If all values of `parts` implicitly convert to `bool true`, return the values
111  * as an array, otherwise restore whole and return null.
112  */
113 CommonType!T[] tryEvery(S, T...)(ref S whole, lazy T parts)
114 if (T.length != 0)
115 {
116     auto wholeBackup = whole;
117     bool all = true;
118     alias R = typeof(return);
119     R results;
120     foreach (result; parts) // evaluate each part
121     {
122         if (result)
123             results ~= result;
124         else
125         {
126             all = false;
127             break;
128         }
129     }
130     if (all)
131         return results;        // ok that whole has been changed in caller scope
132     else
133     {
134         whole = wholeBackup; // restore whole in caller scope if any failed
135         return R.init;
136     }
137 }
138 
139 ///
140 unittest
141 {
142     auto whole = `xyz`;
143     import std.algorithm.searching : skipOver;
144 
145     assert(whole.tryEvery(whole.skipOver('x'),
146                           whole.skipOver('z')) == []); // failing match
147     assert(whole == `xyz`); // should restore whole
148 
149     assert(whole.tryEvery(whole.skipOver('x'),
150                           whole.skipOver('y'),
151                           whole.skipOver('w')) == []); // failing match
152     assert(whole == `xyz`); // should restore whole
153 
154     assert(whole.tryEvery(whole.skipOver('x'),
155                           whole.skipOver('y')) == [true, true]); // successful match
156     assert(whole == `z`); // should digest matching part
157 }
158 
159 import std.traits : isInstanceOf;
160 
161 import std.typecons : Nullable;
162 
163 /** Returns: `true` iff `a` has a value containing meaningful information.
164  */
165 bool hasContents(T)(in T a)
166 {
167     static if (isInstanceOf!(Nullable, T))
168         return !a.isNull;
169     else static if (isArray!T ||
170                     isSomeString!T)
171         return cast(bool)a.length; // see: http://stackoverflow.com/questions/18563414/empty-string-should-implicit-convert-to-bool-true/18566334?noredirect=1#18566334
172     else
173         return cast(bool)a;
174 }
175 
176 /** Reset `a` to its default value.
177  *
178  * Params:
179  *      T = the argument type, likely to be infered.
180  *      a = a reference to a T.
181  *
182  * See_Also: std.typecons.Nullable.nullify
183  */
184 auto ref reset(T)(ref T a) @trusted // pure nothrow
185 {
186     static if (isInstanceOf!(Nullable, T))
187         a.nullify();
188     else
189         return a = T.init;
190 }
191 
192 ///
193 @safe @nogc pure nothrow unittest
194 {
195     uint a = 159;
196     a.reset();
197     assert(a == a.init);
198 
199     string b = "bla";
200     b.reset();
201     assert(b == b.init);
202 }
203 
204 ///
205 unittest
206 {
207     import std.typecons : Nullable;
208     auto n = Nullable!(size_t,
209                        size_t.max)();
210     import nxt.predicates : isUntouched;
211     assert(n.isUntouched);
212     n = 0;
213     assert(!n.isUntouched);
214     assert(n == 0);
215     n.reset;
216     assert(n.isUntouched);
217 }
218 
219 import std.typecons : Tuple, tuple;
220 
221 /** Find `needles` in order in `haystack`. */
222 auto findInOrder(alias pred = `a == b`,
223                  alias finder = find,
224                  R,
225                  E...)(R haystack,
226                        E needles) /* @trusted pure nothrow */
227 {
228     import std.range.primitives : empty;
229     auto hit = haystack; // reference
230     foreach (needle; needles) // for every needle in order
231     {
232         hit = finder!pred(hit, needle);
233         if (hit.empty)
234             break;
235     }
236     return hit;
237 }
238 
239 ///
240 @safe pure nothrow @nogc unittest
241 {
242     import std.range.primitives : empty;
243     assert(`a b c`.findInOrder(`a`, `b`, `c`));
244     assert(`b a`.findInOrder(`a`, `b`).empty);
245 }
246 
247 /** Returns: If `range` is symmetric.
248  *
249  * See_Also: http://forum.dlang.org/thread/dlfeiszyweafpjiocplf@forum.dlang.org#post-vpzuaqxvtdpzpeuorxdl:40forum.dlang.org
250  * See_Also: https://stackoverflow.com/questions/21849580/equality-operator-in-favour-of-std-range-equal
251  *
252  * TODO: Test graphemes in `string` and `wstring`.
253  * TODO: Move to Phobos
254 */
255 bool isSymmetric(R)(R range)
256 if (isBidirectionalRange!(R))
257 {
258     size_t i = 0;
259     import std.range.primitives : empty;
260     while (!range.empty)
261     {
262         import std.range.primitives : front, back, popFront, popBack;
263         if (range.front != range.back)
264             return false;
265         range.popFront(); ++i;
266         if (range.empty)
267             break;
268         range.popBack(); ++i;
269     }
270     return true;
271 }
272 
273 ///
274 @safe pure unittest
275 {
276     assert(`dallassallad`.isSymmetric);
277     assert(!`ab`.isSymmetric);
278     assert(`a`.isSymmetric);
279     assert(`åäå`.isSymmetric);
280     assert(`áá`.isSymmetric);
281     assert(`åäå`.isSymmetric);
282     assert(``.isSymmetric);
283     assert([1, 2, 2, 1].isSymmetric);
284 }
285 
286 /** Returns: If `range` is a palindrome larger than `minLength`.
287  */
288 bool isPalindrome(R)(R range,
289                      size_t minLength = 0) // TODO: good value for minLength?
290 if (isBidirectionalRange!(R))
291 {
292     static if (isRandomAccessRange!R) // arrays excluding `char[]` and `wchar[]`
293         if (range.length < minLength)
294             return false;
295     size_t i = 0;
296     // TODO: reuse `isSymmetric`
297     import std.range.primitives : empty;
298     while (!range.empty)
299     {
300         import std.range.primitives : front, back, popFront, popBack;
301         if (range.front != range.back)
302             return false;
303         range.popFront(); ++i;
304         if (range.empty)
305             break;
306         range.popBack(); ++i;
307     }
308     return i >= minLength;
309 }
310 
311 ///
312 @safe pure unittest
313 {
314     assert(`dallassallad`.isPalindrome);
315     assert(!`ab`.isPalindrome);
316     assert(`a`.isPalindrome);
317     assert(`åäå`.isPalindrome);
318     assert(`áá`.isPalindrome);
319     assert(`åäå`.isPalindrome);
320     assert(``.isPalindrome);
321     assert([1, 2, 2, 1].s[].isPalindrome);
322 }
323 
324 import nxt.traits_ex : areEquable;
325 
326 /** Return true if $(D s1) is an Anagram of $(D s2).
327  *
328  * Equal arguments are not considered to be an anagrams of each other.
329  *
330  * TODO: Is there a faster way of calculating anagrams?
331  * TODO: Allow const input
332  * TODO: Move to Phobos std.algorithm.sorting.
333  * TODO: Should requirement isInputRange be relaxed?
334  *
335  * Note that implementations in http://rosettacode.org/wiki/Anagrams#D doesn't
336  * correctly handle multi-byte encoded characters in string and wstring.
337  */
338 auto isAnagramOf(R1, R2)(scope R1 r1, scope R2 r2) // TODO: nothrow
339 if (isInputRange!R1 &&
340     isInputRange!R2 &&
341     areEquable!(ElementType!R1,
342                 ElementType!R2))
343 {
344     immutable sortLimit = 0;
345     import std.range.primitives : empty;
346     if (r1.empty || r2.empty)
347         return false;
348     if (r1.length + r2.length < sortLimit)
349     {
350         import std.algorithm.comparison : equal;
351         import nxt.sort_ex : sorted; // NOTE allocates
352         return equal(r1.sorted,
353                      r2.sorted);
354     }
355     else
356     {
357         alias E1 = ElementType!R1;
358         alias E2 = ElementType!R2;
359         alias C = CommonType!(E1, E2);
360 
361         alias T = Tuple!(size_t, // R1 histogram bin count
362                          size_t); // R2 histogram bin count
363 
364         import std.traits : isNarrowString;
365         import std.utf : byUTF;
366 
367         // TODO: functionize
368         static if (isNarrowString!R1)
369             auto s1 = r1.byUTF!dchar;
370         else
371             auto s1 = r1; // TODO: avoid copying
372 
373         static if (isNarrowString!R2)
374             auto s2 = r2.byUTF!dchar;
375         else
376             auto s2 = r2; // TODO: avoid copying
377 
378         // histogram
379         T[C] hist;              // TODO: use non-GC-allocating AA
380 
381         // create histograms
382         foreach (const ref e1; s1)
383         {
384             // TODO: functionize to hist.initOrUpdate(e1, T(0,1), (ref AA aa){ aa[0] += 1; })
385             if (auto hit = e1 in hist)
386                 (*hit)[0] += 1;
387             else
388                 hist[e1] = T(1, 0);
389         }
390         foreach (const ref e2; s2)
391         {
392             // TODO: functionize to hist.initOrUpdate(e2, T(0,1), (ref AA aa){ aa[1] += 1; })
393             if (auto hit = e2 in hist)
394                 (*hit)[1] += 1;
395             else
396                 hist[e2] = T(0, 1);
397         }
398 
399         // check histograms
400         foreach (const ref e; hist) // TODO: nothrow
401             if (e[0] != e[1])
402                 return false;
403         return true;
404     }
405 }
406 alias isPermutationOf = isAnagramOf; // TODO: Only define isAnagramOf for strings?
407 
408 ///
409 @safe pure unittest
410 {
411     assert([1, 2, 3, 4, 5].s[].isPermutationOf([1, 2, 4, 5, 3].s[]));
412     assert(![1, 2, 3, 4, 5].s[].isPermutationOf([1, 4, 5, 3].s[]));
413 }
414 
415 ///
416 @safe pure unittest
417 {
418     assert(!``w.isAnagramOf(``));
419     assert(`äöå`w.isAnagramOf(`åäö`));
420     assert(`äöå`.isAnagramOf(`åäö`w));
421     assert(`äöå`w.isAnagramOf(`åäö`w));
422 
423     assert(`äöå`d.isAnagramOf(`åäö`));
424     assert(`äöå`.isAnagramOf(`åäö`d));
425     assert(`äöå`d.isAnagramOf(`åäö`d));
426 }
427 
428 ///
429 @safe pure unittest
430 {
431     assert(`äöå`.isAnagramOf(`åäö`));
432     assert(!`äöå`.isAnagramOf(`xyz`));
433     assert(!`äöå`.isAnagramOf(``));
434     assert(!``.isAnagramOf(`åäö`));
435 }
436 @safe pure unittest
437 {
438     assert(`xyzxyzxyzxyzxyzxyzxyzxyz`.isAnagramOf(`xyzxyzxyzxyzxyzxyzxyzxyz`));
439     assert(`xyzxyzxyzxyzxyzxyzxyzxyz`.isAnagramOf(`xxyyzzxxyyzzxxyyzzxxyyzz`));
440 }
441 
442 ///
443 @safe pure unittest
444 {
445     import std.conv: to;
446 
447     auto x = `äöå`.to!(dchar[]);
448 
449     auto y = sort(x);
450     alias Y = typeof(y);
451 
452     immutable z = `åäö`;
453 
454     assert(y.isAnagramOf(z));
455     assert(z.isAnagramOf(y));
456 }
457 
458 /* ref Unqual!T unqual(T)(in T x) pure nothrow if isStuct!T { return cast(Unqual!T)x; } */
459 /* /// */
460 /* unittest { */
461 /*     const int x; */
462 /*     unqual(x) = 1; */
463 /* } */
464 
465 enum Reduction
466 {
467     forwardDifference,
468     backwardDifference,
469     sum,
470 }
471 
472 /** Generalized Windowed Reduce.
473  *
474  * See_Also: https://stackoverflow.com/questions/21004944/forward-difference-algorithm
475  * See_Also: http://forum.dlang.org/thread/ujouqtqeehkegmtaxebg@forum.dlang.org#post-lczzsypupcfigttghkwx:40forum.dlang.org
476  * See_Also: http://rosettacode.org/wiki/Forward_difference#D
477 */
478 auto ref windowedReduce(Reduction reduction = Reduction.forwardDifference, R)(R range)
479 if (isInputRange!R)
480 {
481     import std.algorithm.iteration: map;
482     import std.range: zip, dropOne;
483     auto ref op(T)(T a, T b) @safe pure nothrow
484     {
485         static      if (reduction == Reduction.forwardDifference)
486             return b - a; // TODO: final static switch
487         else static if (reduction == Reduction.backwardDifference)
488             return a - b;
489         else static if (reduction == Reduction.sum)
490             return a + b;
491     }
492     return range.zip(range.dropOne).map!(a => op(a[0], a[1])); // a is a tuple here
493 }
494 // NOTE: Disabled for now because this solution cannot be made nothrow
495 /* auto ref windowedReduce(Reduction reduction = Reduction.forwardDifference, uint N = 0, R)(R range) */
496 /*     @safe pure /\* nothrow *\/ if (isInputRange!R) */
497 /* { */
498 /*     auto ref helper(R range) @safe pure /\* nothrow *\/ { */
499 /*         import std.algorithm.iteration: map; */
500 /*         import std.range: zip, dropOne; */
501 /*         //  Note: that a[0] and a[1] indexes Zip tuple */
502 /*         static if (reduction == Reduction.forwardDifference) */
503 /*             return range.zip(range.dropOne).map!(a => a[1] - a[0]); */
504 /*         static if (reduction == Reduction.backwardDifference) */
505 /*             return range.zip(range.dropOne).map!(a => a[0] - a[1]); */
506 /*         static if (reduction == Reduction.sum) */
507 /*             return range.zip(range.dropOne).map!(a => a[0] + a[1]); */
508 /*     } */
509 /*     static if (N != 0) { */
510 /*         return windowedReduce!(reduction, N - 1)(helper(range)); */
511 /*     } else { */
512 /*         return helper(range); */
513 /*     } */
514 /* } */
515 
516 /* /// */
517 /* unittest { */
518 /*     import std.range: front; */
519 /*     dbg([1].windowedReduce!(Reduction.forwardDifference)); */
520 /*     dbg([1, 22].windowedReduce!(Reduction.forwardDifference)); */
521 /*     dbg([1, 22, 333].windowedReduce!(Reduction.forwardDifference)); */
522 /* } */
523 
524 ///
525 unittest
526 {
527     import std.datetime : Clock, SysTime;
528     SysTime[] times;
529     immutable n = 4;
530     foreach (_; 0 .. n)
531         times ~= Clock.currTime;
532     version(show) dbg(times);
533     auto spans = times.windowedReduce!(Reduction.forwardDifference);
534     version(show) dbg(spans);
535     // dbg(*(cast(ulong*)&(spans.front)));
536     version(show) import std.datetime : Duration;
537     version(show) dbg(Duration.sizeof);
538 }
539 
540 ///
541 @safe pure unittest
542 {
543     immutable i = [1, 4, 9, 17];
544     assert(i.windowedReduce!(Reduction.forwardDifference).equal ([+3, +5, +8]));
545     assert(i.windowedReduce!(Reduction.backwardDifference).equal([-3, -5, -8]));
546     assert(i.windowedReduce!(Reduction.sum).equal ([+5, +13, +26]));
547     assert([1].windowedReduce.empty);
548     version(show) dbg(i.windowedReduce!(Reduction.sum));
549 }
550 
551 /* TODO: Assert that ElementType!R only value semantics.  */
552 auto ref packBitParallelRunLengths(R)(in R x)
553 if (isInputRange!R)
554 {
555     import std.bitmanip: BitArray;
556     import core.bitop: bt;
557     alias E = ElementType!R; // element type
558     enum nBits = 8*E.sizeof;
559 
560     BitArray[nBits] runs;
561 
562     // allocate runs
563     foreach (ref run; runs)
564         run.length = x.length;
565 
566     /* string toString() @property @trusted const { */
567     /*     typeof(return) y; */
568     /*     import std.conv: to; */
569     /*     foreach (run; runs) { */
570     /*         y ~= run.to!string ~ "\n"; */
571     /*     } */
572     /*     return y; */
573     /* } */
574 
575     /* size_t[nBits] counts; */
576 
577     import nxt.static_bitarray : StaticBitArray;
578     import nxt.bitop_ex: testBit;
579     foreach (eltIx, elt; x)
580         /* StaticBitArray!nBits bits; */
581         foreach (bitIndex; 0..nBits)
582             runs[bitIndex][eltIx] = elt.testBit(bitIndex);
583     return runs;
584 }
585 alias packBPRL = packBitParallelRunLengths;
586 
587 ///
588 pure unittest
589 {
590     /* import backtrace.backtrace; */
591     /* import std.stdio: stderr; */
592     /* backtrace.backtrace.install(stderr); */
593     immutable x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
594     immutable xPacked = x.packBitParallelRunLengths;
595     version(show) dbg(xPacked);
596 }
597 
598 /** Compute Forward Difference of `range`.
599  *
600  * TODO: Is there a difference between whether `R r` is immutable, const or
601  * mutable?
602  *
603  * TODO: If `r` contains only one element return empty range.
604  *
605  * See_Also: https://stackoverflow.com/questions/21004944/forward-difference-algorithm
606  * See_Also: http://forum.dlang.org/thread/ujouqtqeehkegmtaxebg@forum.dlang.org#post-lczzsypupcfigttghkwx:40forum.dlang.org
607  * See_Also: http://rosettacode.org/wiki/Forward_difference#D
608  */
609 auto forwardDifference(R)(R r)
610 if (isInputRange!R)
611 {
612     import std.range: front, empty, popFront, dropOne;
613 
614     struct ForwardDifference
615     {
616         R _range;
617         alias E = ElementType!R;                       // Input ElementType
618         alias D = typeof(_range.front - _range.front); // Element Difference Type. TODO: Use this as ElementType of range
619         D _front;
620         bool _initialized = false;
621 
622         this(R range)
623         in(!range.empty)
624         {
625             auto tmp = range;
626             if (tmp.dropOne.empty) // TODO: This may be an unneccesary cost but is practical to remove extra logic
627                 _range = R.init; // return empty range
628             else
629                 _range = range; // store range internally (by reference)
630         }
631 
632         @property:
633         auto ref front()
634         {
635             if (!_initialized)
636                 popFront();
637             return _front;
638         }
639         auto ref moveFront()
640         {
641             popFront();
642             return _front;
643         }
644         void popFront()
645         {
646             if (empty is false)
647             {
648                 _initialized = true;
649                 E rf = _range.front;
650                 _range.popFront();
651                 if (_range.empty is false)
652                     _front = _range.front - rf;
653             }
654         }
655         bool empty() => _range.empty;
656     }
657 
658     return ForwardDifference(r);
659 }
660 
661 ///
662 unittest
663 {
664     auto x = [long.max, 0, 1];
665     auto y = x.forwardDifference;
666     version(show) dbg(y);
667 }
668 
669 import std.traits: isCallable, ReturnType, arity;
670 import nxt.traits_ex: arityMin0;
671 
672 /** Create Range of Elements Generated by `fun`.
673  *
674  * Use for example to generate random instances of return value of fun.
675  *
676  * TODO: I believe we need arityMin, arityMax trait here
677  */
678 auto apply(alias fun, N)(N n)
679 if (// TODO: isCallable!fun &&
680         arityMin0!fun &&
681         !is(ReturnType!fun == void) &&
682         isIntegral!N)
683 {
684     import std.range: iota;
685     import std.algorithm.iteration: map;
686     return n.iota.map!(n => fun);
687 }
688 
689 ///
690 unittest
691 {
692     import std.datetime: Clock;
693     import std.array: array;
694     immutable n = 3;
695     auto times = n.apply!(Clock.currTime).array;
696     version(show) dbg(times);
697     auto spans = times.forwardDifference;
698     version(show) dbg(spans);
699 }
700 
701 /** In Place Ordering (in Sorted Order) of all Elements `t`.
702  *
703  * See_Also: https://stackoverflow.com/questions/21102646/in-place-ordering-of-elements/
704  * See_Also: http://forum.dlang.org/thread/eweortsmcmibppmvtriw@forum.dlang.org#post-eweortsmcmibppmvtriw:40forum.dlang.org
705 */
706 void orderInPlace(T...)(ref T t) @trusted
707 {
708     import std.algorithm: sort, swap;
709     static if (t.length == 2)
710     {
711         if (t[0] > t[1])
712             swap(t[0], t[1]);
713     }
714     else
715     {                           // generic version
716         T[0][T.length] buffer;      // static buffer to capture elements
717         foreach (idx, a; t)
718             buffer[idx] = a;
719         auto sorted = sort(buffer[]);
720         foreach (idx, a; t)
721             t[idx] = sorted[idx];
722     }
723 }
724 
725 ///
726 unittest
727 {
728     auto x = 2, y = 1;
729     orderInPlace(x, y);
730     assert(x == 1);
731     assert(y == 2);
732 }
733 
734 ///
735 unittest
736 {
737     auto x = 3, y = 1, z = 2;
738     orderInPlace(x, y, z);
739     assert(x == 1);
740     assert(y == 2);
741     assert(z == 3);
742 }
743 
744 import std.algorithm: SwapStrategy;
745 
746 /** Allow static arrays to be sorted without [].
747  *
748  * See_Also: http://forum.dlang.org/thread/jhzurojjnlkatjdgcfhg@forum.dlang.org
749  */
750 template sort(alias less = `a < b`, SwapStrategy ss = SwapStrategy.unstable)
751 {
752     import std.algorithm: stdSort = sort;
753     auto sort(Arr)(ref Arr arr)
754     if (__traits(isStaticArray, Arr))
755     {
756         return stdSort!(less, ss)(arr[]);
757     }
758     auto sort(Range)(Range r)
759     if (!__traits(isStaticArray, Range))
760     {
761         return stdSort!(less, ss)(r);
762     }
763 }
764 
765 ///
766 unittest
767 {
768     int[5] a = [ 9, 5, 1, 7, 3 ];
769     int[]  b = [ 4, 2, 1, 6, 3 ];
770     sort(a);
771     sort(b);
772 }
773 
774 /** Stable Variant of Quick Sort.
775  *
776  * See_Also: http://forum.dlang.org/thread/gjuvmrypvxeebvztszpr@forum.dlang.org
777  */
778 auto ref stableSort(T)(auto ref T a) pure
779 if (isRandomAccessRange!T)
780 {
781     if (a.length >= 2)
782     {
783         import std.algorithm: partition3, sort;
784         auto parts = partition3(a, a[$ / 2]); // mid element as pivot
785         parts[0].sort();
786         parts[2].sort();
787     }
788     return a;
789 }
790 
791 ///
792 unittest
793 {
794     import nxt.random_ex : randInPlace;
795     immutable n = 2^^16;
796     auto a = new int[n];
797     a.randInPlace();
798     auto b = a.dup;
799     a[].stableSort;
800     import std.algorithm: sort;
801     sort(b);
802     assert(a == b);
803 }
804 
805 /** Execute Expression `expr` the same way `n` times. */
806 void doTimes(uint n, lazy void expr)
807 {
808     while (n--)
809         expr();
810 }
811 
812 /** Execute Expression `expr` $(I inline) the same way `n` times.
813     `n` must be a constant known at compile time.
814 */
815 void doTimes(uint n)(lazy void expr)
816 {
817     static foreach (i; 0 .. n)
818         expr();
819 }
820 
821 ///
822 unittest
823 {
824     int i = 0;
825     doTimes!4(i++);
826     assert(i == 4);
827 }
828 
829 alias loop = doTimes;
830 alias doN = doTimes;
831 alias repeat = doTimes;
832 
833 /** Execute Expression `action` the same way `n` times. */
834 void times(alias action, N)(N n)
835 if (isCallable!action &&
836     isIntegral!N &&
837     arity!action <= 1)
838 {
839     import std.traits: ParameterTypeTuple;
840     static if (arity!action == 1 && // if one argument and
841                isIntegral!(ParameterTypeTuple!action[0])) // its an integer
842         foreach (i; 0 .. n)
843             action(i); // use it as action input
844     else
845         foreach (i; 0 .. n)
846             action();
847 }
848 
849 ///
850 unittest
851 {
852     enum n = 10;
853     int sum = 0;
854     10.times!({ sum++; });
855     assert(sum == n);
856 }
857 
858 private string genNaryFun(string fun, V...)()
859 {
860     string code;
861     import std.string : format;
862     foreach (n, v; V)
863         code ~= "alias values[%d] %s;".format(n, cast(char)('a'+n));
864     code ~= `return ` ~ fun ~ `;`;
865     return code;
866 }
867 template naryFun(string fun)
868 {
869     auto naryFun(V...)(V values)
870     {
871         mixin(genNaryFun!(fun, V));
872     }
873 }
874 
875 ///
876 unittest
877 {
878     alias test = naryFun!`a + b + c`;
879     assert(test(1, 2, 3) == 6);
880 }
881 
882 import std.meta : allSatisfy;
883 
884 /** Zip `ranges` together with operation `fun`.
885  *
886  * TODO: Remove when Issue 8715 is fixed providing zipWith
887  */
888 auto zipWith(alias fun, Ranges...)(Ranges ranges)
889 if (Ranges.length >= 2 &&
890     allSatisfy!(isInputRange, Ranges))
891 {
892     import std.range: zip;
893     import std.algorithm.iteration: map;
894     static if (ranges.length < 2)
895         static assert(0, `Need at least 2 range arguments.`);
896     else static if (ranges.length == 2)
897         return zip(ranges).map!(a => binaryFun!fun(a.expand));
898     else
899         return zip(ranges).map!(a => naryFun!fun(a.expand));
900     // return zip(ranges).map!(a => fun(a.expand));
901 }
902 
903 ///
904 unittest
905 {
906     auto x = [1, 2, 3];
907     import std.array: array;
908     assert(zipWith!`a+b`(x, x).array == [2, 4, 6]);
909     assert(zipWith!((a, b) => a + b)(x, x).array == [2, 4, 6]);
910     assert(zipWith!`a+b+c`(x, x, x).array == [3, 6, 9]);
911 }
912 
913 auto zipWith(fun, StoppingPolicy, Ranges...)(StoppingPolicy sp,
914                                              Ranges ranges)
915 if (Ranges.length != 0 &&
916     allSatisfy!(isInputRange, Ranges))
917 {
918     import std.range: zip;
919     import std.algorithm.iteration: map;
920     return zip(sp, ranges).map!fun;
921 }
922 
923 /** Pair. TODO: std.typecons */
924 alias Pair(T, U) = Tuple!(T, U);
925 /** Instantiator for `Pair`. */
926 auto pair(T, U)(T t, U u) { return Pair!(T, U)(t, u); }
927 
928 /** Triple. TODO: std.typecons */
929 alias Triple(T, U, V) = Tuple!(T, U, V);
930 /** Instantiator for `Triple`. */
931 auto triple(T, U, V)(T t, U u, V v) { return Triple!(T, U, V)(t, u, v); }
932 
933 /** Quadruple. TODO: std.typecons */
934 alias Quadruple(T, U, V, W) = Tuple!(T, U, V, W);
935 /** Instantiator for `Quadruple`. */
936 auto quadruple(T, U, V, W)(T t, U u, V v, W w) { return Quadruple!(T, U, V, W)(t, u, v, w); }
937 
938 /** Limit/Span (Min,Max) Pair.
939  *
940  * TODO: Simultaneous min and max (minmax) can be optimized to 3/4 comparison.
941  * TODO: Decide on either Span, MinMax or Limits
942  * See_Also: https://stackoverflow.com/questions/21241878/generic-span-type-in-phobos
943  */
944 struct Limits(T)
945 {
946     import std.algorithm.comparison : min, max;
947 
948     @property:
949 
950     /** Expand Limits to include `a`. */
951     auto ref include(in T a) nothrow
952     {
953         _lims[0] = min(_lims[0], a);
954         _lims[1] = max(_lims[1], a);
955         return this;
956     }
957     alias expand = include;
958 
959     auto ref reset() nothrow
960     {
961         _lims[0] = T.max;
962         _lims[1] = T.min;
963     }
964 
965     string toString() const
966     {
967         import std.conv: to;
968         return (`[` ~ to!string(_lims[0]) ~
969                 `...` ~ to!string(_lims[1]) ~ `]`) ;
970     }
971 
972     auto _lims = tuple(T.max, T.min);
973 
974     alias _lims this;
975 }
976 
977 auto limits(T)() { return Limits!T(); }
978 
979 ///
980 unittest
981 {
982     /* import std.file: SysTime; */
983     /* SysTime st; */
984     Limits!int x;
985     x.expand(-10);
986     x.expand(10);
987     assert(x[0] == -10);
988     assert(x[1] == +10);
989     version(show) dbg(x);
990 }
991 
992 /* import rational: Rational; */
993 
994 /* template getTypeString(T) { */
995 /*     static if (is(T == Rational)) */
996 /*         string getTypeString(T)() @safe pure nothrow { */
997 /*             return x`211A`; */
998 /*         } */
999 /* } */
1000 /* /// */
1001 /* unittest { */
1002 /*     import rational: Rational; */
1003 /*     dbg(getTypeString!Rational); */
1004 /* } */
1005 
1006 /** Check if `a` and `b` are colinear. */
1007 bool areColinear(T)(T a, T b)
1008 {
1009     // a and b are colinear if a.x / a.y == b.x / b.y
1010     // We can avoid the division by multiplying out.
1011     return a.x * b.y == a.y * b.x;
1012 }
1013 
1014 /* /\** TODO: Remove when each is standard in Phobos. *\/ */
1015 /* void each(R)(R range, delegate x) @safe pure /\* nothrow *\/ if (isInputRange!R) { */
1016 /*     foreach (ref elt; range) { */
1017 /*         x(elt); */
1018 /*     } */
1019 /* } */
1020 /* /// */
1021 /* unittest { */
1022 /*     version(show) [1, 2, 3, 4].each(a => dbg(a)); */
1023 /* } */
1024 
1025 enum isIntLike(T) = is(typeof({T t = 0; t = t+t;})); // More if needed
1026 
1027 /** $(LUCKY Fibonacci) Numbers (Infinite Range).
1028     See_Also: http://forum.dlang.org/thread/dqlrfoxzsppylcgljyyf@forum.dlang.org#post-mailman.1072.1350619455.5162.digitalmars-d-learn:40puremagic.com
1029     See_Also: https://www.reddit.com/r/programming/comments/rif9x/uniform_function_call_syntax_for_the_d/
1030 */
1031 auto fibonacci(T = int)(T nth = 0)
1032 if (isIntLike!T)
1033 {
1034     static struct Result
1035     {
1036         T a, b;
1037         inout(T) front() inout => b;
1038         void popFront()
1039         {
1040             T c = a+b;
1041             a = b;
1042             b = c;
1043         }
1044         bool empty() const @property => false;
1045     }
1046     return nth == 0 ? Result(0, 1) : Result(1, 1);
1047 }
1048 
1049 ///
1050 unittest
1051 {
1052     import std.range: take;
1053     assert(fibonacci.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));
1054     assert(1.fibonacci.take(9).equal([1, 2, 3, 5, 8, 13, 21, 34, 55]));
1055 }
1056 
1057 /** Expand Static `array` into a parameter arguments (AliasSeq!).
1058     See_Also: http://forum.dlang.org/thread/hwellpcaomwbpnpofzlx@forum.dlang.org?page=1
1059 */
1060 template expand(alias array, size_t idx = 0)
1061 if (__traits(isStaticArray, typeof(array)))
1062 {
1063     @property ref delay() { return array[idx]; }
1064     static if (idx + 1 < array.length)
1065     {
1066         import std.meta : AliasSeq;
1067         alias expand = AliasSeq!(delay, expand!(array, idx + 1));
1068     }
1069     else
1070         alias expand = delay;
1071 }
1072 
1073 ///
1074 unittest
1075 {
1076     static void foo(int a, int b, int c)
1077     {
1078         version(show)
1079         {
1080             import std.stdio: writefln;
1081             writefln("a: %s, b: %s, c: %s", a, b, c);
1082         }
1083     }
1084     int[3] arr = [1, 2, 3];
1085     foo(expand!arr);
1086 }
1087 
1088 /** Python Style To-String-Conversion Alias. */
1089 string str(T)(in T a)
1090 {
1091     import std.conv: to;
1092     return to!string(a);
1093 }
1094 
1095 /** Python Style Length Alias. */
1096 auto len(T)(in T a)
1097 {
1098     return a.length;
1099 }
1100 
1101 ///
1102 unittest
1103 {
1104     import std.algorithm.iteration: map;
1105     import std.array: array;
1106     assert(([42].map!str).array == [`42`]);
1107 }
1108 
1109 import std.range: InputRange, OutputRange;
1110 alias Source = InputRange; // nicer alias
1111 alias Sink = OutputRange; // nicer alias
1112 
1113 /* belongs to std.range */
1114 import std.range: cycle, retro;
1115 import std.functional: compose;
1116 alias retroCycle = compose!(cycle, retro);
1117 
1118 import std.traits: isAggregateType;
1119 
1120 /** Generic Member Setter.
1121     See_Also: http://forum.dlang.org/thread/fdjkijrtduraaajlxxne@forum.dlang.org
1122 */
1123 auto ref T set(string member, T, U)(auto ref T a, in U value)
1124 if (isAggregateType!T &&
1125     __traits(hasMember, T, member))
1126 {
1127     __traits(getMember, a, member) = value;
1128     return a;
1129 }
1130 
1131 ///
1132 unittest
1133 {
1134     class C { int x, y, z, w; }
1135     auto c = new C().set!`x`(11).set!`w`(44);
1136     assert(c.x == 11);
1137     assert(c.w == 44);
1138 }
1139 
1140 /* Check if `part` is part of `whole`.
1141    See_Also: http://forum.dlang.org/thread/ls9dbk$jkq$1@digitalmars.com
1142    TODO: Standardize name and remove alises.
1143  */
1144 bool isSliceOf(T)(in T[] part,
1145                   in T[] whole)
1146 {
1147     return (whole.ptr <= part.ptr &&
1148             part.ptr + part.length <=
1149             whole.ptr + whole.length);
1150 }
1151 
1152 /* See_Also: http://forum.dlang.org/thread/cjpplpzdzebfxhyqtskw@forum.dlang.org#post-cjpplpzdzebfxhyqtskw:40forum.dlang.org */
1153 auto dropWhile(alias pred = `a == b`, R, E)(R range, E element)
1154 if (isInputRange!R &&
1155     is (typeof(binaryFun!pred(range.front, element)) : bool))
1156 {
1157     alias predFun = binaryFun!pred;
1158     return range.find!(a => !predFun(a, element));
1159 }
1160 alias dropAllOf = dropWhile;
1161 alias stripFront = dropWhile;
1162 alias lstrip = dropWhile;       // Python style
1163 
1164 import std.algorithm.searching: until;
1165 alias takeUntil = until;
1166 
1167 alias dropUntil = find;
1168 
1169 ///
1170 unittest
1171 {
1172     assert([1, 2, 3].dropWhile(1) == [2, 3]);
1173     assert([1, 1, 1, 2, 3].dropWhile(1) == [2, 3]);
1174     assert([1, 2, 3].dropWhile(2) == [1, 2, 3]);
1175     assert(`aabc`.dropWhile('a') == `bc`); // TODO: Remove restriction on cast to dchar
1176 }
1177 
1178 /* See_Also: http://forum.dlang.org/thread/cjpplpzdzebfxhyqtskw@forum.dlang.org#post-cjpplpzdzebfxhyqtskw:40forum.dlang.org */
1179 auto takeWhile(alias pred = `a == b`, R, E)(R range, E element)
1180 if (isInputRange!R &&
1181     is (typeof(binaryFun!pred(range.front, element)) : bool))
1182 {
1183     import std.algorithm: until;
1184     alias predFun = binaryFun!pred;
1185     return range.until!(a => !predFun(a, element));
1186 }
1187 alias takeAllOf = takeWhile;
1188 
1189 ///
1190 unittest
1191 {
1192     assert(equal([1, 1, 2, 3].takeWhile(1),
1193                  [1, 1]));
1194 }
1195 
1196 /** Simpler variant of Phobos' `split`. */
1197 auto split(alias pred, R)(R haystack)
1198 if (isForwardRange!R)
1199 {
1200     import std.range.primitives : empty;
1201     static if (isSomeString!R ||
1202                isRandomAccessRange!R)
1203     {
1204         auto balance = find!pred(haystack);
1205         immutable pos1 = haystack.length - balance.length;
1206         immutable pos2 = balance.empty ? pos1 : pos1 + 1;
1207         return tuple(haystack[0 .. pos1],
1208                      haystack[pos1 .. pos2],
1209                      haystack[pos2 .. haystack.length]);
1210     }
1211     else
1212     {
1213         import std.functional : unaryFun;
1214         auto original = haystack.save;
1215         auto h = haystack.save;
1216         size_t pos1, pos2;
1217         while (!h.empty)
1218         {
1219             if (unaryFun!pred(h.front))
1220             {
1221                 h.popFront();
1222                 ++pos2;
1223             }
1224             else
1225             {
1226                 haystack.popFront();
1227                 h = haystack.save;
1228                 pos2 = ++pos1;
1229             }
1230         }
1231         // TODO: use Voldemort struct instead of tuple
1232         return tuple(takeExactly(original, pos1),
1233                      takeExactly(haystack, pos2 - pos1),
1234                      h);
1235     }
1236 }
1237 
1238 ///
1239 unittest
1240 {
1241     import std.ascii: isDigit;
1242     assert(`aa1bb`.split!(a => a.isDigit) == tuple(`aa`, `1`, `bb`));
1243     assert(`aa1`.split!(a => a.isDigit) == tuple(`aa`, `1`, ``));
1244     assert(`1bb`.split!(a => a.isDigit) == tuple(``, `1`, `bb`));
1245 }
1246 
1247 /** Simpler variant of Phobos' `splitBefore`. */
1248 auto splitBefore(alias pred, R)(R haystack)
1249 if (isForwardRange!R)
1250 {
1251     static if (isSomeString!R ||
1252                sRandomAccessRange!R)
1253     {
1254         auto balance = find!pred(haystack);
1255         immutable pos = haystack.length - balance.length;
1256         return tuple(haystack[0 .. pos],
1257                      haystack[pos .. haystack.length]);
1258     }
1259     else
1260     {
1261         import std.functional : unaryFun;
1262         auto original = haystack.save;
1263         auto h = haystack.save;
1264         size_t pos;
1265         import std.range.primitives : empty;
1266         while (!h.empty)
1267             if (unaryFun!pred(h.front))
1268                 h.popFront();
1269             else
1270             {
1271                 haystack.popFront();
1272                 h = haystack.save;
1273                 ++pos;
1274             }
1275         // TODO: use Voldemort struct instead of tuple
1276         return tuple(takeExactly(original, pos),
1277                      haystack);
1278     }
1279 }
1280 
1281 ///
1282 unittest
1283 {
1284     import std.ascii: isDigit;
1285     assert(`11ab`.splitBefore!(a => !a.isDigit) == tuple(`11`, `ab`));
1286     assert(`ab`.splitBefore!(a => !a.isDigit) == tuple(``, `ab`));
1287 }
1288 
1289 auto splitAfter(alias pred, R)(R haystack)
1290 if (isForwardRange!R)
1291 {
1292     static if (isSomeString!R || isRandomAccessRange!R)
1293     {
1294         import std.range.primitives : empty;
1295         auto balance = find!pred(haystack);
1296         immutable pos = balance.empty ? 0 : haystack.length - balance.length + 1;
1297         // TODO: use Voldemort struct instead of tuple
1298         return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]);
1299     }
1300     else
1301     {
1302         static assert(0, `How to implement this?`);
1303         // import std.range.primitives : empty;
1304         /* auto original = haystack.save; */
1305         /* auto h = haystack.save; */
1306         /* size_t pos1, pos2; */
1307         /* while (!n.empty) */
1308         /* { */
1309         /*     if (h.empty) */
1310         /*     { */
1311         /*         // Failed search */
1312         /*         return tuple(takeExactly(original, 0), original); */
1313         /*     } */
1314         /*     if (binaryFun!pred(h.front, n.front)) */
1315         /*     { */
1316         /*         h.popFront(); */
1317         /*         n.popFront(); */
1318         /*         ++pos2; */
1319         /*     } */
1320         /*     else */
1321         /*     { */
1322         /*         haystack.popFront(); */
1323         /*         n = needle.save; */
1324         /*         h = haystack.save; */
1325         /*         pos2 = ++pos1; */
1326         /*     } */
1327         /* } */
1328         /* return tuple(takeExactly(original, pos2), h); */
1329     }
1330 }
1331 
1332 ///
1333 unittest
1334 {
1335     import std.ascii: isDigit;
1336     assert(`aa1bb`.splitAfter!(a => a.isDigit) == tuple(`aa1`, `bb`));
1337     assert(`aa1`.splitAfter!(a => a.isDigit) == tuple(`aa1`, ``));
1338 }
1339 
1340 auto moveUntil(alias pred, R)(ref R r)
1341 if (isInputRange!R)
1342 {
1343     auto split = r.splitBefore!pred;
1344     r = split[1];
1345     return split[0];
1346 }
1347 
1348 ///
1349 unittest
1350 {
1351     auto r = `xxx111`;
1352     auto id = r.moveUntil!(a => a == '1');
1353     assert(id == `xxx`);
1354     assert(r == `111`);
1355 }
1356 
1357 auto moveWhile(alias pred, R)(ref R r)
1358 if (isInputRange!R)
1359 {
1360     return r.moveUntil!(a => !pred(a));
1361 }
1362 
1363 ///
1364 unittest
1365 {
1366     auto r = `xxx111`;
1367     auto id = r.moveWhile!(a => a == 'x');
1368     assert(id == `xxx`);
1369     assert(r == `111`);
1370 }
1371 
1372 /** Variant of `findSplitBefore` that destructively pops everything up to,
1373     not including, `needle` from `haystack`.
1374 */
1375 auto findPopBefore(alias pred = `a == b`, R1, R2)(ref R1 haystack, R2 needle)
1376 if (isForwardRange!R1 &&
1377     isForwardRange!R2)
1378 {
1379     import std.range.primitives : empty;
1380 
1381     if (needle.empty)
1382         return haystack[0 .. 0]; // contextual empty hit
1383     if (haystack.empty)
1384         return R1.init;
1385 
1386     import std.algorithm.searching : findSplitBefore;
1387     if (auto split = findSplitBefore!pred(haystack, needle)) // TODO: If which case are empty and what return value should they lead to?
1388     {
1389         haystack = split[1];
1390         return split[0];
1391     }
1392     else
1393         return R1.init; // TODO: correct?
1394 }
1395 
1396 ///
1397 @safe pure nothrow @nogc unittest
1398 {
1399     auto haystack = `xy`;
1400     const needle = `z`;
1401     auto pop = haystack.findPopBefore(needle);
1402     assert(haystack == `xy`);
1403     assert(pop == ``);
1404 }
1405 
1406 ///
1407 @safe pure nothrow @nogc unittest
1408 {
1409     auto haystack = `xyz`;
1410     const needle = `y`;
1411     auto pop = haystack.findPopBefore(needle);
1412     assert(pop == `x`);
1413     assert(haystack == `yz`);
1414 }
1415 
1416 /** Variant of `findSplitAfter` that destructively pops everything up to,
1417     including, `needle` from `haystack`.
1418 */
1419 auto findPopAfter(alias pred = `a == b`, R1, R2)(ref R1 haystack, R2 needle)
1420 if (isForwardRange!R1 &&
1421     isForwardRange!R2)
1422 {
1423     import std.range.primitives : empty;
1424 
1425     if (needle.empty)
1426         return haystack[0 .. 0]; // contextual empty hit
1427     if (haystack.empty)
1428         return R1.init;
1429 
1430     import std.algorithm.searching : findSplitAfter;
1431     auto split = findSplitAfter!pred(haystack, needle);// TODO: use new interface to findSplitAfter
1432     if (split[0].empty)
1433         return R1.init; // TODO: correct?
1434     else
1435     {
1436         haystack = split[1];
1437         return split[0];
1438     }
1439 }
1440 
1441 ///
1442 @safe pure nothrow @nogc unittest
1443 {
1444     auto source = `xyz`;
1445     auto haystack = source;
1446     const needle = `y`;
1447     auto pop = haystack.findPopAfter(needle);
1448     assert(pop == `xy`);
1449     assert(haystack == `z`);
1450 }
1451 
1452 ///
1453 @safe pure nothrow @nogc unittest
1454 {
1455     auto source = `xy`;
1456     auto haystack = source;
1457     const needle = `z`;
1458     auto pop = haystack.findPopAfter(needle);
1459     assert(pop is null);
1460     assert(!pop);
1461     assert(haystack == source);
1462 }
1463 
1464 /** Find First Occurrence any of `needles` in `haystack`.
1465  *
1466  * Like to std.algorithm.find but takes an array of needles as argument instead
1467  * of a variadic list of key needle arguments.  Return found range plus index
1468  * into needles starting at 1 upon.
1469  */
1470 Tuple!(R, size_t) findFirstOfAnyInOrder(alias pred = `a == b`, R)(R haystack, const R[] needles)
1471 {
1472     import std.algorithm.searching : find;
1473     switch (needles.length)
1474     {
1475         case 1:
1476             import std.range.primitives : empty;
1477             auto hit = haystack.find(needles[0]);
1478             return tuple(hit, hit.empty ? 0UL : 1UL);
1479         case 2:
1480             return haystack.find(needles[0],
1481                                  needles[1]);
1482         case 3:
1483             return haystack.find(needles[0],
1484                                  needles[1],
1485                                  needles[2]);
1486         case 4:
1487             return haystack.find(needles[0],
1488                                  needles[1],
1489                                  needles[2],
1490                                  needles[3]);
1491         case 5:
1492             return haystack.find(needles[0],
1493                                  needles[1],
1494                                  needles[2],
1495                                  needles[3],
1496                                  needles[4]);
1497         case 6:
1498             return haystack.find(needles[0],
1499                                  needles[1],
1500                                  needles[2],
1501                                  needles[3],
1502                                  needles[4],
1503                                  needles[5]);
1504         case 7:
1505             return haystack.find(needles[0],
1506                                  needles[1],
1507                                  needles[2],
1508                                  needles[3],
1509                                  needles[4],
1510                                  needles[5],
1511                                  needles[6]);
1512         case 8:
1513             return haystack.find(needles[0],
1514                                  needles[1],
1515                                  needles[2],
1516                                  needles[3],
1517                                  needles[4],
1518                                  needles[5],
1519                                  needles[6],
1520                                  needles[7]);
1521         default:
1522             import std.conv: to;
1523             assert(0, `Too many keys ` ~ needles.length.to!string);
1524     }
1525 }
1526 
1527 ///
1528 @safe pure unittest
1529 {
1530     assert(`abc`.findFirstOfAnyInOrder([`x`]) == tuple(``, 0UL));
1531     assert(`abc`.findFirstOfAnyInOrder([`a`]) == tuple(`abc`, 1UL));
1532     assert(`abc`.findFirstOfAnyInOrder([`c`]) == tuple(`c`, 1UL));
1533     assert(`abc`.findFirstOfAnyInOrder([`a`, `b`]) == tuple(`abc`, 1UL));
1534     assert(`abc`.findFirstOfAnyInOrder([`a`, `b`]) == tuple(`abc`, 1UL));
1535     assert(`abc`.findFirstOfAnyInOrder([`x`, `b`]) == tuple(`bc`, 2UL));
1536 }
1537 
1538 Tuple!(R, size_t)[] findAllOfAnyInOrder(alias pred = `a == b`, R)(R haystack, R[] needles)
1539 {
1540     // TODO: Return some clever lazy range that calls each possible haystack.findFirstOfAnyInOrder(needles);
1541     return typeof(return).init;
1542 }
1543 
1544 /** Return true if all arguments `args` are strictly ordered, that is args[0] <
1545  * args[1] < args[2] < ... .
1546  *
1547  * TODO: CT-variant
1548  * See_Also: http://forum.dlang.org/thread/wzsdhzycwqyrvqmmttix@forum.dlang.org?page=2#post-vprvhifglfegnlvzqmjj:40forum.dlang.org
1549  */
1550 bool areStrictlyOrdered(Ts...)(Ts args)
1551 if (args.length >= 2 &&
1552     haveCommonType!Ts)
1553 {
1554     foreach (i, arg; args[1..$])
1555         if (args[i] >= arg)
1556             return false;
1557     return true;
1558 }
1559 
1560 ///
1561 @safe pure nothrow @nogc unittest
1562 {
1563     static assert(!__traits(compiles, areStrictlyOrdered()));
1564     static assert(!__traits(compiles, areStrictlyOrdered(1)));
1565     assert(areStrictlyOrdered(1, 2, 3));
1566     assert(!areStrictlyOrdered(1, 3, 2));
1567     assert(!areStrictlyOrdered(1, 2, 2));
1568     assert(areStrictlyOrdered('a', 'b', 'c'));
1569 }
1570 
1571 /** Return true if all arguments `args` are unstrictly ordered,
1572  * that is args[0] <= args[1] <= args[2] <= ... .
1573  *
1574  * TODO: CT-variant
1575  * See_Also: http://forum.dlang.org/thread/wzsdhzycwqyrvqmmttix@forum.dlang.org?page=2#post-vprvhifglfegnlvzqmjj:40forum.dlang.org
1576 */
1577 bool areUnstrictlyOrdered(Ts...)(Ts args)
1578 if (args.length >= 2 &&
1579     haveCommonType!Ts)
1580 {
1581     foreach (i, arg; args[1..$])
1582         if (args[i] > arg)
1583             return false;
1584     return true;
1585 }
1586 
1587 ///
1588 @safe pure nothrow @nogc unittest
1589 {
1590     static assert(!__traits(compiles, areUnstrictlyOrdered()));
1591     static assert(!__traits(compiles, areUnstrictlyOrdered(1)));
1592     assert(areUnstrictlyOrdered(1, 2, 2, 3));
1593     assert(!areUnstrictlyOrdered(1, 3, 2));
1594     assert(areUnstrictlyOrdered('a', 'b', 'c'));
1595 }
1596 
1597 import core.checkedint: addu, subu, mulu;
1598 
1599 alias sadd = addu;
1600 alias ssub = subu;
1601 alias smul = mulu;
1602 
1603 /** Distinct Elements of `r`.
1604  *
1605  * See_Also: http://forum.dlang.org/thread/jufggxqwzhlsmhshtnfj@forum.dlang.org?page=2
1606  * See_Also: http://dpaste.dzfl.pl/7b4b37b490a7
1607  */
1608 auto distinct(R)(R r)
1609 if (isInputRange!(Unqual!R))
1610 {
1611     import std.traits: ForeachType;
1612     bool[ForeachType!R] seen; // TODO: Use containers.hashset.HashSet
1613     import std.algorithm.iteration: filter;
1614     return r.filter!((k)
1615                      {
1616                          if (k !in seen)
1617                              return false;
1618                          else
1619                              return seen[k] = true;
1620                      });
1621 }
1622 
1623 // unittest
1624 // {
1625 //     immutable first = [1, 0, 2, 1, 3];
1626 //     immutable second = [1,5,6];
1627 //     import std.range: chain, take;
1628 //     assert(equal(first.chain(second).distinct.take(5),
1629 //                  [1, 0, 2, 3, 5]));
1630 // }
1631 
1632 /** Returns: `true` iff `value` is equal to any of `values`, `false` otherwise. */
1633 bool isAmong(alias pred = (a, b) => a == b,
1634              Value,
1635              Values...)(Value value,
1636                         Values values)
1637 if (Values.length != 0)
1638 {
1639     import std.algorithm.comparison : among;
1640     return cast(bool)value.among!pred(values);
1641 }
1642 
1643 ///
1644 @safe pure nothrow @nogc unittest
1645 {
1646     assert(`b`.isAmong(`a`, `b`));
1647     assert(!`c`.isAmong(`a`, `b`));
1648 }
1649 
1650 import std.traits : isExpressionTuple;
1651 import nxt.traits_ex : haveCommonType;
1652 
1653 /** Returns: `true` iff `value` is equal to any of `values`, `false` otherwise. */
1654 template isAmong(values...)
1655 if (isExpressionTuple!values &&
1656     values.length != 0)
1657 {
1658     bool isAmong(Value)(Value value)
1659         if (haveCommonType!(Value, values))
1660     {
1661         switch (value)
1662         {
1663             foreach (uint i, v; values)
1664             case v:
1665                 return true;
1666         default:
1667             return false;
1668         }
1669     }
1670 }
1671 
1672 ///
1673 @safe pure nothrow @nogc unittest
1674 {
1675     assert(`b`.isAmong!(`a`, `b`));
1676     assert(!`c`.isAmong!(`a`, `b`));
1677 }
1678 
1679 import std.algorithm.setops : cartesianProduct;
1680 
1681 /** More descriptive alias. */
1682 alias elementCombinations = cartesianProduct;
1683 
1684 /** Reset all members in aggregate instance `c`.
1685  *
1686  * See_Also: http://forum.dlang.org/post/ckitmpguywfitgadfpkv@forum.dlang.org
1687  * See_Also: http://forum.dlang.org/post/fbs8b5$5bu$1@digitalmars.com
1688  */
1689 void resetAllMembers(T)(T c)
1690 if (is(T == class))
1691 {
1692     foreach (ref m; c.tupleof)
1693     {
1694         import std.traits : isMutable;
1695         alias M = typeof(m);
1696         static if (isMutable!M)
1697             m = M.init;
1698     }
1699 }
1700 
1701 ///
1702 @safe pure nothrow unittest
1703 {
1704     class C
1705     {
1706         this (int a, int b, string c)
1707         {
1708             this.a = a;
1709             this.b = b;
1710             this.c = c;
1711         }
1712         int a; int b; string c;
1713     }
1714     void f(C c)
1715     {
1716         c.resetAllMembers();
1717     }
1718     auto c = new C(1, 2, "3");
1719     assert(c.a == 1);
1720     assert(c.b == 2);
1721     assert(c.c == "3");
1722     f(c);
1723     assert(c.a == 0);
1724     assert(c.b == 0);
1725     assert(c.c == null);
1726 }
1727 
1728 /** Returns: `true` iff `r` contains strictly values that are strictly increase
1729  * with the increment `step`.
1730  *
1731  * See_Also: http://forum.dlang.org/post/mqjyhvqxepgfljpkxvmd@forum.dlang.org
1732  */
1733 bool isLinearRamp(R)(R r, size_t step = 1)
1734 if (isInputRange!R &&
1735     isIntegral!(ElementType!R))
1736 {
1737     import std.algorithm.searching : findAdjacent;
1738     import std.range.primitives : empty;
1739     return r.findAdjacent!((a, b) => a + step != b).empty;
1740 }
1741 
1742 bool hasHoles(R)(R r)
1743 if (isInputRange!R &&
1744     isIntegral!(ElementType!R))
1745 {
1746     return !r.isLinearRamp;
1747 }
1748 
1749 ///
1750 @safe pure nothrow unittest
1751 {
1752     assert([1].isLinearRamp);
1753     assert([1, 2, 3].isLinearRamp);
1754     assert(![1, 1].isLinearRamp);
1755     assert(![1, 2, 1].isLinearRamp);
1756     assert(![1, 2, 4].isLinearRamp);
1757 }
1758 
1759 /** Check if `r` counts to exactly `exactCount` elements.
1760  */
1761 bool countsExactly(R)(R r, size_t exactCount) @("complexity", "O(exactCount)")
1762 if (isInputRange!R)
1763 {
1764     import std.range.primitives : hasLength;
1765     static if (hasLength!R)
1766         return r.length == exactCount;
1767     else
1768     {
1769         size_t n = 0;
1770         import std.range.primitives : empty;
1771         while (!r.empty)
1772         {
1773             r.popFront();
1774             if (++n > exactCount)
1775                 return false;
1776         }
1777         return n == exactCount;
1778     }
1779 }
1780 
1781 /** Check if `r` counts to at least `minCount` elements.
1782  */
1783 bool countsAtLeast(R)(R r, size_t minCount) @("complexity", "O(minCount)")
1784 if (isInputRange!R)
1785 {
1786     import std.range.primitives : hasLength;
1787     static if (hasLength!R)
1788         return r.length >= minCount;
1789     else
1790     {
1791         size_t n;
1792         import std.range.primitives : empty;
1793         while (!r.empty)
1794         {
1795             r.popFront();
1796             if (++n >= minCount)
1797                 return true;
1798         }
1799         return false;
1800     }
1801 }
1802 
1803 /** Check if `r` counts to at most `maxCount` elements.
1804  */
1805 bool countsAtMost(R)(R r, size_t maxCount) @("complexity", "O(maxCount)")
1806 if (isInputRange!R)
1807 {
1808     import std.range.primitives : hasLength;
1809     static if (hasLength!R)
1810         return r.length <= maxCount;
1811     else
1812     {
1813         size_t n;
1814         import std.range.primitives : empty;
1815         while (!r.empty)
1816         {
1817             r.popFront();
1818             if (++n > maxCount)
1819                 return false;
1820         }
1821         return true;
1822     }
1823 }
1824 
1825 ///
1826 @safe pure nothrow @nogc unittest
1827 {
1828     static void test(R)(R x)
1829         if (isInputRange!R)
1830     {
1831         import std.algorithm.searching : count;
1832         immutable n = x.count;
1833 
1834         // below
1835         foreach (immutable i; 0 .. n)
1836         {
1837             assert(x.countsAtLeast(i));
1838             assert(!x.countsExactly(i));
1839             assert(!x.countsAtMost(i));
1840         }
1841 
1842         // at
1843         assert(x.countsAtLeast(n));
1844         assert(x.countsExactly(n));
1845         assert(x.countsAtMost(n));
1846 
1847         // above
1848         foreach (immutable i; n + 1 .. n + 10)
1849         {
1850             assert(!x.countsAtLeast(i));
1851             assert(!x.countsExactly(i));
1852             assert(x.countsAtMost(i));
1853         }
1854     }
1855 
1856     import std.algorithm.iteration : filter;
1857     import std.range : iota;
1858 
1859     test(3.iota.filter!(x => true));
1860 
1861     int[3] x = [0, 1, 2];
1862     test(x[]);
1863 }
1864 
1865 import std.meta : allSatisfy;
1866 import std.range.primitives : hasLength;
1867 
1868 /** Returns: `true` iff `r` and all `ss` all have equal length.
1869  */
1870 bool equalLength(R, Ss...)(const R r, const Ss ss)
1871     @safe pure nothrow @nogc
1872 if (Ss.length != 0 &&
1873     allSatisfy!(hasLength, R, Ss))
1874 {
1875     foreach (const ref s; ss)
1876         if (r.length != s.length)
1877             return false;
1878     return true;
1879 }
1880 
1881 ///
1882 @safe pure nothrow unittest
1883 {
1884     assert(equalLength([1], [2], [3]));
1885     assert(!equalLength([1, 1], [2], [3]));
1886     assert(!equalLength([1], [2, 2], [3]));
1887     assert(!equalLength([1], [2], [3, 3]));
1888 }
1889 
1890 /** Collect/Gather the elements of `r` into a `Container` and return it.
1891  *
1892  * TODO: Use std.container.util.make instead?: https://dlang.org/phobos/std_container_util.html#.make
1893  * TODO: Rename `container` to `output`?
1894  * TODO: Support Set-containers via `insert` aswell, or add `alias put = insert` to them?
1895  * TODO: What about `Appender`?
1896  * TODO: Rename from `collect` to `gather`.
1897  */
1898 Container collect(Container, Range) (Range r)
1899     // if (isInputRange!Range &&
1900     //     isOutputRange!(Container, ElementType!Range))
1901 {
1902     static if (hasLength!Range)
1903     {
1904         static if (isArray!Container)
1905         {
1906             import std.array : uninitializedArray;
1907             auto output = uninitializedArray!(Container)(r.length);
1908         }
1909         else
1910         {
1911             Container output;
1912             output.length = r.length;
1913         }
1914         import std.algorithm.mutation : copy;
1915         r.copy(output[]);       // slicing is @trusted
1916         return output;
1917     }
1918     else
1919     {
1920         static if (isArray!Container)
1921         {
1922             import std.array : Appender;
1923             Appender!Container output;
1924             foreach (ref e; r)  // TODO: make const when this works with array_ex
1925                 output.put(e);
1926             return output.data;
1927         }
1928         else
1929         {
1930             Container output;
1931             foreach (ref e; r)  // TODO: make const when this works with array_ex
1932                 output ~= e; // TODO: use Appender or remove because too GC.intensive inefficient, or reuse `.array`?
1933             return output;
1934         }
1935     }
1936 }
1937 
1938 ///
1939 @safe pure nothrow unittest
1940 {
1941     import std.range : iota;
1942     import std.algorithm.iteration : map, filter;
1943     import nxt.algorithm_ex : collect;
1944 
1945     alias E = int;
1946     alias V = E[];
1947     immutable n = 1000;
1948 
1949     auto x = 0.iota(n).collect!V;
1950 
1951     assert([0, 1, 2].map!(_ => _^^2).collect!V.equal([0, 1, 4]));
1952     assert([0, 1, 2, 3].filter!(_ => _ & 1).collect!V.equal([1, 3]));
1953 }
1954 
1955 /** Returns: `x` eagerly split in two parts, all as equal in length as possible.
1956  *
1957  * Safely avoids range checking thanks to D's builtin slice expressions.
1958  * Use in divide-and-conquer algorithms such as quicksort and binary search.
1959  */
1960 auto spliced2(T)(T[] x) @trusted
1961 {
1962     static struct Result        // Voldemort type
1963     {
1964         T[] first;              // first half
1965         T[] second;             // second half
1966     }
1967     immutable m = x.length / 2;
1968     // range checking is not needed
1969     return Result(x.ptr[0 .. m], // can be @trusted
1970                   x.ptr[m .. x.length]); // can be @trusted
1971 }
1972 alias halved = spliced2;
1973 
1974 ///
1975 @safe pure nothrow @nogc unittest
1976 {
1977     immutable int[6] x = [0, 1, 2, 3, 4, 5];
1978     immutable y = x.spliced2;
1979     assert(y.first.equal(x[0 .. 3]));
1980     assert(y.second.equal(x[3 .. $]));
1981 }
1982 
1983 /** Returns: `x` eagerly split in three parts, all as equal in length as possible.
1984  *
1985  * Safely avoids range checking thanks to D's builtin slice expressions.
1986  * Use in divide-and-conquer algorithms such as quicksort and binary search.
1987  */
1988 auto spliced3(T)(T[] x) @trusted
1989 {
1990     enum count = 3;
1991     static struct Result        // Voldemort type
1992     {
1993         T[] first;              // first half
1994         T[] second;             // second half
1995         T[] third;              // third half
1996     }
1997     // range checking is not needed
1998     immutable m = 1*x.length/count;
1999     immutable n = 2*x.length/count;
2000     return Result(x.ptr[0 .. m], // can be @trusted
2001                   x.ptr[m .. n], // can be @trusted
2002                   x.ptr[n .. x.length]); // can be @trusted
2003 }
2004 
2005 @safe pure nothrow @nogc unittest
2006 {
2007     immutable int[6] x = [0, 1, 2, 3, 4, 5];
2008     immutable y = x.spliced3;
2009     assert(y.first.equal(x[0 .. 2]));
2010     assert(y.second.equal(x[2 .. 4]));
2011     assert(y.third.equal(x[4 .. 6]));
2012 }
2013 
2014 /** Splice `x` in `N` parts, all as equal in lengths as possible.
2015  *
2016  * Safely avoids range checking thanks to D's builtin slice expressions.
2017  * Use in divide-and-conquer algorithms such as quicksort and binary search.
2018  */
2019 auto splicerN(uint N, T)(T[] x) @trusted
2020 {
2021     static struct Result        // Voldemort type
2022     {
2023         enum count = N;
2024         pragma(inline) @trusted pure nothrow @nogc:
2025 
2026         /// Returns: `i`:th slice.
2027         inout(T)[] at(uint i)() inout
2028         {
2029             static assert(i < N, "Index " ~ i ~ " to large");
2030             static if (i == 0)
2031                 return _.ptr[0 .. (i + 1)*_.length/N]; // can be @trusted
2032             else static if (i + 1 == N)
2033                 return _.ptr[i * _.length/N .. _.length]; // can be @trusted
2034             else
2035                 return _.ptr[i * _.length/N .. (i + 1)*_.length/N]; // non-range-checked can be @trusted
2036         }
2037 
2038         private T[] _;
2039     }
2040     return Result(x);
2041 }
2042 
2043 ///
2044 @safe pure nothrow @nogc unittest
2045 {
2046     enum count = 6;
2047 
2048     immutable int[count] x = [0, 1, 3, 3, 4, 5];
2049     immutable y = x.splicerN!count;
2050 
2051     static foreach (i; 0 .. count)
2052     {
2053         assert(y.at!i.equal(x[i .. i + 1]));
2054     }
2055 }
2056 
2057 /** Specialization of `splicerN` to `N` being 2. */
2058 auto splicer2(T)(T[] x) @trusted
2059 {
2060     static struct Result        // Voldemort type
2061     {
2062         enum count = 2;
2063         pragma(inline) @trusted pure nothrow @nogc:
2064 
2065         /// Returns: first part of splice.
2066         @property inout(T)[] first() inout
2067         {
2068             return _.ptr[0 .. _.length/count]; // can be @trusted
2069         }
2070 
2071         /// Returns: second part of splice.
2072         @property inout(T)[] second() inout
2073         {
2074             return _.ptr[_.length/count .. _.length]; // can be @trusted
2075         }
2076 
2077         inout(T)[] at(uint i)() inout
2078         {
2079             static assert(i < count, "Index " ~ i ~ " to large");
2080             static      if (i == 0)
2081                 return first;
2082             else static if (i == 1)
2083                 return second;
2084         }
2085 
2086         private T[] _;
2087     }
2088     return Result(x);
2089 }
2090 
2091 ///
2092 @safe pure nothrow @nogc unittest
2093 {
2094     immutable int[6] x = [0, 1, 2, 3, 4, 5];
2095     immutable y = x.splicer2;
2096     assert(y.first.equal(x[0 .. 3]));
2097     assert(y.second.equal(x[3 .. $]));
2098     assert(y.at!0.equal(x[0 .. 3]));
2099     assert(y.at!1.equal(x[3 .. $]));
2100 }
2101 
2102 /** Specialization of `splicerN` to `N` being 3. */
2103 auto splicer3(T)(T[] x) @trusted
2104 {
2105     static struct Result        // Voldemort type
2106     {
2107         enum count = 3;
2108         pragma(inline) @trusted pure nothrow @nogc:
2109 
2110         /// Returns: first part of splice.
2111         @property inout(T)[] first() inout
2112         {
2113             return _.ptr[0 .. _.length/count];  // can be @trusted
2114         }
2115 
2116         /// Returns: second part of splice.
2117         @property inout(T)[] second() inout
2118         {
2119             return _.ptr[_.length/count .. 2*_.length/count];  // can be @trusted
2120         }
2121 
2122         /// Returns: third part of splice.
2123         @property inout(T)[] third() inout
2124         {
2125             return _.ptr[2*_.length/count .. _.length]; // can be @trusted
2126         }
2127 
2128         private T[] _;
2129     }
2130     return Result(x);
2131 }
2132 
2133 ///
2134 @safe pure nothrow @nogc unittest
2135 {
2136     immutable int[6] x = [0, 1, 3, 3, 4, 5];
2137     immutable y = x.splicer3;
2138     assert(y.first.equal(x[0 .. 2]));
2139     assert(y.second.equal(x[2 .. 4]));
2140     assert(y.third.equal(x[4 .. $]));
2141 }
2142 
2143 /**
2144    See_Also: http://forum.dlang.org/post/mqfaevkvhwwtzaafrtve@forum.dlang.org
2145  */
2146 auto use(alias F, T)(T t) if (is(typeof(F(T.init)))) => F(t);
2147 
2148 @safe pure nothrow @nogc unittest
2149 {
2150     foreach (const i; 1 .. 11)
2151         foreach (const j; 1 .. 11)
2152             immutable result = (i * j).use!(x => x*x);
2153 }
2154 
2155 import nxt.char_traits : isASCII;
2156 
2157 /** TOOD Merge into Phobos' startsWith. */
2158 template startsWith(needles...)
2159 if (isExpressionTuple!needles &&
2160     needles.length != 0)
2161 {
2162     uint startsWith(Haystack)(Haystack haystack) @trusted
2163         if (!is(CommonType!(typeof(Haystack.front), needles) == void))
2164     {
2165         if (haystack.length == 0)
2166             return 0;
2167         static if (isArray!Haystack &&
2168                    is(typeof(Haystack.init[0]) : char) &&
2169                    allSatisfy!(isASCII, needles))
2170         {
2171             // no front decoding needed
2172             static if (needles.length == 1)
2173                 return haystack.ptr[0] == needles[0] ? 1 : 0;
2174             else
2175             {
2176                 import std.algorithm.comparison : among;
2177                 return haystack.ptr[0].among!(needles);
2178             }
2179         }
2180         else
2181         {
2182             import std.range.primitives : front; // need decoding
2183             import std.algorithm.comparison : among;
2184             return haystack.front.among!(needles);
2185         }
2186     }
2187 }
2188 
2189 @safe pure nothrow @nogc unittest
2190 {
2191     const haystack = "abc";
2192     assert(haystack.startsWith!('a'));
2193     assert(!haystack.startsWith!('b'));
2194 }
2195 
2196 @safe pure unittest
2197 {
2198     const haystack = "äbc";
2199     assert(haystack.startsWith!('ä'));
2200 }
2201 
2202 @safe pure nothrow @nogc unittest
2203 {
2204     const haystack = "abc";
2205     assert(haystack.startsWith!('b') == 0);
2206     assert(haystack.startsWith!('a') == 1);
2207     assert(haystack.startsWith!('_',
2208                                 'a') == 2);
2209 }
2210 
2211 /** TOOD Merge into Phobos' endsWith. */
2212 template endsWith(needles...)
2213 if (isExpressionTuple!needles &&
2214     needles.length != 0)
2215 {
2216     uint endsWith(Haystack)(Haystack haystack)
2217         @trusted
2218     if (!is(CommonType!(typeof(Haystack.back), needles) == void))
2219     {
2220         if (haystack.length == 0)
2221             return 0;
2222         static if (isArray!Haystack &&
2223                    is(Unqual!(typeof(Haystack.init[0])) == char) && // TODO: reuse existing trait
2224                    allSatisfy!(isASCII, needles))
2225         {
2226             // no back decoding needed
2227             static if (needles.length == 1)
2228                 return haystack.ptr[haystack.length - 1] == needles[0] ? 1 : 0;
2229             else
2230             {
2231                 import std.algorithm.comparison : among;
2232                 return haystack.ptr[haystack.length - 1].among!(needles);
2233             }
2234         }
2235         else
2236         {
2237             import std.range.primitives : back; // need decoding
2238             import std.algorithm.comparison : among;
2239             return haystack.back.among!(needles);
2240         }
2241     }
2242 }
2243 
2244 @safe pure nothrow @nogc unittest
2245 {
2246     const haystack = "abc";
2247     assert(haystack.endsWith!('b') == 0);
2248     assert(haystack.endsWith!('c') == 1);
2249     assert(haystack.endsWith!('_',
2250                               'c') == 2);
2251 }
2252 
2253 @safe pure unittest
2254 {
2255     const haystack = "abć";
2256     assert(haystack.endsWith!('ć'));
2257 }
2258 
2259 /**
2260  * Allows forbidden casts.
2261  *
2262  * Params:
2263  *      OT = The output type.
2264  *      IT = The input type, optional, likely to be infered.
2265  *      it = A reference to an IT.
2266  *
2267  * Returns:
2268  *      the same as $(D cast(OT) it), except that it never fails to compile.
2269  */
2270 auto bruteCast(OT, IT)(auto ref IT it) => *cast(OT*) &it;
2271 
2272 ///
2273 nothrow pure @nogc unittest
2274 {
2275     static immutable array = [0u,1u,2u];
2276     size_t len;
2277     //len = cast(uint) array; // not allowed.
2278     len = bruteCast!uint(array);
2279     assert(len == array.length);
2280 }
2281 
2282 /** Split `range` using multiple separators stored as elements in `separators`.
2283  *
2284  * See_Also: https://forum.dlang.org/post/nv60ra$9vc$1@digitalmars.com
2285  */
2286 auto splitterN(R, S)(scope return R range,
2287                      const scope S separators)
2288 {
2289     import std.algorithm.iteration : splitter;
2290     import std.algorithm.searching : canFind;
2291     return range.splitter!(element => separators.canFind(element));
2292 }
2293 
2294 ///
2295 @safe pure unittest
2296 {
2297     auto result = "a-b+c".splitterN("+-");
2298     assert(result.equal(["a", "b", "c"].s[]));
2299 }
2300 
2301 // splitter is nothrow @nogc when `haystack` is of same NarrowString as `needle`
2302 @safe pure nothrow @nogc unittest
2303 {
2304     import std.algorithm.iteration : splitter;
2305     assert("a b".splitter(" ").equal(["a", "b"].s[]));
2306 }
2307 
2308 version(unittest)
2309 {
2310     import std.algorithm.comparison : equal;
2311     import nxt.array_help : s;
2312 }