1 module des.stdx.range;
2 
3 public import std.range;
4 
5 import std.conv;
6 import std.algorithm;
7 
8 import des.stdx.traits;
9 import des.ts;
10 
11 private version(unittest)
12 {
13     struct VecN(size_t N){ float[N] data; alias data this; }
14     alias Vec=VecN!3;
15     static assert( canUseAsArray!Vec );
16 }
17 
18 /++ fill output range with result of fn() called per elements
19  +/
20 void mapFlat(alias fn,R,V,E...)( ref R output, V val, E tail )
21     if( is( typeof( output.put( fn( (ElementTypeRec!V).init ) ) ) ) )
22 {
23     static if( E.length > 0 )
24     {
25         mapFlat!fn( output, val );
26         mapFlat!fn( output, tail );
27     }
28     else
29     {
30         static if( isInputRange!V )
31             foreach( l; val ) mapFlat!fn( output, l );
32         else static if( canUseAsArray!V )
33             foreach( i; 0 .. val.length )
34                 mapFlat!fn( output, val[i] );
35         else static if( is( ElementTypeRec!V == V ) )
36             output.put( fn( val ) );
37         else static assert( 0, "V has elements, " ~
38                 "but isn't input range, or not have length" );
39     }
40 }
41 
42 ///
43 unittest
44 {
45     import std.array;
46 
47     auto app = appender!(int[])();
48     mapFlat!(a=>to!int(a)*2)( app, [1,2], 3,
49                        [[4,5],[6]],
50                        iota(7,9),
51                        [[[9],[10,11]],[[12]]],
52                        Vec([13.3,666,105]) );
53     assertEq( app.data, [2,4,6,8,10,12,14,16,18,20,22,24,26,1332,210] );
54 }
55 
56 ///
57 template ElementTypeRec(T)
58 {
59     static if( is( ElementType!T == void ) ) // is not range or array
60         alias ElementTypeRec = T;
61     else // if is range or array
62         alias ElementTypeRec = ElementTypeRec!(ElementType!T);
63 }
64 
65 ///
66 unittest
67 {
68     static assert( is( ElementTypeRec!int == int ) );
69     static assert( is( ElementTypeRec!(int[]) == int ) );
70     static assert( is( ElementTypeRec!(int[2]) == int ) );
71     static assert( is( ElementTypeRec!(int[][]) == int ) );
72     static assert( is( ElementTypeRec!(int[3][2]) == int ) );
73     static assert( is( ElementTypeRec!(typeof(iota(7))) == int ) );
74     static assert( is( ElementTypeRec!(VecN!10) == float ) );
75 }
76 
77 /++ fill output range with flat values
78  +/
79 void fillFlat(T,R,V,E...)( ref R output, V val, E tail )
80     if( isOutputRange!(R,T) )
81 { mapFlat!(a=>to!T(a))( output, val, tail ); }
82 
83 ///
84 unittest
85 {
86     import std.array;
87 
88     auto app = appender!(int[])();
89     fillFlat!int( app, [1,2], 3,
90                        [[4,5],[6]],
91                        iota(7,9),
92                        [[[9],[10,11]],[[12]]],
93                        Vec([13.3,666,105]) );
94     assertEq( app.data, [1,2,3,4,5,6,7,8,9,10,11,12,13,666,105] );
95 }
96 
97 /++ get flat length of values
98  +/
99 size_t getFlatLength(V,E...)( V val, E tail ) @nogc
100 {
101     static if( E.length > 0 )
102         return getFlatLength( val ) + getFlatLength( tail );
103     else
104     {
105         static if( isInfinite!V )
106             static assert( 0, "not support infinite range" );
107         else static if( isForwardRange!V )
108             return val.save().map!(a=>getFlatLength(a)).sum;
109         else static if( canUseAsArray!V )
110         {
111             size_t s = 0;
112             foreach( i; 0 .. val.length )
113                 s += getFlatLength( val[i] );
114             return s;
115         }
116         else static if( is( ElementType!V == void ) )
117             return 1;
118         else static assert( 0, "unsupported type" );
119     }
120 }
121 
122 ///
123 unittest
124 {
125     assertEq( getFlatLength( 1,2,3 ), 3 );
126     assertEq( getFlatLength( [1,2,3] ), 3 );
127     assertEq( getFlatLength( Vec([1,2,3]) ), 3 );
128     assertEq( getFlatLength( [1,2], 3, [[[4],[5,6]],[[7,8,9]]], Vec([1,2,3]) ), 12 );
129     assertEq( getFlatLength( 1, 2, iota(4) ), 6 );
130     assertEq( getFlatLength( 1, 2, iota(4) ), 6 );
131 }
132 
133 /// return output range with reference to array
134 auto arrayOutputRange(A)( ref A arr )
135     if( isArray!A || isStaticArray!A )
136 {
137     alias T = ElementType!A;
138 
139     static struct Result
140     {
141         T* end, cur;
142 
143         this( T* start, T* end )
144         {
145             this.cur = start;
146             this.end = end;
147         }
148 
149         void put( T val )
150         {
151             assert( cur != end );
152             *(cur++) = val;
153         }
154     }
155 
156     return Result( arr.ptr, arr.ptr + arr.length );
157 }
158 
159 ///
160 unittest
161 {
162     int[13] arr1;
163     auto rng1 = arrayOutputRange( arr1 );
164     fillFlat!int( rng1, [1,2], 3, [[4,5],[6]], iota(7,9), [[[9],[10,11]],[[12]]] );
165     assertEq( arr1, [1,2,3,4,5,6,7,8,9,10,11,12,0] );
166 
167     int[] arr2 = new int[]( 13 ) ;
168     auto rng2 = arrayOutputRange( arr2 );
169     fillFlat!int( rng2, [1,2], 3, [[4,5],[6]], iota(7,9), [[[9],[10,11]],[[12]]] );
170     assertEq( arr2, [1,2,3,4,5,6,7,8,9,10,11,12,0] );
171 }
172 
173 /// create flat copy of vals
174 auto flatData(T,E...)( in E vals )
175 if( E.length > 0 )
176 {
177     auto ret = appender!(T[])();
178     fillFlat!T( ret, vals );
179     return ret.data;
180 }
181 
182 ///
183 unittest
184 {
185     assertEq( flatData!float([1.0,2],[[3,4]],5,[6,7]), [1,2,3,4,5,6,7] );
186 
187     assertEq( flatData!double( VecN!3([1,2,3]) ), [1,2,3] );
188     assertEq( flatData!double( VecN!1([2]) ), [2] );
189 }