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