1 /// This module defines the core transducer primitives.
2 module rxd.xf.xform;
3 
4 import std.meta, std.traits;
5 import std.range : ElementType;
6 
7 import rxd.meta2.lambda : λ;
8 import rxd.meta2.switch_n : switch2, switch3;
9 
10 auto outputRf = λ!((output, input)
11 {
12     import std.range : put;
13     output.put(input);
14     return output;
15 });
16 
17 auto transform(alias fn, I, O)(I input, O output)
18 {
19     return input.accumulate(map!fn()(outputRf), output);
20 }
21 
22 private T[n] s(T, size_t n)(T[n] literal) { return literal; }
23 
24 /// Showcase transform (map + copy)
25 nothrow @safe @nogc
26 unittest
27 {
28     auto arr1 = [4, 2, 7, 4, 6, 5, 1].s;
29     auto arr2 = typeof(arr1).init;
30 
31     transform!(x => x * 2)(arr1[], arr2[]);
32 
33     assert (arr2 == [8, 4, 14, 8, 12, 10, 2]);
34 }
35 
36 /// Showcase type sequence transformation
37 pure nothrow @safe @nogc
38 unittest
39 {
40     import rxd.meta2.type;
41     import std.meta : AliasSeq;
42 
43     auto trf = λ!( (state, input) => state.append(input) );
44 
45     alias transform = (input, xform) =>
46         accumulate(input, xform(trf), aliasTuple());
47 
48     auto tuple = AliasTuple!(Type!int, Type!double, Type!char)();
49 
50     auto large = filter!(t => t.typeOf.sizeof >= 4);
51     auto result1 = transform(tuple, large);
52     static assert (is(result1.typeSeq == AliasSeq!(int, double)));
53 
54     auto toArray = map!(t => t[]);
55     auto result2 = transform(tuple, toArray);
56     static assert (is(result2.typeSeq == AliasSeq!(int[], double[], char[])));
57 
58     auto xf = large(toArray(trf));
59     auto result3 = tuple.accumulate(xf, aliasTuple());
60     static assert (is(result3.typeSeq == AliasSeq!(int[], double[])));
61 }
62 
63 /// Showcase reduce-like usage
64 pure nothrow @safe @nogc
65 unittest
66 {
67     import std.range : iota, chain;
68 
69     assert (1.iota(4).accumulate(λ!((a, b) => a + b), 0) == 6);
70 
71     assert (1.iota(5).accumulate(λ!((a, b) => a * b), 1) == 24);
72 
73     //assert (["I", "am", "one"].s[].accumulate(λ!((a, b) => chain(a, b, " ")), "") == "I am one ");
74 }
75 
76 /// Showcase composition
77 unittest
78 {
79     import std.stdio, std.range;
80     auto sum = λ!((a, b) => a + b);
81 
82     auto odd = filter!(x => x % 2 == 1);
83     assert (1.iota(4).accumulate(odd(sum), 0) == 1 + 3);
84 
85     auto mulBy2 = map!(x => x * 2);
86     assert (1.iota(4).accumulate(mulBy2(sum), 0) == 2 + 4 + 6);
87 
88     assert (1.iota(4).accumulate(odd(mulBy2(sum)), 0) == 2 + 6);
89 }
90 
91 ///
92 unittest
93 {
94     //auto rangeWrapperRf = λ!((state, input)
95     //{
96     //    import std.range : put;
97     //    output.put(input);
98     //    return output;
99     //});
100 
101     //alias sequence = (input, xform) =>
102     //    input.accumulate(xform(outputRf), output);
103 
104     //writeln(transform([1, 2, 3], map2((int x) => x + 1)));
105 }
106 
107 ///
108 auto map(alias mapFn)()
109 {
110     return λ!((step) {
111         return λ!((result, input) {
112             return step(result, mapFn(input));
113         });
114     });
115 }
116 
117 ///
118 auto filter(alias filterPred)()
119 {
120     return λ!(
121         (step)
122         {
123             return λ!(
124                 (rf, result, input)
125                 {
126                     import rxd.meta2.type : isType, isAliasTuple;
127                     static if (isType!(typeof(input)) ||
128                             isAliasTuple!(typeof(input)))
129                     {
130                         static if (filterPred(typeof(input).init))
131                             return rf(result, input);
132                         else
133                             return result;
134                     }
135                     else
136                     {
137                         return filterPred(input)?
138                             rf(result, input) :
139                             result;
140                     }
141 
142                     //mixin (switch2!(
143                     //    "filterPred(input)",
144                     //    "return rf(result, input);",
145                     //    "return result;",
146                     //));
147                 }
148             )(step);
149         }
150     );
151 };
152 
153 ///
154 auto accumulate(Rf, S, In, size_t line = __LINE__)(In input, Rf step, S state)
155     if ( is(ElementType!In E))
156 {
157     pragma (msg, line.stringof ~ " accumulate: S(", typeof(S.init),
158             "), E(", typeof(ElementType!In.init), ") ");
159 
160     static if (__traits(compiles, { enum b = input.empty; } ))
161     {
162         static if (input.empty)
163             return state;
164         else
165             return step(accumulate(input.dropBackOne, step, state), input.back);
166     }
167     else
168     {
169         foreach (elem; input)
170                 state = step(state, elem);
171 
172             return state;
173     }
174 //    mixin (switch3!("input.empty",
175 //        "return state;",
176 //        "return step(accumulate(input.dropBackOne, step, state), input.back);",
177 //        q{
178 //            foreach (elem; input)
179 //                state = step(state, elem);
180 //
181 //            return state;
182 //        }
183 //    ));
184 }
185 
186 ///
187 
188 
189 
190 struct Transducer(Input, Output = Input)
191 {
192 
193 }
194 
195 unittest
196 {
197     //Transducer!int filter_odd = filter((int x) => x % 2);
198 
199     //Transducer!(int, string) serialize = map((int x) => "int" );
200 }