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 }