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