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 }