1 /// Algorithms for mutating ranges and other various range functions. 2 module tern.algorithm.mutation; 3 4 public import tern.algorithm.lazy_filter; 5 public import tern.algorithm.lazy_map; 6 public import tern.algorithm.lazy_substitute; 7 import tern.traits; 8 import tern.typecons; 9 import tern.object; 10 import tern.functional; 11 12 /** 13 * Last In First Out 14 * 15 * ```d 16 * [] -> push(1) push(2) -> [1, 2] // Order doesn't change between LIFO vs FILO 17 * [1, 2] -> pop() -> [1] // Value pushed last gets popped 18 * ``` 19 */ 20 public enum LIFO; 21 /** 22 * First In Last Out 23 * 24 * ```d 25 * [] -> push(1) push(2) -> [1, 2] // Order doesn't change between LIFO vs FILO 26 * [1, 2] -> pop() -> [2] // Value pushed first gets popped 27 * ``` 28 */ 29 public enum FILO; 30 31 public: 32 /** 33 * Maps `range` to `F`, where every value will become the output of `F`. 34 * 35 * Params: 36 * F = The function to map `range` to. 37 * range = The range to be mapped to `F`. 38 * 39 * Returns: 40 * A lazy map of `range` using `F`. 41 */ 42 LazyMap!(F, T) map(alias F, T)(T range) 43 if (isForward!T && isCallable!F) 44 { 45 return LazyMap!(F, T)(range); 46 } 47 48 /** 49 * Filters `range` by predicate `F`, where values will removed if `F` is false. 50 * 51 * Params: 52 * F = The function predicate to filter `range` by. 53 * range = The range to be filtered by `F`. 54 * 55 * Returns: 56 * A lazy filter of `range` using `F`. 57 */ 58 LazyFilter!(F, T) filter(alias F, T)(T range) 59 if (isForward!T && isCallable!F) 60 { 61 return LazyFilter!(F, T)(range); 62 } 63 64 /** 65 * Substitutes all instances of `from` in `range` with `to`. 66 * 67 * Params: 68 * range = The range to be substituted in. 69 * from = The value to be replaced. 70 * to = The value to replace `from` with. 71 * 72 * Returns: 73 * A lazy substitute of `range` using `from` and `to`. 74 */ 75 LazySubstitute!(A, B, C) substitute(A, B, C)(A range, B from, C to) 76 if (isForward!T && isIndexable!T) 77 { 78 return LazySubstitute!(A, B, C)(range, from, to); 79 } 80 81 /** 82 * Replaces all instances of `from` in `range` 83 * 84 * Params: 85 * range = The range to replace `from` in. 86 * to = The value to replace `from` with. 87 * from = The value to be replaced in `range`. 88 * 89 * Returns: 90 * The new range after all replaces. 91 */ 92 A replace(A, B, C)(A range, B from, C to) 93 if (isIndexable!A && isElement!(A, B) && isElement!(A, C)) 94 { 95 Range!A ret = range; 96 ret.plane!((ref i) { 97 ret[i] = from; 98 })(from); 99 return ret.value; 100 } 101 102 /// ditto 103 A replace(A, B, C)(A range, B from, C to) 104 if (isIndexable!A && isIndexable!B && isIndexable!C && !isElement!(A, B) && !isElement!(A, C)) 105 { 106 Range!A ret = range; 107 ret.plane!((ref i) { 108 if (to.loadLength <= from.loadLength) 109 ret[i..(i + to.loadLength)] = to; 110 else 111 { 112 ret[i..(i + from.loadLength)] = to[0..from.loadLength]; 113 ret.insert(i + from.loadLength, to[from.loadLength..$]); 114 } 115 116 if (to.loadLength < from.loadLength) 117 ret.alienate(i + to.loadLength, from.loadLength - to.loadLength); 118 119 i += to.loadLength - 1; 120 })(from); 121 return ret.value; 122 } 123 124 /** 125 * Replaces all values in `from` with `to` in `range`. 126 * 127 * Params: 128 * range = The range to replace `from` in. 129 * to = The value to replace all values in `from` with. 130 * from = The values to be replaced in `range`. 131 * 132 * Returns: 133 * The new range after all replaces. 134 */ 135 A replaceMany(A, B, C...)(A range, B to, C from) 136 if (isIndexable!A) 137 { 138 foreach (u; from) 139 range.replace(u, to); 140 return range; 141 } 142 143 /** 144 * Removes `val` from `range`. 145 * 146 * Params: 147 * range = The range to remove `vals` from. 148 * vals = The value to be removed from `range`. 149 * 150 * Returns: 151 * The new range after `val` has been removed. 152 */ 153 A remove(A, B)(A range, B val) 154 if (isIndexable!A) 155 { 156 Range!A ret = range; 157 ret.plane!((ref i) { 158 ret.alienate(i, val.loadLength); 159 i -= val.loadLength; 160 })(val); 161 return ret.value; 162 } 163 164 /** 165 * Removes many `vals` from `range`. 166 * 167 * Params: 168 * range = The range to remove `vals` from. 169 * vals = The values to be removed from `range`. 170 * 171 * Returns: 172 * The new range after all `vals` have been removed. 173 */ 174 A removeMany(A, B...)(A range, B vals) 175 if (B.length > 1 && isIndexable!A) 176 { 177 foreach (u; vals) 178 range = range.remove(u); 179 return range; 180 } 181 182 /** 183 * Joins an array of `ranges` by an element `by`. 184 * 185 * Params: 186 * ranges = The ranges to be joined `by`. 187 * by = The element to join each range in `ranges` by. 188 * 189 * Returns: 190 * A joined range with `by` as the delimiter. 191 */ 192 A join(A, B)(A[] ranges, B by) 193 if (isIndexable!A) 194 { 195 A ret; 196 foreach (range; ranges) 197 { 198 static if (isIndexable!B) 199 ret ~= range~cast(A)by; 200 else 201 ret ~= range~cast(ElementType!A)by; 202 } 203 return ret; 204 } 205 206 /** 207 * Splits `range` by a given element to split `by`. 208 * 209 * Params: 210 * range = The range to be split. 211 * by = The element to have `range` split by. 212 * 213 * Returns: 214 * An array of `A` containing `range` after split `by`. 215 */ 216 A[] split(A, B)(A range, B by) 217 if (isIndexable!A) 218 { 219 A[] ret; 220 range.plane!((ref i) { 221 if (i != 0) 222 ret ~= range[0..i]; 223 224 range = range[(i + by.loadLength)..$]; 225 i = 0; 226 })(by); 227 ret ~= range[0..$]; 228 return ret; 229 } 230 231 /** 232 * Splits `range` by a given predicate `F`. 233 * 234 * Params: 235 * F = The function predicate to split `range` by. 236 * range = The range to be split by the predicate `F`. 237 * 238 * Returns: 239 * An array of `A` containing `range` after split by `F`. 240 */ 241 A[] split(alias F, A)(A range) 242 if (isIndexable!A && isCallable!F) 243 { 244 A[] ret; 245 range.plane!((ref i) { 246 if (i != 0) 247 ret ~= range[0..i]; 248 249 range = range[(i + 1)..$]; 250 i = 0; 251 }, F); 252 ret ~= range[0..$]; 253 return ret; 254 } 255 256 /** 257 * Swaps elements `i0` and `i1` in `range`. 258 * 259 * Params: 260 * range = The range. 261 * i0 = First i to swap. 262 * i1 = Second i to swap. 263 */ 264 pragma(inline) 265 void swap(T)(ref T range, size_t i0, size_t i1) 266 if (isIndexable!T) 267 { 268 auto d = range[i0]; 269 range[i0] = range[i1]; 270 range[i1] = d; 271 } 272 273 /** 274 * Pops a value off of `range`. 275 * 276 * Params: 277 * O = Stack order to pop the value using. 278 * range = The range being popped from. 279 * 280 * Returns: 281 * The value that was popped off the stack. 282 * 283 * Remarks:returns: 284 * Defaults to `LIFO`. 285 */ 286 pragma(inline) 287 ElementType!T pop(O = LIFO, T)(ref T range) 288 if ((is(O == LIFO) || is(O == FILO)) && isIndexable!T) 289 { 290 static if (is(O == LIFO)) 291 { 292 scope (exit) range = range[0..$-1]; 293 return range[$-1]; 294 } 295 else 296 { 297 scope (exit) range = range[1..$]; 298 return range[0]; 299 } 300 } 301 302 /** 303 * Duplicates the top value of `range` without modifying the stack. 304 * 305 * Params: 306 * O = Stack order to duplicate the value using. 307 * range = The range. 308 * 309 * Returns: 310 * The duplicated value from the top of the stack. 311 * 312 * Remarks: 313 * Defaults to `LIFO`. 314 */ 315 pragma(inline) 316 ElementType!T peek(O, T)(ref T range) 317 if ((is(O == LIFO) || is(O == FILO)) && isIndexable!T) 318 { 319 static if (is(O == LIFO)) 320 return range[$-1]; 321 else 322 return range[0]; 323 } 324 325 /** 326 * Swaps the top two values on the stack. 327 * 328 * Params: 329 * O = Stack order to perform the swap on. 330 * range = The range. 331 * 332 * Remarks: 333 * Defaults to `LIFO`. 334 */ 335 pragma(inline) 336 void swap(O = LIFO, T)(ref T range) 337 if ((is(O == LIFO) || is(O == FILO)) && isIndexable!T) 338 { 339 static if (is(O == LIFO)) 340 range.swap(range.loadLength - 1, range.loadLength - 2); 341 else 342 range.swap(0, 1); 343 } 344 345 /** 346 * Pushes a value onto `range`. 347 * 348 * Params: 349 * range = The range being pushed to. 350 * val = The value to push onto the range. 351 */ 352 pragma(inline) 353 nothrow void push(A, B)(ref A range, B val) 354 if ((is(O == LIFO) || is(O == FILO)) && isIndexable!A && isElement!(A, B)) 355 { 356 range ~= val; 357 } 358 359 /** 360 * Reverses the contents of `range`. 361 * 362 * Params: 363 * range = The range to be reversed. 364 */ 365 pragma(inline) 366 void reverse(T)(ref T range) 367 if (isIndexable!T) 368 { 369 for (size_t i = 0; i < range.loadLength / 2; i++) 370 range.swap(i, range.loadLength - i - 1); 371 } 372 373 /** 374 * Fills all elements in `range` with `elem`. 375 * 376 * Params: 377 * range = The range to be filled. 378 * elem = The range to fill all elements with. 379 */ 380 pragma(inline) 381 void fill(A, B)(ref A range, B elem) 382 if (isIndexable!A && isElement!(A, B)) 383 { 384 Range!A ret = range; 385 foreach (i; 0..ret.length) 386 ret[i] = elem; 387 range = ret.value; 388 } 389 390 /** 391 * Clears all elements in `range` without modifying length. 392 * 393 * Elements will be set to `ElementType!T.init` after clearing. 394 * 395 * Params: 396 * range = The range to be cleared. 397 */ 398 pragma(inline) 399 void clear(T)(ref T range) 400 if (isIndexable!T) 401 { 402 Range!A ret = range; 403 foreach (i; 0..ret.length) 404 ret[i] = ElementType!T.init; 405 range = ret.value; 406 } 407 408 /** 409 * Alienates `i..length` in `range`, removing it. 410 * 411 * Params: 412 * range = The range to alienate at. 413 * index = Start index of the elements to be alienated. 414 * length = The length of the section to be alienated. 415 */ 416 pragma(inline) 417 void alienate(T)(ref T range, size_t index, size_t length) 418 if (isSliceable!T) 419 { 420 Range!T ret = range; 421 ret = ret[0..index]~ret[(index + length)..$]; 422 range = ret.value; 423 } 424 425 /** 426 * Inserts `elem` in `range` at `index`. 427 * 428 * Params: 429 * range = The range to insert into. 430 * index = The index where `elem` will be inserted. 431 * elem = The element to insert. 432 */ 433 pragma(inline) 434 void insert(A, B)(ref A range, size_t index, B elem) 435 if (isSliceable!A && (isElement!(B, A) || isIndexable!B)) 436 { 437 Range!A ret = range; 438 static if (isIndexable!B) 439 { 440 if (index >= ret.length) 441 ret = ret[0..index]~cast(A)elem; 442 else 443 ret = ret[0..index]~cast(A)elem~ret[index..$]; 444 } 445 else 446 { 447 if (index >= ret.length) 448 ret = ret[0..index]~cast(ElementType!A)elem; 449 else 450 ret = ret[0..index]~cast(ElementType!A)elem~ret[(index + 1)..$]; 451 } 452 range = ret.value; 453 }