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