root/trunk/rangeextra/rangeextra.d

Revision 578, 21.1 kB (checked in by dsimcha, 1 year ago)

More TNew bugfixes.

Line 
1 /**Some extra ranges that were not included in std.range.
2  *
3  * Author:  David Simcha*/
4  /*
5  * You may use this software under your choice of either of the following
6  * licenses.  YOU NEED ONLY OBEY THE TERMS OF EXACTLY ONE OF THE TWO LICENSES.
7  * IF YOU CHOOSE TO USE THE PHOBOS LICENSE, YOU DO NOT NEED TO OBEY THE TERMS OF
8  * THE BSD LICENSE.  IF YOU CHOOSE TO USE THE BSD LICENSE, YOU DO NOT NEED
9  * TO OBEY THE TERMS OF THE PHOBOS LICENSE.  IF YOU ARE A LAWYER LOOKING FOR
10  * LOOPHOLES AND RIDICULOUSLY NON-EXISTENT AMBIGUITIES IN THE PREVIOUS STATEMENT,
11  * GET A LIFE.
12  *
13  * ---------------------Phobos License: ---------------------------------------
14  *
15  *  Copyright (C) 2009 by David Simcha.
16  *
17  *  This software is provided 'as-is', without any express or implied
18  *  warranty. In no event will the authors be held liable for any damages
19  *  arising from the use of this software.
20  *
21  *  Permission is granted to anyone to use this software for any purpose,
22  *  including commercial applications, and to alter it and redistribute it
23  *  freely, in both source and binary form, subject to the following
24  *  restrictions:
25  *
26  *  o  The origin of this software must not be misrepresented; you must not
27  *     claim that you wrote the original software. If you use this software
28  *     in a product, an acknowledgment in the product documentation would be
29  *     appreciated but is not required.
30  *  o  Altered source versions must be plainly marked as such, and must not
31  *     be misrepresented as being the original software.
32  *  o  This notice may not be removed or altered from any source
33  *     distribution.
34  *
35  * --------------------BSD License:  -----------------------------------------
36  *
37  * Copyright (c) 2009, David Simcha
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions are met:
42  *
43  *     * Redistributions of source code must retain the above copyright
44  *       notice, this list of conditions and the following disclaimer.
45  *
46  *     * Redistributions in binary form must reproduce the above copyright
47  *       notice, this list of conditions and the following disclaimer in the
48  *       documentation and/or other materials provided with the distribution.
49  *
50  *     * Neither the name of the authors nor the
51  *       names of its contributors may be used to endorse or promote products
52  *       derived from this software without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
55  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
56  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
57  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
58  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
59  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
60  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64  */
65 module rangeextra;
66
67 import std.range, std.traits, std.typetuple, std.typecons, core.memory,
68        std.array, std.functional;
69
70 version(unittest) {
71     import std.algorithm, std.conv, std.stdio;void main(){}
72 }
73
74 template hasLength(T) {
75     enum bool hasLength = is(typeof(T.init.length)) ||
76                           is(typeof(T.init.length()));
77 }
78
79 /**Tests whether T can be iterated over using foreach.  This is a superset
80  * of isInputRange, as it also accepts things that use opApply, builtin
81  * arrays, builtin associative arrays, etc.  Useful when all you need is
82  * lowest common denominator iteration functionality and you don't care about
83  * more advanced range features.*/
84 template isIterable(T)
85 {
86     static if (is(typeof({foreach(elem; T.init) {}}))) {
87         enum bool isIterable = true;
88     } else {
89         enum bool isIterable = false;
90     }
91 }
92
93 unittest {
94     struct Foo {  // For testing opApply.
95         // For testing.
96
97         int opApply(int delegate(ref uint) dg) { assert(0); }
98     }
99
100     static assert(isIterable!(uint[]));
101     static assert(!isIterable!(uint));
102     static assert(isIterable!(Foo));
103     static assert(isIterable!(uint[string]));
104     static assert(isIterable!(Chain!(uint[], uint[])));
105 }
106
107 /**Determine the iterable type of any iterable object, regardless of whether
108  * it uses ranges, opApply, etc.  This is typeof(elem) if one does
109  * foreach(elem; T.init) {}.*/
110 template IterType(T) {
111     alias ReturnType!(
112         {
113             foreach(elem; T.init) {
114                 return elem;
115             }
116         }) IterType;
117 }
118
119 unittest {
120     struct Foo {  // For testing opApply.
121         // For testing.
122
123         int opApply(int delegate(ref uint) dg) { assert(0); }
124     }
125
126     static assert(is(IterType!(uint[]) == uint));
127     static assert(is(IterType!(Foo) == uint));
128     static assert(is(IterType!(uint[string]) == uint));
129     static assert(is(IterType!(Chain!(uint[], uint[])) == uint));
130 }
131
132 ///
133 struct Reindex(alias lambda, T)
134 if(isRandomAccessRange!(T)) {
135 private:
136     alias unaryFun!(lambda) reindexTempl;
137
138     static if(is(ParameterTypeTuple!(reindexTempl))) {
139         alias ParameterTypeTuple!(reindexTempl)[0] I;
140         alias reindexTempl reindexFun;
141     } else {
142         alias reindexTempl!(size_t) reindexFun;
143         alias size_t I;
144     }
145
146     alias ReturnType!(reindexFun) R;
147     static assert(is(typeof(range[R.init])), "Cannot index a " ~ T.stringof ~
148         " with a " ~ R.stringof ~ ".");
149     alias ElementType!(T) E;
150
151 public:
152     T range;
153     alias range this;
154
155     static if(hasAssignableElements!(T)) {
156         ref E opIndex(I index) {
157             return range[reindexFun(index)];
158         }
159
160         auto opIndexAssign(E val, I index) {
161             return range[reindexFun(index)] = val;
162         }
163     } else {
164         E opIndex(I index) {
165             return range[reindexFun(index)];
166         }
167     }
168
169     static if(is(typeof(range[R.init..R.init]) == T)) {
170         typeof(this) opSlice(I index1, I index2) {
171             return typeof(this)
172                 (range[reindexFun(index1)..reindexFun(index2)]);
173         }
174     }
175
176     static if(is(typeof(range[R.init..R.init] = E.init))) {
177         void opSliceAssign(E val, I index1, I index2) {
178             range[reindexFun(index1)..reindexFun(index2)] = val;
179         }
180     }
181
182     static if(is(typeof(range[R.init..R.init] = T.init))) {
183         void opSliceAssign(T otherRange, I index1, I index2) {
184             return range[reindexFun(index1)..reindexFun(index2)] = otherRange;
185         }
186     }
187 }
188
189 /**Reindex a random-access range by translating its indices via the function
190  * lambda.  All properties and methods of the underlying range that do not
191  * involve indexing are forwarded to the underlying range using alias this.
192  *
193  * Examples:
194  * ---
195  * // An array indexed by only even numbers.
196  * auto evenArray = reindex!"a / 2"([0,2,4,6,8,10].dup);
197  * for(uint i = 0; i <= 10; i += 2) {
198  *     assert(evenArray[i] == i);
199  * }
200  *
201  * // A one-indexed array.
202  * auto oneArray = reindex!"a - 1"([1,2,3,4,5].dup);
203  * for(uint i = 1; i <= 5; i++) {
204  *     assert(oneArray[i] == i);
205  * }
206  * ---
207  */
208 Reindex!(lambda, T) reindex(alias lambda, T)(T range) {
209     return Reindex!(lambda, T)(range);
210 }
211
212 unittest {
213     auto oneArray = reindex!"a - 1"([1,2,3,4,5].dup);
214
215     for(uint i = 1; i <= 5; i++) {
216         assert(oneArray[i] == i);
217     }
218
219     oneArray[1..3] = 1;
220     assert(oneArray[1] == 1);
221     assert(oneArray[2] == 1);
222     assert(oneArray[3] == 3);
223
224     auto last3 = oneArray[3..6];
225     assert(last3[3] == 5);
226
227     auto evenArray = reindex!"a / 2"([0,2,4,6,8,10].dup);
228     for(uint i = 0; i <= 10; i += 2) {
229         assert(evenArray[i] == i);
230     }
231     writeln("Passed Reindex test.");
232 }
233
234 ///
235 struct Rotated(T)
236 if(isRandomAccessRange!(T) && hasLength!(T)) {
237     T range;
238     private size_t startFrom;
239     private size_t nIter;  // For iteration.
240     alias ElementType!(T) E;
241
242     void setStart(size_t start) {
243         startFrom = start;
244     }
245
246     size_t length() {
247         return range.length;
248     }
249
250     static if(hasAssignableElements!(T)) {
251         ref E opIndex(size_t index) {
252             return range[ startFrom + index < length ?
253                     startFrom + index :
254                     startFrom + index - length];
255         }
256
257         auto opIndexAssign(E val, size_t index) {
258             return (startFrom + index < length) ?
259                (range[startFrom + index] = val) :
260                (range[startFrom + index - length] = val);
261         }
262     } else {
263         E opIndex(size_t index) {
264             return range[ startFrom + index < length ?
265                     startFrom + index :
266                     startFrom + index - length];
267         }
268     }
269
270     E front() {
271         return this[0];
272     }
273
274     void popFront() {
275         startFrom++;
276         nIter++;
277     }
278
279     bool empty() {
280         return nIter == length;
281     }
282
283     void put(E elem) {
284         startFrom = (startFrom == length - 1) ? 0 : startFrom + 1;
285         immutable pos = startFrom == 0 ? length - 1 : startFrom - 1;
286         range[pos] = elem;
287     }
288 }
289
290 /**Gives a rotated view of a random access range without modifying the
291  * underlying range.  Also funtions as an output range.  Given an underlying
292  * range of length L, a Rotated range remembers the last L elements inserted
293  * via its put() interface, in the order that they were inserted.  Insertions
294  * are O(1), but to the user of the API, the effect is similar to shifting
295  * an array on each insertion.
296  *
297  * Examples:
298  * ---
299  * uint[] arr = [5,6,7,8,9];
300  * auto rot = rotated(arr, 2);
301  * assert(rot[0] == 7);
302  * assert(rot[3] == 5);
303  * assert(rot.length == 5);
304  *
305  * rot.put(0);
306  * rot.put(1);
307  * rot.put(2);
308  * rot.put(3);
309  * rot.put(4);
310  * foreach(i; 0..5) {
311  *     assert(rot[i] == i);
312  * }
313  * ---
314  */
315 Rotated!(T) rotated(T)(T range, size_t startPos = 0)
316 if(isRandomAccessRange!(T) && hasLength!(T))
317 in {
318     assert(startPos < range.length);
319 } body {
320     alias Rotated!(T) t;
321     return t(range, startPos, 0);
322 }
323
324 unittest {
325     uint[] arr = [5,6,7,8,9];
326     auto rot = rotated(arr, 2);
327     assert(rot[0] == 7);
328     assert(rot[1] == 8);
329     assert(rot[2] == 9);
330     assert(rot[3] == 5);
331     assert(rot[4] == 6);
332     assert(rot.length == 5);
333
334     rot.put(0);
335     rot.put(1);
336     rot.put(2);
337     rot.put(3);
338     rot.put(4);
339     foreach(i; 0..5) {
340         assert(rot[i] == i);
341     }
342
343     writeln("Passed rotated unittest.");
344 }
345
346 /**A struct wrapper for a static array, to get around the fact that static
347  * arrays can't be returned from functions.  Alias this is used to forward
348  * all operations to the underlying static array.*/
349 struct StaticArray(E, size_t N) {
350     ///
351     E[N] values;
352     alias values this;
353 }
354
355 ///
356 struct Comb(size_t N, T)
357 if(isForwardRange!(T)){
358 private:
359     alias ElementType!(T) E;
360     T[N] ranges = void;
361     bool _empty;
362
363     void popAll(size_t startFrom) {
364         if(startFrom == size_t.max) {
365             _empty = true;
366             return;
367         }
368         ranges[startFrom].popFront;
369         if(ranges[startFrom].empty) {
370             return popAll(startFrom - 1);
371         }
372         foreach(i; startFrom + 1..N) {
373             ranges[i] = ranges[i - 1];
374             ranges[i].popFront;
375             if(ranges[i].empty) {
376                 return popAll(startFrom - 1);
377             }
378         }
379     }
380
381
382 public:
383     this(T range) {
384         static if(N > 0) {
385             ranges[0] = range;
386             foreach(i; 1..N) {
387                 ranges[i] = ranges[i - 1];
388                 ranges[i].popFront;
389             }
390         }
391     }
392
393     StaticArray!(E, N) front() {
394         StaticArray!(E, N) ret;
395         foreach(i, range; ranges) {
396             ret[i] = range.front;
397         }
398         return ret;
399     }
400
401
402     void popFront() {
403         static if(N > 0) {
404             popAll(N - 1);
405         }
406     }
407
408     static if(N > 0) {
409         bool empty() {
410             return _empty;
411         }
412     } else {
413         enum bool empty = true;
414     }
415 }
416
417 /**A struct to iterate over all possible unordered combinations of elements
418  * of size N, in a forward range.  This struct is itself a forward range.
419  * Each call to front() returns a StaticArray of size N, containing the
420  * relevant elements of the range.
421  *
422  * Examples:
423  * ---
424  * auto foo = map!(to!(uint, string))(cast(string[]) ["1", "2", "3"]);
425  * auto c = comb!2(foo);
426  * assert(c.front == [1,2]);
427  * c.popFront;
428  * assert(c.front == [1,3]);
429  * c.popFront;
430  * assert(c.front == [2,3]);
431  * c.popFront;
432  * assert(c.empty);
433  * ---
434  */
435 Comb!(N, T) comb(uint N, T)(T range)
436 if(isForwardRange!(T)) {
437     return Comb!(N, T)(range);
438 }
439
440 unittest {
441     auto foo = map!(to!(uint, string))(cast(string[]) ["1", "2", "3"]);
442     auto c = comb!2(foo);
443     assert(c.front == [1,2]);
444     c.popFront;
445     assert(c.front == [1,3]);
446     c.popFront;
447     assert(c.front == [2,3]);
448     c.popFront;
449     assert(c.empty);
450     writeln("Passed comb unittest.");
451 }
452
453 template ElementsTuple(T...) {
454     static if(T.length == 1) {
455         alias TypeTuple!(Unqual!(ElementType!(T[0]))) ElementsTuple;
456     } else {
457         alias TypeTuple!(Unqual!(ElementType!(T[0])), ElementsTuple!(T[1..$]))
458             ElementsTuple;
459     }
460 }
461
462 template SameElemTypes(T...) {
463     static if(T.length == 1) {
464         enum bool SameElemTypes = true;
465     } else {
466         enum bool SameElemTypes =
467             is(Unqual!(ElementType!(T[0])) == Unqual!(ElementType!(T[1]))) &&
468             SameElemTypes!(T[1..$]);
469     }
470 }
471
472 template UnqualAll(T...) {
473     static if(T.length == 1) {
474         alias TypeTuple!(Unqual!(T[0])) UnqualAll;
475     } else {
476         alias TypeTuple!(Unqual!(T[0]), UnqualAll!(T[1..$])) UnqualAll;
477     }
478 }
479
480 ///
481 struct Broadcast(T...)
482 if(allSatisfy!(isForwardRange, T)) {
483 private :
484     UnqualAll!T starts;
485     UnqualAll!T ranges;
486
487     void popFrontImpl(uint index)() {
488         ranges[index].popFront;
489         if(ranges[index].empty) {
490             static if(index == 0) {
491                 return;
492             } else {
493                 ranges[index] = starts[index];
494                 return popFrontImpl!(index - 1)();
495             }
496         }
497     }
498
499 public:
500     this(T args) {
501         starts = args;
502         ranges = args;
503     }
504
505     void popFront() {
506         popFrontImpl!(T.length - 1)();
507     }
508
509     static if(SameElemTypes!(T)) {
510         StaticArray!(Unqual!(ElementType!(T[0])), T.length) front() {
511             typeof(return) ret;
512             foreach(ti, range; ranges) {
513                 ret[ti] = range.front;
514             }
515             return ret;
516         }
517     } else {
518         Tuple!(ElementsTuple!(T)) front() {
519             ElementsTuple!(T) ret;
520             foreach(ti, range; ranges) {
521                 ret[ti] = range.front;
522             }
523             return tuple(ret);
524         }
525     }
526
527     bool empty() {
528         return ranges[0].empty;
529     }
530 }
531
532 /**Given a set of forward ranges, iterate over all possible tuples composed of
533  * [range1Element, range2Element, ..., rangeNElement].  Calling front()
534  * returns a StaticArray if the element types of all of the ranges are
535  * identical and a std.typecons.Tuple otherwise.
536  *
537  * Examples:
538  * ---
539  * // Iterate over all possible RNA codons.
540  * immutable bases = "ACGU";
541  * auto codonRange = broadcast(bases, bases, bases);
542  * assert(codonRange.front == "AAA");
543  * codonRange.popFront;
544  * assert(codonRange.front == "AAC");
545  * ---
546  */
547 Broadcast!(T) broadcast(T...)(T ranges) {
548     return Broadcast!(T)(ranges);
549 }
550
551 unittest {
552     immutable letters = "AB";
553     auto n = broadcast(letters, letters, letters);
554     assert(n.front == "AAA");
555     n.popFront;
556     assert(n.front == "AAB");
557     n.popFront;
558     assert(n.front == "ABA");
559     n.popFront;
560     assert(n.front == "ABB");
561     n.popFront;
562     assert(n.front == "BAA");
563     n.popFront;
564     assert(n.front == "BAB");
565     n.popFront;
566     assert(n.front == "BBA");
567     n.popFront;
568     assert(n.front == "BBB");
569     n.popFront;
570     assert(n.empty);
571     writeln("Passed Broadcast unittest.");
572 }
573
574 template AppendableImpl(T, U) {
575     T[] data;
576     U elem;
577     enum bool ret = is(typeof(data ~= elem));
578 }
579
580 template Appendable(T, U) {
581     enum bool Appendable = AppendableImpl!(T, U).ret;
582 }
583
584 /**An array with a capacity field.  It's based on D's builtin arrays and
585  * forwards everything except appending and resizing to the underlying builtin
586  * array using alias this.  Therefore, the compile time interface and semantics
587  * of a TNew are the same as those of a builtin array, modulo a few bugs in
588  * alias this.
589  *
590  * Examples:
591  * ---
592  * TNew!uint ab, ab2;
593  * foreach(i; 0..5) {
594  *     ab ~= i;
595  *     ab2 ~= [i];
596  * }
597  * assert(ab == cast(uint[]) [0,1,2,3,4]);
598  * assert(ab2 == cast(uint[]) [0,1,2,3,4]);
599  * ---
600  */
601 struct TNew(T) {
602     /**Access underlying array directly.  Useful for getting around some
603      * alias this bugs.*/
604     T[] arr;
605     alias arr this;
606     private size_t capacity = 0;
607
608
609     private void updateCapacity() {
610         capacity = GC.sizeOf(cast(void*) arr.ptr) / T.sizeof;
611     }
612
613     ///
614     this(T[] newData) {
615         arr = newData;
616     }
617
618     /**Append a single element.*/
619     typeof(this) opCatAssign(U)(U elem)
620     if(Appendable!(T, U) && (Appendable!(T, U[]) || !isArray!(U))) {
621         if(arr.length < capacity) {
622             // Casting away const to change the last element of the array.
623             // This is very dangerous, but since this struct is supposed to
624             // faithfully replicate the compile time interface of builtin
625             // arrays, that means replicating the bugs, too.
626             *(cast(Unqual!(T)*) arr.ptr + arr.length) = elem;
627             *(cast(size_t*) &arr) += 1;
628         } else {
629             arr ~= elem;
630             updateCapacity();
631         }
632         return this;
633     }
634
635     /**Append an array.*/
636     typeof(this) opCatAssign(U)(U elems)
637     if(Appendable!(T, U) &&  !(Appendable!(T, U[]) || !isArray!(U))) {
638         if((arr.length + elems.length) <= capacity) {
639             // Again, casting away const to change the last element of the array.
640             auto mutData = (cast(Unqual!(T)*) arr.ptr)
641                            [0..arr.length + elems.length];
642             mutData[arr.length..$] = cast(typeof(mutData)) elems[];
643             arr = cast(typeof(arr)) mutData;
644         } else {
645             arr ~= elems;
646             updateCapacity();
647         }
648         return this;
649     }
650
651     ///
652     typeof(this) opAssign(T[] newData) {
653         if(arr.ptr !is newData.ptr) {
654             capacity = 0;
655         }
656         arr = newData;
657         return this;
658     }
659
660     ///
661     size_t length(size_t newLen) {
662         if(newLen <= arr.length) {
663             return arr.length = newLen;
664         }
665         if(newLen <= capacity) {
666             auto ptr = cast(Unqual!(T)*) arr.ptr;
667             ptr[arr.length..newLen] = T.init;
668             *(cast(size_t*) &arr) = newLen;
669         } else {
670             arr.length = newLen;
671             updateCapacity();
672         }
673         return newLen;
674     }
675
676     /**Make the capacity big enough to hold at least nElem elements
677      * without further GC activity.*/
678     void reserve(size_t nElem) {
679         if(nElem <= arr.length || nElem <= capacity) {
680             return;
681         }
682         updateCapacity();
683         if(nElem <= capacity) {
684             return;
685         }
686         if(typeid(T).flags & 1) {
687             arr = (cast(T*) GC.realloc(cast(void*) arr.ptr, nElem * T.sizeof))
688                   [0..arr.length];
689         } else {
690             arr = (cast(T*) GC.realloc(cast(void*) arr.ptr, nElem * T.sizeof,
691                   GC.BlkAttr.NO_SCAN))[0..arr.length];
692         }
693         updateCapacity();
694     }
695
696     // The following is quick and dirty fixes for stuff that's really bugs in
697     // DMD, specifically in alias this.  The appropriate bug reports have been
698     // filed and these are just temporary kludges.  They don't need ddoc
699     // b/c they should already be expected to work via alias this.
700
701     // Bugzilla 2889
702     size_t length() {
703         return arr.length;
704     }
705
706     // -------------------------Bug 2778-------------------------------
707     T front() {
708         return arr.front;
709     }
710
711     void popFront() {
712         return arr.popFront;
713     }
714
715     bool empty() {
716         return arr.empty;
717     }
718
719     T back() {
720         return arr.back;
721     }
722
723     void popBack() {
724         return arr.popBack;
725     }
726
727     // ----------------------------------------------------------------
728 }
729
730 /**Array builder literals.*/
731 TNew!(T) tNew(T)(T[] data) {
732     TNew!(T) ret;
733     ret = data;
734     return ret;
735 }
736
737 unittest {
738     TNew!uint ab, ab2;
739     foreach(i; 0..5) {
740         ab ~= i;
741         ab2 ~= [i];
742     }
743     assert(ab == cast(uint[]) [0,1,2,3,4]);
744     assert(ab2 == cast(uint[]) [0,1,2,3,4]);
745     writeln("Passed TNew unittest.");
746 }
Note: See TracBrowser for help on using the browser.