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 <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.array;
15 
16 import std.exception;
17 import std.traits;
18 
19 public import ae.utils.aa;
20 public import ae.utils.appender;
21 
22 /// Slice a variable.
23 T[] toArray(T)(ref T v)
24 {
25 	return (&v)[0..1];
26 }
27 
28 /// Return the value represented as an array of bytes.
29 @property inout(ubyte)[] bytes(T)(ref inout(T) value)
30 	if (!(is(T == class) || isDynamicArray!T))
31 {
32 	return value.toArray().bytes;
33 }
34 
35 /// ditto
36 @property inout(ubyte)[] bytes(T)(inout(T) value)
37 	if ( (is(T == class) || isDynamicArray!T))
38 {
39 	static if (is(T U : U[]))
40 		return cast(inout(ubyte)[])value;
41 	else
42 		return (cast(inout(ubyte)*)value)[0..__traits(classInstanceSize, T)];
43 }
44 
45 unittest
46 {
47 	ubyte b = 5;
48 	assert(b.bytes == [5]);
49 
50 	struct S { ubyte b = 5; }
51 	S s;
52 	assert(s.bytes == [5]);
53 
54 	ubyte[1] sa = [5];
55 	assert(sa.bytes == [5]);
56 }
57 
58 int memcmp(in ubyte[] a, in ubyte[] b)
59 {
60 	import core.stdc.string;
61 	assert(a.length == b.length);
62 	return memcmp(a.ptr, b.ptr, a.length);
63 }
64 
65 /// Like std.algorithm.copy, but without the auto-decode bullshit.
66 /// https://issues.dlang.org/show_bug.cgi?id=13650
67 void memmove(T)(T[] dst, in T[] src)
68 {
69 	import core.stdc.string;
70 	assert(src.length == dst.length);
71 	memmove(dst.ptr, src.ptr, dst.length * T.sizeof);
72 }
73 
74 T[] vector(string op, T)(T[] a, T[] b)
75 {
76 	assert(a.length == b.length);
77 	T[] result = new T[a.length];
78 	foreach (i, ref r; result)
79 		r = mixin("a[i]" ~ op ~ "b[i]");
80 	return result;
81 }
82 
83 T[] vectorAssign(string op, T)(T[] a, T[] b)
84 {
85 	assert(a.length == b.length);
86 	foreach (i, ref r; a)
87 		mixin("r " ~ op ~ "= b[i];");
88 	return a;
89 }
90 
91 T[] padRight(T)(T[] s, size_t l, T c)
92 {
93 	auto ol = s.length;
94 	if (ol < l)
95 	{
96 		s.length = l;
97 		s[ol..$] = c;
98 	}
99 	return s;
100 }
101 
102 T[] repeatOne(T)(T c, size_t l)
103 {
104 	T[] result = new T[l];
105 	result[] = c;
106 	return result;
107 }
108 
109 bool contains(T, V)(T[] arr, V val)
110 	if (is(typeof(arr[0]==val)))
111 {
112 	foreach (v; arr)
113 		if (v == val)
114 			return true;
115 	return false;
116 }
117 
118 bool isIn(T)(T val, in T[] arr)
119 {
120 	return arr.contains(val);
121 }
122 
123 bool isOneOf(T)(T val, T[] arr...)
124 {
125 	return arr.contains(val);
126 }
127 
128 /// Like AA.get - soft indexing, throws an
129 /// Exception (not an Error) on out-of-bounds,
130 /// even in release builds.
131 ref T get(T)(T[] arr, size_t index)
132 {
133 	enforce(index < arr.length, "Out-of-bounds array access");
134 	return arr[index];
135 }
136 
137 /// Like AA.get - soft indexing, returns
138 /// default value on out-of-bounds.
139 auto get(T)(T[] arr, size_t index, auto ref T defaultValue)
140 {
141 	if (index >= arr)
142 		return defaultValue;
143 	return arr[index];
144 }
145 
146 /// Slices an array. Throws an Exception (not an Error)
147 /// on out-of-bounds, even in release builds.
148 T[] slice(T)(T[] arr, size_t p0, size_t p1)
149 {
150 	enforce(p0 < p1 && p1 < arr.length, "Out-of-bounds array slice");
151 	return arr[p0..p1];
152 }
153 
154 import std.random;
155 
156 /// Select and return a random element from the array.
157 auto ref sample(T)(T[] arr)
158 {
159 	return arr[uniform(0, $)];
160 }
161 
162 unittest
163 {
164 	assert([7, 7, 7].sample == 7);
165 	auto s = ["foo", "bar"].sample(); // Issue 13807
166 	const(int)[] a2 = [5]; sample(a2);
167 }
168 
169 /// Select and return a random element from the array,
170 /// and remove it from the array.
171 T pluck(T)(ref T[] arr)
172 {
173 	auto pos = uniform(0, arr.length);
174 	auto result = arr[pos];
175 	arr = arr.remove(pos);
176 	return result;
177 }
178 
179 unittest
180 {
181 	auto arr = [1, 2, 3];
182 	auto res = [arr.pluck, arr.pluck, arr.pluck];
183 	res.sort();
184 	assert(res == [1, 2, 3]);
185 }
186 
187 import std.functional;
188 
189 T[] countSort(alias value = "a", T)(T[] arr)
190 {
191 	alias unaryFun!value getValue;
192 	alias typeof(getValue(arr[0])) V;
193 	if (arr.length == 0) return arr;
194 	V min = getValue(arr[0]), max = getValue(arr[0]);
195 	foreach (el; arr[1..$])
196 	{
197 		auto v = getValue(el);
198 		if (min > v)
199 			min = v;
200 		if (max < v)
201 			max = v;
202 	}
203 	auto n = max-min+1;
204 	auto counts = new size_t[n];
205 	foreach (el; arr)
206 		counts[getValue(el)-min]++;
207 	auto indices = new size_t[n];
208 	foreach (i; 1..n)
209 		indices[i] = indices[i-1] + counts[i-1];
210 	T[] result = new T[arr.length];
211 	foreach (el; arr)
212 		result[indices[getValue(el)-min]++] = el;
213 	return result;
214 }
215 
216 // ***************************************************************************
217 
218 void stackPush(T)(ref T[] arr, T val)
219 {
220 	arr ~= val;
221 }
222 alias stackPush queuePush;
223 
224 T stackPeek(T)(T[] arr) { return arr[$-1]; }
225 
226 T stackPop(T)(ref T[] arr)
227 {
228 	auto ret = arr[$-1];
229 	arr = arr[0..$-1];
230 	return ret;
231 }
232 
233 T queuePeek(T)(T[] arr) { return arr[0]; }
234 
235 T queuePeekLast(T)(T[] arr) { return arr[$-1]; }
236 
237 T queuePop(T)(ref T[] arr)
238 {
239 	auto ret = arr[0];
240 	arr = arr[1..$];
241 	if (!arr.length) arr = null;
242 	return ret;
243 }
244 
245 T shift(T)(ref T[] arr) { T result = arr[0]; arr = arr[1..$]; return result; }
246 void unshift(T)(ref T[] arr, T value) { arr.insertInPlace(0, value); }
247 
248 /// If arr starts with prefix, slice it off and return true.
249 /// Otherwise leave arr unchaned and return false.
250 bool eat(T)(ref T[] arr, T[] prefix)
251 {
252 	if (arr.startsWith(prefix))
253 	{
254 		arr = arr[prefix.length..$];
255 		return true;
256 	}
257 	return false;
258 }
259 
260 /// Return arr until the first instance of separator (excluding it),
261 /// and set arr to the remaining part (again, excluding the separator).
262 /// Throws if the separator is not found.
263 T[] eatUntil(T)(ref T[] arr, T[] separator)
264 {
265 	import std.exception;
266 	import std.string;
267 
268 	auto p = arr.countUntil(separator);
269 	enforce(p >= 0, "%s not found in %s".format(separator, arr));
270 	auto result = arr[0..p];
271 	arr = arr[p+separator.length..$];
272 	return result;
273 }
274 
275 // ***************************************************************************
276 
277 import std.algorithm;
278 import std.array;
279 
280 // Equivalents of array(xxx(...)), but less parens and UFCS-able.
281 auto amap(alias pred, T)(T[] arr) { return array(map!pred(arr)); }
282 auto afilter(alias pred, T)(T[] arr) { return array(filter!pred(arr)); }
283 auto auniq(T)(T[] arr) { return array(uniq(arr)); }
284 auto asort(alias pred, T)(T[] arr) { sort!pred(arr); return arr; }
285 
286 unittest
287 {
288 	assert([1, 2, 3].amap!`a*2`() == [2, 4, 6]);
289 	assert([1, 2, 3].amap!(n => n*n)() == [1, 4, 9]);
290 }