1 /**
2  * Array utility functions
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.utils.array;
15 
16 import std.algorithm.iteration;
17 import std.algorithm.mutation;
18 import std.algorithm.searching;
19 import std.algorithm.sorting;
20 import std.array;
21 import std.exception;
22 import std.format;
23 import std.functional;
24 import std.range.primitives;
25 import std.traits;
26 
27 import ae.utils.meta;
28 
29 public import ae.utils.aa;
30 public import ae.utils.appender;
31 
32 /// Slice a variable.
33 @property T[] asSlice(T)(ref T v)
34 {
35 	return (&v)[0..1];
36 }
37 
38 // deprecated alias toArray = asSlice;
39 // https://issues.dlang.org/show_bug.cgi?id=23968
40 deprecated T[] toArray(T)(ref T v) { return v.asSlice; }
41 
42 /// Wrap `v` into a static array of length 1.
43 @property T[1] asUnitStaticArray(T)(T v)
44 {
45 	return (&v)[0..1];
46 }
47 
48 /// ditto
49 @property ref T[1] asUnitStaticArray(T)(ref T v)
50 {
51 	return (&v)[0..1];
52 }
53 
54 debug(ae_unittest) unittest
55 {
56 	int[1] arr = 1.asUnitStaticArray;
57 	int i;
58 	assert(i.asUnitStaticArray.ptr == &i);
59 }
60 
61 /// std.array.staticArray polyfill
62 static if (__traits(hasMember, std.array, "staticArray"))
63 	public import std.array : staticArray;
64 else
65 	pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) { return a; }
66 
67 /// Convert a dynamic array to a static array
68 template toStaticArray(size_t n)
69 {
70 	ref T[n] toStaticArray(T)(return T[] a)
71 	{
72 		assert(a.length == n, "Size mismatch");
73 		return a[0 .. n];
74 	}
75 }
76 
77 debug(ae_unittest) unittest
78 {
79 	auto a = [1, 2, 3];
80 	assert(a.toStaticArray!3 == [1, 2, 3]);
81 
82 	import std.range : chunks;
83 	auto b = [1, 2, 3, 4];
84 	assert(b.chunks(2).map!(toStaticArray!2).array == [[1, 2], [3, 4]]);
85 }
86 
87 /// Return the value represented as an array of bytes.
88 @property inout(ubyte)[] asBytes(T)(ref inout(T) value)
89 	if (!hasIndirections!T)
90 {
91 	return value.asSlice.asBytes;
92 }
93 
94 /// ditto
95 @property inout(ubyte)[] asBytes(T)(inout(T) value)
96 	if (is(T U : U[]) && !hasIndirections!U)
97 {
98 	return cast(inout(ubyte)[])value;
99 }
100 
101 // deprecated alias bytes = asBytes;
102 // https://issues.dlang.org/show_bug.cgi?id=23968
103 deprecated @property inout(ubyte)[] bytes(T)(ref inout(T) value) if (!hasIndirections!T) { return value.asBytes; }
104 deprecated @property inout(ubyte)[] bytes(T)(inout(T) value) if (is(T U : U[]) && !hasIndirections!U) { return value.asBytes; }
105 
106 debug(ae_unittest) unittest
107 {
108 	ubyte b = 5;
109 	assert(b.asBytes == [5]);
110 
111 	struct S { ubyte b = 5; }
112 	S s;
113 	assert(s.asBytes == [5]);
114 
115 	ubyte[1] sa = [5];
116 	assert(sa.asBytes == [5]);
117 
118 	void[] va = sa[];
119 	assert(va.asBytes == [5]);
120 }
121 
122 /// Reverse of .asBytes
123 ref inout(T) as(T)(inout(ubyte)[] bytes)
124 	if (!hasIndirections!T)
125 {
126 	assert(bytes.length == T.sizeof, "Data length mismatch for %s".format(T.stringof));
127 	return *cast(inout(T)*)bytes.ptr;
128 }
129 
130 /// ditto
131 inout(T) as(T)(inout(ubyte)[] bytes)
132 	if (is(T U == U[]) && !hasIndirections!U)
133 {
134 	return cast(inout(T))bytes;
135 }
136 
137 // deprecated alias fromBytes = as;
138 // https://issues.dlang.org/show_bug.cgi?id=23968
139 deprecated ref inout(T) fromBytes(T)(inout(ubyte)[] bytes) if (!hasIndirections!T) { return bytes.as!T; }
140 deprecated inout(T) fromBytes(T)(inout(ubyte)[] bytes) if (is(T U == U[]) && !hasIndirections!U) { return bytes.as!T; }
141 
142 debug(ae_unittest) unittest
143 {
144 	{       ubyte b = 5; assert(b.asBytes.as!ubyte == 5); }
145 	{ const ubyte b = 5; assert(b.asBytes.as!ubyte == 5); }
146 	struct S { ubyte b; }
147 	{       ubyte b = 5; assert(b.asBytes.as!S == S(5)); }
148 }
149 
150 debug(ae_unittest) unittest
151 {
152 	struct S { ubyte a, b; }
153 	ubyte[] arr = [1, 2];
154 	assert(arr.as!S == S(1, 2));
155 	assert(arr.as!(S[]) == [S(1, 2)]);
156 	assert(arr.as!(S[1]) == [S(1, 2)]);
157 }
158 
159 /// Return the value represented as a static array of bytes.
160 @property ref inout(ubyte)[T.sizeof] asStaticBytes(T)(ref inout(T) value)
161 	if (!hasIndirections!T)
162 {
163 	return *cast(inout(ubyte)[T.sizeof]*)&value;
164 }
165 
166 /// ditto
167 @property inout(ubyte)[T.sizeof] asStaticBytes(T)(inout(T) value)
168 	if (!hasIndirections!T)
169 {
170 	return *cast(inout(ubyte)[T.sizeof]*)&value;
171 }
172 
173 debug(ae_unittest) unittest
174 {
175 	ubyte[4] arr = 1.asStaticBytes;
176 
177 	int i;
178 	void* a = i.asStaticBytes.ptr;
179 	void* b = &i;
180 	assert(a is b);
181 }
182 
183 /// Returns an empty, but non-null slice of T.
184 auto emptySlice(T)() pure @trusted
185 {
186 	static if (false)  // https://github.com/ldc-developers/ldc/issues/831
187 	{
188 		T[0] arr;
189 		auto p = arr.ptr;
190 	}
191 	else
192 		auto p = cast(T*)1;
193 	return p[0..0];
194 }
195 
196 debug(ae_unittest) unittest
197 {
198 	int[] arr = emptySlice!int;
199 	assert(arr.ptr);
200 	immutable int[] iarr = emptySlice!int;
201 	assert(iarr.ptr);
202 }
203 
204 // ************************************************************************
205 
206 /// A more generic alternative to the "is" operator,
207 /// which doesn't have corner cases with static arrays / floats.
208 bool isIdentical(T)(auto ref T a, auto ref T b)
209 {
210 	static if (hasIndirections!T)
211 		return a is b;
212 	else
213 		return a.asStaticBytes == b.asStaticBytes;
214 }
215 
216 debug(ae_unittest) unittest
217 {
218 	float nan;
219 	assert(isIdentical(nan, nan));
220 	float[4] nans;
221 	assert(isIdentical(nans, nans));
222 }
223 
224 /// C `memcmp` wrapper.
225 int memcmp(in ubyte[] a, in ubyte[] b)
226 {
227 	assert(a.length == b.length);
228 	import core.stdc.string : memcmp;
229 	return memcmp(a.ptr, b.ptr, a.length);
230 }
231 
232 /// Like std.algorithm.copy, but without the auto-decode bullshit.
233 /// https://issues.dlang.org/show_bug.cgi?id=13650
234 void memmove(T)(T[] dst, in T[] src)
235 {
236 	assert(src.length == dst.length);
237 	import core.stdc.string : memmove;
238 	memmove(dst.ptr, src.ptr, dst.length * T.sizeof);
239 }
240 
241 /// Performs binary operation `op` on every element of `a` and `b`.
242 T[] vector(string op, T)(T[] a, T[] b)
243 {
244 	assert(a.length == b.length);
245 	T[] result = new T[a.length];
246 	foreach (i, ref r; result)
247 		r = mixin("a[i]" ~ op ~ "b[i]");
248 	return result;
249 }
250 
251 /// Performs in-place binary operation `op` on every element of `a` and `b`.
252 T[] vectorAssign(string op, T)(T[] a, T[] b)
253 {
254 	assert(a.length == b.length);
255 	foreach (i, ref r; a)
256 		mixin("r " ~ op ~ "= b[i];");
257 	return a;
258 }
259 
260 /// Return `s` expanded to at least `l` elements, filling them with `c`.
261 T[] padRight(T)(T[] s, size_t l, T c)
262 {
263 	auto ol = s.length;
264 	if (ol < l)
265 	{
266 		s.length = l;
267 		s[ol..$] = c;
268 	}
269 	return s;
270 }
271 
272 /// Return a new `T[]` of length `l`, filled with `c`.
273 T[] repeatOne(T)(T c, size_t l)
274 {
275 	T[] result = new T[l];
276 	result[] = c;
277 	return result;
278 }
279 
280 static import std.string;
281 /// Pull in overload set
282 private alias indexOf = std..string.indexOf;
283 private alias lastIndexOf = std..string.lastIndexOf;
284 
285 /// Complement to std.string.indexOf which works with arrays
286 /// of non-character types.
287 /// Unlike std.algorithm.countUntil, it does not auto-decode,
288 /// and returns an index usable for array indexing/slicing.
289 ptrdiff_t indexOf(T, D)(in T[] arr, in D val)
290 //	if (!isSomeChar!T)
291 	if (!isSomeChar!T && is(typeof(arr.countUntil(val))) && is(typeof(arr[0]==val)))
292 {
293 	//assert(arr[0]==val);
294 	return arr.countUntil(val);
295 }
296 
297 ptrdiff_t indexOf(T)(in T[] arr, in T[] val) /// ditto
298 	if (!isSomeChar!T && is(typeof(arr.countUntil(val))))
299 {
300 	return arr.countUntil(val);
301 } /// ditto
302 
303 debug(ae_unittest) unittest
304 {
305 	assert("abc".indexOf('b') == 1);
306 	assert("abc".indexOf("b") == 1);
307 	assert([1, 2, 3].indexOf( 2 ) == 1);
308 	assert([1, 2, 3].indexOf([2]) == 1);
309 }
310 
311 /// Same for `lastIndexOf`.
312 ptrdiff_t lastIndexOf(T, D)(in T[] arr, in D val)
313 	// if (!isSomeChar!T)
314 	if (!isSomeChar!T && is(typeof(arr[0]==val)))
315 {
316 	foreach_reverse (i, ref v; arr)
317 		if (v == val)
318 			return i;
319 	return -1;
320 }
321 
322 ptrdiff_t lastIndexOf(T)(in T[] arr, in T[] val) /// ditto
323 	if (!isSomeChar!T && is(typeof(arr[0..1]==val[0..1])))
324 {
325 	if (val.length > arr.length)
326 		return -1;
327 	foreach_reverse (i; 0 .. arr.length - val.length + 1)
328 		if (arr[i .. i + val.length] == val)
329 			return i;
330 	return -1;
331 } /// ditto
332 
333 debug(ae_unittest) unittest
334 {
335 	assert("abc".lastIndexOf('b') == 1);
336 	assert("abc".lastIndexOf("b") == 1);
337 	assert([1, 2, 3].lastIndexOf( 2 ) == 1);
338 	assert([1, 2, 3].lastIndexOf([2]) == 1);
339 
340 	assert([1, 2, 3, 2, 4].lastIndexOf( 2 ) == 3);
341 	assert([1, 2, 3, 2, 4].lastIndexOf([2]) == 3);
342 	assert([1, 2, 3, 2, 4].lastIndexOf([2, 3]) == 1);
343 	assert([1, 2, 3, 2, 4].lastIndexOf([2, 5]) == -1);
344 	assert([].lastIndexOf([2, 5]) == -1);
345 }
346 
347 deprecated("Use `indexOf`")
348 ptrdiff_t indexOfElement(T, D)(in T[] arr, auto ref const D val)
349 	if (is(typeof(arr[0]==val)))
350 {
351 	foreach (i, ref v; arr)
352 		if (v == val)
353 			return i;
354 	return -1;
355 }
356 
357 /// Whether array contains value, no BS.
358 bool contains(T, V)(in T[] arr, auto ref const V val)
359 	if (is(typeof(arr[0]==val)))
360 {
361 	return arr.indexOf(val) >= 0;
362 }
363 
364 /// Ditto, for substrings
365 bool contains(T, U)(T[] str, U[] what)
366 if (is(Unqual!T == Unqual!U))
367 {
368 	return str._indexOf(what) >= 0;
369 }
370 
371 debug(ae_unittest) unittest
372 {
373 	assert( "abc".contains('b'));
374 	assert(!"abc".contains('x'));
375 	assert( "abc".contains("b"));
376 	assert(!"abc".contains("x"));
377 }
378 
379 /// Like startsWith, but with an offset.
380 bool containsAt(T)(in T[] haystack, in T[] needle, size_t offset)
381 {
382 	return haystack.length >= offset + needle.length
383 		&& haystack[offset..offset+needle.length] == needle;
384 }
385 
386 debug(ae_unittest) unittest
387 {
388 	assert( "abracadabra".containsAt("ada", 5));
389 	assert(!"abracadabra".containsAt("ada", 6));
390 	assert(!"abracadabra".containsAt("ada", 99));
391 }
392 
393 /// Returns `true` if one of the elements of `arr` contains `val`.
394 bool isIn(T)(T val, in T[] arr)
395 {
396 	return arr.contains(val);
397 }
398 
399 /// Returns `true` if one of the elements of `arr` contains `val`.
400 bool isOneOf(T)(T val, T[] arr...)
401 {
402 	return arr.contains(val);
403 }
404 
405 /// Like AA.get - soft indexing, throws an
406 /// Exception (not an Error) on out-of-bounds,
407 /// even in release builds.
408 ref T get(T)(T[] arr, size_t index)
409 {
410 	enforce(index < arr.length, "Out-of-bounds array access");
411 	return arr[index];
412 }
413 
414 /// Like AA.get - soft indexing, returns
415 /// default value on out-of-bounds.
416 auto get(T)(T[] arr, size_t index, auto ref T defaultValue)
417 {
418 	if (index >= arr.length)
419 		return defaultValue;
420 	return arr[index];
421 }
422 
423 /// Expand the array if index is out-of-bounds.
424 ref T getExpand(T)(ref T[] arr, size_t index)
425 {
426 	if (index >= arr.length)
427 		arr.length = index + 1;
428 	return arr[index];
429 }
430 
431 /// ditto
432 ref T putExpand(T)(ref T[] arr, size_t index, auto ref T value)
433 {
434 	if (index >= arr.length)
435 		arr.length = index + 1;
436 	return arr[index] = value;
437 }
438 
439 /// Slices an array. Throws an Exception (not an Error)
440 /// on out-of-bounds, even in release builds.
441 T[] slice(T)(T[] arr, size_t p0, size_t p1)
442 {
443 	enforce(p0 < p1 && p1 < arr.length, "Out-of-bounds array slice");
444 	return arr[p0..p1];
445 }
446 
447 /// Given an array and a reference to an element,
448 /// return whether the array slices over the element reference.
449 bool slicesOver(T)(const(T)[] arr, ref const T element)
450 {
451 	auto start = arr.ptr;
452 	auto end = start + arr.length;
453 	auto p = &element;
454 	return start <= p && p < end;
455 }
456 
457 /// Given an array and a reference to an element inside it, returns its index.
458 /// The reverse operation of indexing an array.
459 size_t elementIndex(T)(const(T)[] arr, ref const T element) @trusted
460 {
461 	auto start = arr.ptr;
462 	auto end = start + arr.length;
463 	auto p = &element;
464 	assert(start <= p && p < end, "Element is not in array");
465 	return p - start;
466 }
467 
468 debug(ae_unittest) @safe unittest
469 {
470 	auto arr = [1, 2, 3];
471 	assert(arr.elementIndex(arr[1]) == 1);
472 }
473 
474 /// Given an array and its slice, returns the
475 /// start index of the slice inside the array.
476 /// The reverse operation of slicing an array.
477 size_t sliceIndex(T)(in T[] arr, in T[] slice) @trusted
478 {
479 	auto a = arr.ptr;
480 	auto b = a + arr.length;
481 	auto p = slice.ptr;
482 	assert(a <= p && p <= b, "Out-of-bounds array slice");
483 	return p - a;
484 }
485 
486 /// Like std.array.split, but returns null if val was empty.
487 auto splitEmpty(T, S)(T value, S separator)
488 {
489 	return value.length ? split(value, separator) : null;
490 }
491 
492 /// Like std.array.split, but always returns a non-empty array.
493 auto split1(T, S)(T value, S separator)
494 {
495 	auto result = split(value, separator);
496 	return result.length ? result : [value];
497 }
498 
499 /// Include delimiter in result chunks as suffix
500 H[] splitWithSuffix(H, S)(H haystack, S separator)
501 {
502 	H[] result;
503 	while (haystack.length)
504 	{
505 		auto pos = haystack._indexOf(separator);
506 		if (pos < 0)
507 			pos = haystack.length;
508 		else
509 		{
510 			static if (is(typeof(haystack[0] == separator)))
511 				pos += 1;
512 			else
513 			static if (is(typeof(haystack[0..1] == separator)))
514 				pos += separator.length;
515 			else
516 				static assert(false, "Don't know how to split " ~ H.stringof ~ " by " ~ S.stringof);
517 		}
518 		result ~= haystack[0..pos];
519 		haystack = haystack[pos..$];
520 	}
521 	return result;
522 }
523 
524 debug(ae_unittest) unittest
525 {
526 	assert("a\nb".splitWithSuffix('\n') == ["a\n", "b"]);
527 	assert([1, 0, 2].splitWithSuffix(0) == [[1, 0], [2]]);
528 
529 	assert("a\r\nb".splitWithSuffix("\r\n") == ["a\r\n", "b"]);
530 	assert([1, 0, 0, 2].splitWithSuffix([0, 0]) == [[1, 0, 0], [2]]);
531 }
532 
533 /// Include delimiter in result chunks as prefix
534 H[] splitWithPrefix(H, S)(H haystack, S separator)
535 {
536 	H[] result;
537 	while (haystack.length)
538 	{
539 		auto pos = haystack[1..$]._indexOf(separator);
540 		if (pos < 0)
541 			pos = haystack.length;
542 		else
543 			pos++;
544 		result ~= haystack[0..pos];
545 		haystack = haystack[pos..$];
546 	}
547 	return result;
548 }
549 
550 debug(ae_unittest) unittest
551 {
552 	assert("a\nb".splitWithPrefix('\n') == ["a", "\nb"]);
553 	assert([1, 0, 2].splitWithPrefix(0) == [[1], [0, 2]]);
554 
555 	assert("a\r\nb".splitWithPrefix("\r\n") == ["a", "\r\nb"]);
556 	assert([1, 0, 0, 2].splitWithPrefix([0, 0]) == [[1], [0, 0, 2]]);
557 }
558 
559 /// Include delimiters in result chunks as prefix/suffix
560 S[] splitWithPrefixAndSuffix(S)(S haystack, S prefix, S suffix)
561 {
562 	S[] result;
563 	auto separator = suffix ~ prefix;
564 	while (haystack.length)
565 	{
566 		auto pos = haystack._indexOf(separator);
567 		if (pos < 0)
568 			pos = haystack.length;
569 		else
570 			pos += suffix.length;
571 		result ~= haystack[0..pos];
572 		haystack = haystack[pos..$];
573 	}
574 	return result;
575 }
576 
577 ///
578 debug(ae_unittest) unittest
579 {
580 	auto s = q"EOF
581 Section 1:
582 10
583 11
584 12
585 Section 2:
586 21
587 22
588 23
589 Section 3:
590 31
591 32
592 33
593 EOF";
594 	auto parts = s.splitWithPrefixAndSuffix("Section ", "\n");
595 	assert(parts.length == 3 && parts.join == s);
596 	foreach (part; parts)
597 		assert(part.startsWith("Section ") && part.endsWith("\n"));
598 }
599 
600 /// Ensure that arr is non-null if empty.
601 T[] nonNull(T)(T[] arr)
602 {
603 	if (arr !is null)
604 		return arr;
605 	return emptySlice!(typeof(arr[0]));
606 }
607 
608 /// If arr is null, return null. Otherwise, return a non-null
609 /// transformation dg over arr.
610 template mapNull(alias dg)
611 {
612 	auto mapNull(T)(T arr)
613 	{
614 		if (arr is null)
615 			return null;
616 		return dg(arr).nonNull;
617 	}
618 }
619 
620 debug(ae_unittest) unittest
621 {
622 	assert(string.init.mapNull!(s => s          )  is null);
623 	assert(string.init.mapNull!(s => ""         )  is null);
624 	assert(""         .mapNull!(s => s          ) !is null);
625 	assert(""         .mapNull!(s => string.init) !is null);
626 }
627 
628 /// Select and return a random element from the array.
629 auto ref sample(T)(T[] arr)
630 {
631 	import std.random;
632 	return arr[uniform(0, $)];
633 }
634 
635 debug(ae_unittest) unittest
636 {
637 	assert([7, 7, 7].sample == 7);
638 	auto s = ["foo", "bar"].sample(); // Issue 13807
639 	const(int)[] a2 = [5]; sample(a2);
640 }
641 
642 /// Select and return a random element from the array,
643 /// and remove it from the array.
644 T pluck(T)(ref T[] arr)
645 {
646 	import std.random;
647 	auto pos = uniform(0, arr.length);
648 	auto result = arr[pos];
649 	arr = arr.remove(pos);
650 	return result;
651 }
652 
653 debug(ae_unittest) unittest
654 {
655 	auto arr = [1, 2, 3];
656 	auto res = [arr.pluck, arr.pluck, arr.pluck];
657 	res.sort();
658 	assert(res == [1, 2, 3]);
659 }
660 
661 import std.functional;
662 
663 /// Remove first matching element in an array, mutating the array.
664 /// Returns true if the array has been modified.
665 /// Cf. `K[V].remove(K)`
666 bool removeFirst(T, alias eq = binaryFun!"a == b", SwapStrategy swapStrategy = SwapStrategy.stable)(ref T[] arr, T elem)
667 {
668 	foreach (i, ref e; arr)
669 		if (eq(e, elem))
670 		{
671 			arr = arr.remove!swapStrategy(i);
672 			return true;
673 		}
674 	return false;
675 }
676 
677 debug(ae_unittest) unittest
678 {
679 	int[] arr = [1, 2, 3, 2];
680 	arr.removeFirst(2);
681 	assert(arr == [1, 3, 2]);
682 }
683 
684 /// Remove all matching elements in an array, mutating the array.
685 /// Returns the number of removed elements.
686 size_t removeAll(T, alias eq = binaryFun!"a == b", SwapStrategy swapStrategy = SwapStrategy.stable)(ref T[] arr, T elem)
687 {
688 	auto oldLength = arr.length;
689 	arr = arr.remove!((ref e) => eq(e, elem));
690 	return oldLength - arr.length;
691 }
692 
693 debug(ae_unittest) unittest
694 {
695 	int[] arr = [1, 2, 3, 2];
696 	arr.removeAll(2);
697 	assert(arr == [1, 3]);
698 }
699 
700 /// Sorts `arr` in-place using counting sort.
701 /// The difference between the lowest and highest
702 /// return value of `orderPred(arr[i])` shouldn't be too big.
703 void countSort(alias orderPred = "a", T)(T[] arr, ref T[] valuesBuf, ref size_t[] countsBuf)
704 {
705 	alias getIndex = unaryFun!orderPred;
706 	alias Index = typeof(getIndex(arr[0]));
707 	if (arr.length == 0)
708 		return;
709 	Index min = getIndex(arr[0]);
710 	Index max = min;
711 	foreach (ref el; arr[1..$])
712 	{
713 		Index i = getIndex(el);
714 		if (min > i)
715 			min = i;
716 		if (max < i)
717 			max = i;
718 	}
719 
720 	auto n = max - min + 1;
721 	if (valuesBuf.length < n)
722 		valuesBuf.length = n;
723 	auto values = valuesBuf[0 .. n];
724 	if (countsBuf.length < n)
725 		countsBuf.length = n;
726 	auto counts = countsBuf[0 .. n];
727 
728 	foreach (el; arr)
729 	{
730 		auto i = getIndex(el) - min;
731 		values[i] = el;
732 		counts[i]++;
733 	}
734 
735 	size_t p = 0;
736 	foreach (i; 0 .. n)
737 	{
738 		auto count = counts[i];
739 		arr[p .. p + count] = values[i];
740 		p += count;
741 	}
742 	assert(p == arr.length);
743 }
744 
745 void countSort(alias orderPred = "a", T)(T[] arr)
746 {
747 	static size_t[] countsBuf;
748 	static T[] valuesBuf;
749 	countSort!orderPred(arr, valuesBuf, countsBuf);
750 } /// ditto
751 
752 debug(ae_unittest) unittest
753 {
754 	auto arr = [3, 2, 5, 2, 1];
755 	arr.countSort();
756 	assert(arr == [1, 2, 2, 3, 5]);
757 }
758 
759 // ***************************************************************************
760 
761 /// Push `val` into `arr`, treating it like a stack.
762 void stackPush(T)(ref T[] arr, auto ref T val)
763 {
764 	arr ~= val;
765 }
766 
767 /// Push `val` into `arr`, treating it like a queue.
768 alias stackPush queuePush;
769 
770 /// Peek at the front of `arr`, treating it like a stack.
771 ref T stackPeek(T)(T[] arr) { return arr[$-1]; }
772 
773 /// Pop a value off the front of `arr`, treating it like a stack.
774 ref T stackPop(T)(ref T[] arr)
775 {
776 	auto ret = &arr[$-1];
777 	arr = arr[0..$-1];
778 	return *ret;
779 }
780 
781 /// Peek at the front of `arr`, treating it like a queue.
782 ref T queuePeek(T)(T[] arr) { return arr[0]; }
783 
784 /// Peek at the back of `arr`, treating it like a queue.
785 ref T queuePeekLast(T)(T[] arr) { return arr[$-1]; }
786 
787 /// Pop a value off the front of `arr`, treating it like a queue.
788 ref T queuePop(T)(ref T[] arr)
789 {
790 	auto ret = &arr[0];
791 	arr = arr[1..$];
792 	if (!arr.length) arr = null;
793 	return *ret;
794 }
795 
796 /// Remove the first element of `arr` and return it.
797 ref T shift(T)(ref T[] arr) { auto oldArr = arr; arr = arr[1..$]; return oldArr[0]; }
798 
799 /// Remove the `n` first elements of `arr` and return them.
800 T[] shift(T)(ref T[] arr, size_t n) { T[] result = arr[0..n]; arr = arr[n..$]; return result; }
801 T[N] shift(size_t N, T)(ref T[] arr) { T[N] result = cast(T[N])(arr[0..N]); arr = arr[N..$]; return result; } /// ditto
802 
803 /// Insert elements in the front of `arr`.
804 void unshift(T)(ref T[] arr, T value) { arr.insertInPlace(0, value); }
805 void unshift(T)(ref T[] arr, T[] value) { arr.insertInPlace(0, value); } /// ditto
806 
807 debug(ae_unittest) unittest
808 {
809 	int[] arr = [1, 2, 3];
810 	assert(arr.shift == 1);
811 	assert(arr == [2, 3]);
812 	assert(arr.shift(2) == [2, 3]);
813 	assert(arr == []);
814 
815 	arr = [3];
816 	arr.unshift([1, 2]);
817 	assert(arr == [1, 2, 3]);
818 	arr.unshift(0);
819 	assert(arr == [0, 1, 2, 3]);
820 
821 	assert(arr.shift!2 == [0, 1]);
822 	assert(arr == [2, 3]);
823 }
824 
825 /// If arr starts with prefix, slice it off and return true.
826 /// Otherwise leave arr unchaned and return false.
827 deprecated("Use std.algorithm.skipOver instead")
828 bool eat(T)(ref T[] arr, T[] prefix)
829 {
830 	if (arr.startsWith(prefix))
831 	{
832 		arr = arr[prefix.length..$];
833 		return true;
834 	}
835 	return false;
836 }
837 
838 // Overload disambiguator
839 private sizediff_t _indexOf(H, N)(H haystack, N needle)
840 {
841 	static import std.string;
842 
843 	static if (is(typeof(ae.utils.array.indexOf(haystack, needle))))
844 		alias indexOf = ae.utils.array.indexOf;
845 	else
846 	static if (is(typeof(std..string.indexOf(haystack, needle))))
847 		alias indexOf = std..string.indexOf;
848 	else
849 		static assert(false, "No suitable indexOf overload found");
850 	return indexOf(haystack, needle);
851 }
852 
853 /// Returns the slice of source up to the first occurrence of delim,
854 /// and fast-forwards source to the point after delim.
855 /// If delim is not found, the behavior depends on orUntilEnd:
856 /// - If orUntilEnd is false (default), it returns null
857 ///   and leaves source unchanged.
858 /// - If orUntilEnd is true, it returns source,
859 ///   and then sets source to null.
860 T[] skipUntil(T, D)(ref T[] source, D delim, bool orUntilEnd = false)
861 {
862 	enum bool isSlice = is(typeof(source[0..1]==delim));
863 	enum bool isElem  = is(typeof(source[0]   ==delim));
864 	static assert(isSlice || isElem, "Can't skip " ~ T.stringof ~ " until " ~ D.stringof);
865 	static assert(isSlice != isElem, "Ambiguous types for skipUntil: " ~ T.stringof ~ " and " ~ D.stringof);
866 	static if (isSlice)
867 		auto delimLength = delim.length;
868 	else
869 		enum delimLength = 1;
870 
871 	auto i = _indexOf(source, delim);
872 	if (i < 0)
873 	{
874 		if (orUntilEnd)
875 		{
876 			auto result = source;
877 			source = null;
878 			return result;
879 		}
880 		else
881 			return null;
882 	}
883 	auto result = source[0..i];
884 	source = source[i+delimLength..$];
885 	return result;
886 }
887 
888 /// Like `std.algorithm.skipOver`, but stops when
889 /// `pred(suffix)` or `pred(suffix[0])` returns false.
890 template skipWhile(alias pred)
891 {
892 	T[] skipWhile(T)(ref T[] source, bool orUntilEnd = false)
893 	{
894 		enum bool isSlice = is(typeof(pred(source[0..1])));
895 		enum bool isElem  = is(typeof(pred(source[0]   )));
896 		static assert(isSlice || isElem, "Can't skip " ~ T.stringof ~ " until " ~ pred.stringof);
897 		static assert(isSlice != isElem, "Ambiguous types for skipWhile: " ~ T.stringof ~ " and " ~ pred.stringof);
898 
899 		foreach (i; 0 .. source.length)
900 		{
901 			bool match;
902 			static if (isSlice)
903 				match = pred(source[i .. $]);
904 			else
905 				match = pred(source[i]);
906 			if (!match)
907 			{
908 				auto result = source[0..i];
909 				source = source[i .. $];
910 				return result;
911 			}
912 		}
913 
914 		if (orUntilEnd)
915 		{
916 			auto result = source;
917 			source = null;
918 			return result;
919 		}
920 		else
921 			return null;
922 	}
923 }
924 
925 debug(ae_unittest) unittest
926 {
927 	import std.ascii : isDigit;
928 
929 	string s = "01234abcde";
930 	auto p = s.skipWhile!isDigit;
931 	assert(p == "01234" && s == "abcde", p ~ "|" ~ s);
932 }
933 
934 deprecated("Use skipUntil instead")
935 enum OnEof { returnNull, returnRemainder, throwException }
936 
937 deprecated("Use skipUntil instead")
938 template eatUntil(OnEof onEof = OnEof.throwException)
939 {
940 	T[] eatUntil(T, D)(ref T[] source, D delim)
941 	{
942 		static if (onEof == OnEof.returnNull)
943 			return skipUntil(source, delim, false);
944 		else
945 		static if (onEof == OnEof.returnRemainder)
946 			return skipUntil(source, delim, true);
947 		else
948 			return skipUntil(source, delim, false).enforce("Delimiter not found in source");
949 	}
950 }
951 
952 debug(ae_unittest) deprecated unittest
953 {
954 	string s;
955 
956 	s = "Mary had a little lamb";
957 	assert(s.eatUntil(" ") == "Mary");
958 	assert(s.eatUntil(" ") == "had");
959 	assert(s.eatUntil(' ') == "a");
960 
961 	assertThrown!Exception(s.eatUntil("#"));
962 	assert(s.eatUntil!(OnEof.returnNull)("#") is null);
963 	assert(s.eatUntil!(OnEof.returnRemainder)("#") == "little lamb");
964 
965 	ubyte[] bytes = [1, 2, 0, 3, 4, 0, 0];
966 	assert(bytes.eatUntil(0) == [1, 2]);
967 	assert(bytes.eatUntil([ubyte(0), ubyte(0)]) == [3, 4]);
968 }
969 
970 // ***************************************************************************
971 
972 /// Equivalents of `array(xxx(...))`.
973 template amap(alias pred)
974 {
975 	auto amap(R)(R range)
976 	if (isInputRange!R && hasLength!R)
977 	{
978 		import core.memory : GC;
979 		import core.lifetime : emplace;
980 		alias T = typeof(unaryFun!pred(range.front));
981 		auto result = uninitializedArray!(Unqual!T[])(range.length);
982 		foreach (i; 0 .. range.length)
983 		{
984 			auto value = cast()unaryFun!pred(range.front);
985 			moveEmplace(value, result[i]);
986 			range.popFront();
987 		}
988 		assert(range.empty);
989 		return cast(T[])result;
990 	}
991 
992 	/// Like `amap` but with a static array.
993 	auto amap(T, size_t n)(T[n] arr)
994 	{
995 		alias R = typeof(unaryFun!pred(arr[0]));
996 		R[n] result;
997 		foreach (i, ref r; result)
998 			r = unaryFun!pred(arr[i]);
999 		return result;
1000 	}
1001 }
1002 template afilter(alias pred) { auto afilter(T)(T[] arr) { return array(filter!pred(arr)); } } /// ditto
1003 auto auniq(T)(T[] arr) { return array(uniq(arr)); } /// ditto
1004 auto asort(alias pred, T)(T[] arr) { sort!pred(arr); return arr; } /// ditto
1005 
1006 debug(ae_unittest) unittest
1007 {
1008 	assert([1, 2, 3].amap!`a*2`() == [2, 4, 6]);
1009 	assert([1, 2, 3].amap!(n => n*n)() == [1, 4, 9]);
1010 	assert([1, 2, 3].staticArray.amap!(n => n*n)() == [1, 4, 9].staticArray);
1011 }
1012 
1013 debug(ae_unittest) unittest
1014 {
1015 	struct NC
1016 	{
1017 		int i;
1018 		@disable this();
1019 		this(int i) { this.i = i; }
1020 		@disable this(this);
1021 	}
1022 
1023 	import std.range : iota;
1024 	assert(3.iota.amap!(i => NC(i))[1].i == 1);
1025 }
1026 
1027 debug(ae_unittest) unittest
1028 {
1029 	import std.range : iota;
1030 	immutable(int)[] arr;
1031 	arr = 3.iota.amap!(i => cast(immutable)i);
1032 	assert(arr[1] == 1);
1033 }
1034 
1035 // ***************************************************************************
1036 
1037 /// Array with normalized comparison and hashing.
1038 /// Params:
1039 ///   T = array element type to wrap.
1040 ///   normalize = function which should return a range of normalized elements.
1041 struct NormalizedArray(T, alias normalize)
1042 {
1043 	T[] arr; /// Underlying array.
1044 
1045 	this(T[] arr) { this.arr = arr; } ///
1046 
1047 	int opCmp    (in T[]                 other) const { return std.algorithm.cmp(normalize(arr), normalize(other    ))   ; } ///
1048 	int opCmp    (    const typeof(this) other) const { return std.algorithm.cmp(normalize(arr), normalize(other.arr))   ; } ///
1049 	int opCmp    (ref const typeof(this) other) const { return std.algorithm.cmp(normalize(arr), normalize(other.arr))   ; } ///
1050 	bool opEquals(in T[]                 other) const { return std.algorithm.cmp(normalize(arr), normalize(other    ))==0; } ///
1051 	bool opEquals(    const typeof(this) other) const { return std.algorithm.cmp(normalize(arr), normalize(other.arr))==0; } ///
1052 	bool opEquals(ref const typeof(this) other) const { return std.algorithm.cmp(normalize(arr), normalize(other.arr))==0; } ///
1053 
1054 	private hash_t toHashReal() const
1055 	{
1056 		import std.digest.crc;
1057 		CRC32 crc;
1058 		foreach (c; normalize(arr))
1059 			crc.put(cast(ubyte[])((&c)[0..1]));
1060 		static union Result { ubyte[4] crcResult; hash_t hash; }
1061 		return Result(crc.finish()).hash;
1062 	}
1063 
1064 	hash_t toHash() const nothrow @trusted
1065 	{
1066 		return (cast(hash_t delegate() nothrow @safe)&toHashReal)();
1067 	} ///
1068 }
1069 
1070 // ***************************************************************************
1071 
1072 /// Equivalent of PHP's `list` language construct:
1073 /// http://php.net/manual/en/function.list.php
1074 /// Works with arrays and tuples.
1075 /// Specify `null` as an argument to ignore that index
1076 /// (equivalent of `list(x, , y)` in PHP).
1077 auto list(Args...)(auto ref Args args)
1078 {
1079 	struct List
1080 	{
1081 		auto dummy() { return args[0]; } // https://issues.dlang.org/show_bug.cgi?id=11886
1082 		void opAssign(T)(auto ref T t)
1083 		{
1084 			assert(t.length == args.length,
1085 				"Assigning %d elements to list with %d elements"
1086 				.format(t.length, args.length));
1087 			foreach (i; rangeTuple!(Args.length))
1088 				static if (!is(Args[i] == typeof(null)))
1089 					args[i] = t[i];
1090 		}
1091 	}
1092 	return List();
1093 }
1094 
1095 ///
1096 debug(ae_unittest) unittest
1097 {
1098 	string name, value;
1099 	list(name, null, value) = "NAME=VALUE".findSplit("=");
1100 	assert(name == "NAME" && value == "VALUE");
1101 }
1102 
1103 version(LittleEndian)
1104 debug(ae_unittest) unittest
1105 {
1106 	uint onlyValue;
1107 	ubyte[] data = [ubyte(42), 0, 0, 0];
1108 	list(onlyValue) = cast(uint[])data;
1109 	assert(onlyValue == 42);
1110 }