1 // Written in the D programming language.
2 /*
3 NYSL Version 0.9982
4 
5 A. This software is "Everyone'sWare". It means:
6   Anybody who has this software can use it as if he/she is
7   the author.
8 
9   A-1. Freeware. No fee is required.
10   A-2. You can freely redistribute this software.
11   A-3. You can freely modify this software. And the source
12       may be used in any software with no limitation.
13   A-4. When you release a modified version to public, you
14       must publish it with your name.
15 
16 B. The author is not responsible for any kind of damages or loss
17   while using or misusing this software, which is distributed
18   "AS IS". No warranty of any kind is expressed or implied.
19   You use AT YOUR OWN RISK.
20 
21 C. Copyrighted to Kazuki KOMATSU
22 
23 D. Above three clauses are applied both to source and binary
24   form of this software.
25 */
26 
27 /**
28 このモジュールは、標準ライブラリのstd.algorithmを強化します。
29 */
30 module carbon.algorithm;
31 
32 import carbon.functional,
33        carbon.range,
34        carbon.templates;
35 
36 
37 import std.algorithm,
38        std.conv,
39        std.functional,
40        std.range,
41        std.string,
42        std.traits;
43 
44 debug import std.stdio;
45 
46 ElementType!R sum(R)(R range)
47 if(isInputRange!R)
48 {
49     return reduce!"a + b"(to!(ElementType!R)(0), range);
50 }
51 
52 
53 ElementType!R product(R)(R range)
54 if(isInputRange!R)
55 {
56     return reduce!"a * b"(to!(ElementType!R)(1), range);
57 }
58 
59 
60 ElementType!R maxOf(R)(R range)
61 if(isInputRange!R)
62 {
63     return reduce!(std.algorithm.max)((ElementType!R).min, range);
64 }
65 
66 
67 ElementType!R minOf(R)(R range)
68 if(isInputRange!R)
69 {
70     return reduce!(std.algorithm.min)((ElementType!R).max, range);
71 }
72 
73 
74 unittest
75 {
76     auto r1 = [1, 2, 3, 4, 5];
77 
78     assert(sum(r1) == 15);
79     assert(product(r1) == 120);
80     assert(maxOf(r1) == 5);
81     assert(minOf(r1) == 1);
82 
83     r1 = r1[$ .. $];
84     assert(sum(r1) == 0);
85     assert(product(r1) == 1);
86     assert(maxOf(r1) == typeof(r1[0]).min);
87     assert(minOf(r1) == typeof(r1[0]).max);
88 }
89 
90 
91 
92 /**
93 tmap
94 */
95 template tmap(fun...)
96 if(fun.length >= 1)
97 {
98     auto tmap(T...)(T args)
99     if(T.length > 1 || (T.length == 1 && !__traits(compiles, ElementType!(T[0]).Types)))
100     {
101         auto dst = TMap!(false, staticMap!(Unqual, T))(args);
102 
103         return dst;
104     }
105 
106 
107     // for tuple range
108     auto tmap(T)(T args)
109     if(__traits(compiles, ElementType!T.Types))
110     {
111         return map!(adaptTuple!(fun, ElementType!T.Types))(args);
112     }
113 
114 
115     struct TMap(bool asB, T...)
116     {
117       private:
118         T _input;
119 
120       static if(isArray!(typeof(fun[$-1]))
121                 && is(ElementType!(typeof(fun[$-1])) : long)
122                 && !isSomeString!(typeof(fun[$-1])))
123       {
124         alias _funWithoutArray = fun[0 .. $-1];
125 
126         alias IndexOfRangeTypeTF = IndexOfRangeTypeTFImpl!(0, fun[$-1]);
127 
128         template IndexOfRangeTypeTFImpl(size_t index, alias array)
129         {
130           static if(index == T.length)
131             alias IndexOfRangeTypeTFImpl = TypeTuple!();
132           else static if(array.length == 0)
133             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index + 1, array));
134           else static if(index == array[0])
135             alias IndexOfRangeTypeTFImpl = TypeTuple!(true, IndexOfRangeTypeTFImpl!(index + 1, array[1 .. $]));
136           else static if(index < array[0])
137             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index + 1, array));
138           else static if(index > array[0])
139             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index, array[1 .. $]));
140           else
141             static assert(0);
142         }
143       }
144       else
145       {
146         alias _funWithoutArray = fun;
147 
148         alias IndexOfRangeTypeTF = IndexOfRangeTypeTFImpl!T;
149 
150         template IndexOfRangeTypeTFImpl(T...)
151         {
152             static if(T.length == 0)
153                 alias IndexOfRangeTypeTFImpl = TypeTuple!();
154             else
155                 alias IndexOfRangeTypeTFImpl = TypeTuple!(isInputRange!(T[0]), IndexOfRangeTypeTFImpl!(T[1..$]));
156         }
157       }
158 
159       static if(_funWithoutArray.length == 1)
160         alias _fun = naryFun!(TypeTuple!(_funWithoutArray)[0]);
161       else
162         alias _fun = adjoin!(staticMap!(naryFun, _funWithoutArray));
163 
164         template RangesTypesImpl(size_t n)
165         {
166             static if(n < T.length)
167             {
168                 static if(IndexOfRangeTypeTF[n])
169                     alias TypeTuple!(T[n], RangesTypesImpl!(n+1)) RangesTypesImpl;
170                 else
171                     alias RangesTypesImpl!(n+1) RangesTypesImpl;
172             }
173             else
174                 alias TypeTuple!() RangesTypesImpl;
175         }
176         
177         alias RangesTypesImpl!(0) RangesTypes;
178         alias staticMap!(ElementType, RangesTypes) ElementTypeOfRanges;
179         alias ETSImpl!(0) ETS;      //_fun args type
180         
181         template ETSImpl(size_t n)
182         {
183             static if(n < T.length)
184             {
185                 static if(IndexOfRangeTypeTF[n])
186                     alias TypeTuple!(ElementType!(T[n]), ETSImpl!(n+1)) ETSImpl;
187                 else
188                     alias TypeTuple!(T[n], ETSImpl!(n+1)) ETSImpl;
189             }
190             else
191                 alias TypeTuple!() ETSImpl;
192         }
193 
194 
195         static assert(is(typeof(_fun(ETS.init))));
196 
197 
198         static string expandMacro(string a, string b)
199         {
200             string dst;
201             foreach(i, isRange; IndexOfRangeTypeTF)
202             {
203               static if(isRange)
204               {
205                 if(a)
206                     dst ~= format(a, i);
207               }
208               else
209               {
210                 if(b)
211                     dst ~= format(b, i);
212               }
213             }
214             return dst;
215         }
216 
217       public:
218 
219       static if(asB && allSatisfy!(isBidirectionalRange, RangesTypes) && allSatisfy!(hasLength, RangesTypes))
220       {
221         @property
222         auto ref back()
223         {
224             return mixin("_fun(" ~ expandMacro("_input[%1$s].back,", "_input[%1$s],") ~ ")");
225         }
226 
227 
228         void popBack()
229         {
230             mixin(expandMacro("_input[%1$s].popBack();\n", null));
231         }
232       }
233         
234         static if(allSatisfy!(isInfinite, RangesTypes))
235         {
236             enum bool empty = false;
237         }
238         else
239         {
240             @property
241             bool empty()
242             {
243                 mixin(expandMacro("if(_input[%1$s].empty) return true;\n", null));
244                 return false;
245             }
246         }
247 
248 
249         void popFront()
250         {
251             mixin(expandMacro("_input[%1$s].popFront();\n", null));
252         }
253 
254 
255         @property
256         auto ref front()
257         {
258             return mixin("_fun(" ~ expandMacro("_input[%1$s].front,", "_input[%1$s],") ~ ")");
259         }
260 
261 
262       static if(allSatisfy!(isRandomAccessRange, RangesTypes))
263       {
264         auto ref opIndex(size_t index)
265         {
266             return mixin("_fun(" ~ expandMacro("_input[%1$s][index],", "_input[%1$s],") ~ ")");
267         }
268       }
269         
270         static if(allSatisfy!(hasLength, RangesTypes))
271         {
272             @property
273             auto length()
274             {
275                 mixin("alias LT = CommonType!(" ~ expandMacro("typeof(_input[%1$s].length),", null) ~ ");");
276 
277                 LT m = LT.max;
278 
279                 mixin(expandMacro("m = min(m, _input[%1$s].length);\n", null));
280                 return m;
281             }
282 
283             alias length opDollar;
284         }
285         
286         static if(allSatisfy!(hasSlicing, RangesTypes))
287         {
288             auto opSlice(size_t idx1, size_t idx2)
289             {
290                 return mixin("TMap!(asB, T)(" ~ expandMacro("_input[%1$s][idx1 .. idx2],", "_input[%1$s],") ~ ")");
291             }
292         }
293         
294         static if(allSatisfy!(isForwardRange, RangesTypes))
295         {
296             @property
297             auto save()
298             {
299                 return mixin("TMap!(asB, T)(" ~ expandMacro("_input[%1$s].save,", "_input[%1$s],") ~ ")");
300             }
301         }
302 
303 
304       static if(allSatisfy!(isBidirectionalRange, RangesTypes) && allSatisfy!(hasLength, RangesTypes))
305       {
306         TMap!(true, T) asBidirectional() @property
307         {
308             auto dst = TMap!(true, T)(this._input);
309 
310             immutable size = dst.length;
311 
312             mixin(expandMacro(q{
313                 static if(hasSlicing!(T[%1$s]))
314                   dst._input[%1$s] = dst._input[%1$s][0 .. size];
315                 else
316                   dst._input[%1$s].popBackN(dst._input[%1$s].length - size);
317               }, null));
318 
319             return dst;
320         }
321       }
322     }
323 }
324 
325 
326 /// ditto
327 unittest
328 {
329     debug scope(failure) writefln("Unittest Failure :%s(%s) ", __FILE__, __LINE__);
330     debug scope(success) {writefln("Unittest Success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
331 
332     auto r1 = [1, 3, 2, 4, 0, 0];
333     auto s = "abc".dup;
334     auto tm1 = tmap!("a", "b", (a, b) => b.repeat(a).array().idup)(r1, s);
335     assert(equal(tm1, [tuple(1, 'a', "a"d),
336                        tuple(3, 'b', "bbb"d),
337                        tuple(2, 'c', "cc"d)]));
338 
339     assert(tmap!"a"(r1, s, "").empty);
340 
341     auto r3 = [1, 2, 3];
342     //first element of [0] expresses r3 is used as range, but second element expresses "abcd" is not used as range.
343     auto ss = tmap!("b[a]", [0])(r3, "abcd");
344     assert(equal(ss, ['b', 'c', 'd'][]));
345     assert(ss.length == 3);
346     assert(equal(ss, ss.save));
347 
348     static assert(isForwardRange!(typeof(ss)));
349     static assert(!isBidirectionalRange!(typeof(ss)));
350     static assert(!isRandomAccessRange!(typeof(ss)));
351 
352     auto ss_b = ss.asBidirectional;
353     static assert(isBidirectionalRange!(typeof(ss_b)));
354     static assert(isRandomAccessRange!(typeof(ss_b)));
355     assert(equal(ss_b.retro, ['d', 'c', 'b']));
356 
357     ///multi function and range-choose test
358     auto tm5 = tmap!("b[a]", "b[a-1]", [0])(r3, "abcd");
359     assert(equal(tm5, [tuple('b', 'a'), tuple('c', 'b'), tuple('d', 'c')][]));
360 }
361 
362 
363 auto flatMap(alias fun, R)(R r)
364 if(isInputRange!R)
365 {
366     return r.map!fun.concat;
367 }
368 
369 unittest
370 {
371     auto r1 = [1, 2, 3, 4];
372     assert(equal(r1.flatMap!"repeat(a, a)", [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]));
373 
374     auto r2 = ["a b c", "de f", "ghi", "jkl mn"];
375     assert(equal(r2.flatMap!split, ["a", "b", "c", "de", "f", "ghi", "jkl", "mn"]));
376 
377     auto r3 = [1, 2];
378     assert(equal!equal(r3.flatMap!"repeat(a, a).repeat(a)",
379         [[1], [2, 2], [2, 2]]));
380 }