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 public import std.algorithm;
33 
34 import carbon.functional,
35        carbon.range,
36        carbon.templates;
37 
38 
39 import std.algorithm,
40        std.conv,
41        std.functional,
42        std.range,
43        std.string,
44        std.traits,
45        std.typetuple;
46 
47 debug import std.stdio;
48 
49 
50 /**
51 引数に与えられたレンジの総和を返します。
52 引数には、無限レンジを渡すことができません。
53 */
54 ElementType!R sum(R)(R range)
55 if(isInputRange!R && !isInfinite!R)
56 {
57     return reduce!"a + b"(to!(ElementType!R)(0), range);
58 }
59 
60 
61 ElementType!R product(R)(R range)
62 if(isInputRange!R && !isInfinite!R)
63 {
64     return reduce!"a * b"(to!(ElementType!R)(1), range);
65 }
66 
67 
68 ElementType!R maxOf(R)(R range)
69 if(isInputRange!R && !isInfinite!R)
70 {
71     return reduce!(std.algorithm.max)((ElementType!R).min, range);
72 }
73 
74 
75 ElementType!R minOf(R)(R range)
76 if(isInputRange!R && !isInfinite!R)
77 {
78     return reduce!(std.algorithm.min)((ElementType!R).max, range);
79 }
80 
81 
82 unittest
83 {
84     auto r1 = [1, 2, 3, 4, 5];
85 
86     assert(sum(r1) == 15);
87     assert(product(r1) == 120);
88     assert(maxOf(r1) == 5);
89     assert(minOf(r1) == 1);
90 
91     r1 = r1[$ .. $];
92     assert(sum(r1) == 0);
93     assert(product(r1) == 1);
94     assert(maxOf(r1) == typeof(r1[0]).min);
95     assert(minOf(r1) == typeof(r1[0]).max);
96 }
97 
98 
99 
100 /**
101 tmap
102 */
103 template tmap(fun...)
104 if(fun.length >= 1)
105 {
106     auto tmap(T...)(T args)
107     if(T.length > 1 || (T.length == 1 && !__traits(compiles, ElementType!(T[0]).Types)))
108     {
109         auto dst = TMap!(false, staticMap!(Unqual, T))(args);
110 
111         return dst;
112     }
113 
114 
115     // for tuple range
116     auto tmap(T)(T args)
117     if(__traits(compiles, ElementType!T.Types))
118     {
119         return map!(adaptTuple!(fun, ElementType!T.Types))(args);
120     }
121 
122 
123     struct TMap(bool asB, T...)
124     {
125       private:
126         T _input;
127 
128       static if(isArray!(typeof(fun[$-1]))
129                 && is(ElementType!(typeof(fun[$-1])) : long)
130                 && !isSomeString!(typeof(fun[$-1])))
131       {
132         alias _funWithoutArray = fun[0 .. $-1];
133 
134         alias IndexOfRangeTypeTF = IndexOfRangeTypeTFImpl!(0, fun[$-1]);
135 
136         template IndexOfRangeTypeTFImpl(size_t index, alias array)
137         {
138           static if(index == T.length)
139             alias IndexOfRangeTypeTFImpl = TypeTuple!();
140           else static if(array.length == 0)
141             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index + 1, array));
142           else static if(index == array[0])
143             alias IndexOfRangeTypeTFImpl = TypeTuple!(true, IndexOfRangeTypeTFImpl!(index + 1, array[1 .. $]));
144           else static if(index < array[0])
145             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index + 1, array));
146           else static if(index > array[0])
147             alias IndexOfRangeTypeTFImpl = TypeTuple!(false, IndexOfRangeTypeTFImpl!(index, array[1 .. $]));
148           else
149             static assert(0);
150         }
151       }
152       else
153       {
154         alias _funWithoutArray = fun;
155 
156         alias IndexOfRangeTypeTF = IndexOfRangeTypeTFImpl!T;
157 
158         template IndexOfRangeTypeTFImpl(T...)
159         {
160             static if(T.length == 0)
161                 alias IndexOfRangeTypeTFImpl = TypeTuple!();
162             else
163                 alias IndexOfRangeTypeTFImpl = TypeTuple!(isInputRange!(T[0]), IndexOfRangeTypeTFImpl!(T[1..$]));
164         }
165       }
166 
167       static if(_funWithoutArray.length == 1)
168         alias _fun = naryFun!(TypeTuple!(_funWithoutArray)[0]);
169       else
170         alias _fun = adjoin!(staticMap!(naryFun, _funWithoutArray));
171 
172         template RangesTypesImpl(size_t n)
173         {
174             static if(n < T.length)
175             {
176                 static if(IndexOfRangeTypeTF[n])
177                     alias TypeTuple!(T[n], RangesTypesImpl!(n+1)) RangesTypesImpl;
178                 else
179                     alias RangesTypesImpl!(n+1) RangesTypesImpl;
180             }
181             else
182                 alias TypeTuple!() RangesTypesImpl;
183         }
184         
185         alias RangesTypesImpl!(0) RangesTypes;
186         alias staticMap!(ElementType, RangesTypes) ElementTypeOfRanges;
187         alias ETSImpl!(0) ETS;      //_fun args type
188         
189         template ETSImpl(size_t n)
190         {
191             static if(n < T.length)
192             {
193                 static if(IndexOfRangeTypeTF[n])
194                     alias TypeTuple!(ElementType!(T[n]), ETSImpl!(n+1)) ETSImpl;
195                 else
196                     alias TypeTuple!(T[n], ETSImpl!(n+1)) ETSImpl;
197             }
198             else
199                 alias TypeTuple!() ETSImpl;
200         }
201 
202 
203         static assert(is(typeof(_fun(ETS.init))));
204 
205 
206         static string expandMacro(string a, string b)
207         {
208             string dst;
209             foreach(i, isRange; IndexOfRangeTypeTF)
210             {
211               static if(isRange)
212               {
213                 if(a)
214                     dst ~= format(a, i);
215               }
216               else
217               {
218                 if(b)
219                     dst ~= format(b, i);
220               }
221             }
222             return dst;
223         }
224 
225       public:
226 
227       static if(asB && allSatisfy!(isBidirectionalRange, RangesTypes) && allSatisfy!(hasLength, RangesTypes))
228       {
229         @property
230         auto ref back()
231         {
232             return mixin("_fun(" ~ expandMacro("_input[%1$s].back,", "_input[%1$s],") ~ ")");
233         }
234 
235 
236         void popBack()
237         {
238             mixin(expandMacro("_input[%1$s].popBack();\n", null));
239         }
240       }
241         
242         static if(allSatisfy!(isInfinite, RangesTypes))
243         {
244             enum bool empty = false;
245         }
246         else
247         {
248             @property
249             bool empty()
250             {
251                 mixin(expandMacro("if(_input[%1$s].empty) return true;\n", null));
252                 return false;
253             }
254         }
255 
256 
257         void popFront()
258         {
259             mixin(expandMacro("_input[%1$s].popFront();\n", null));
260         }
261 
262 
263         @property
264         auto ref front()
265         {
266             return mixin("_fun(" ~ expandMacro("_input[%1$s].front,", "_input[%1$s],") ~ ")");
267         }
268 
269 
270       static if(allSatisfy!(isRandomAccessRange, RangesTypes))
271       {
272         auto ref opIndex(size_t index)
273         {
274             return mixin("_fun(" ~ expandMacro("_input[%1$s][index],", "_input[%1$s],") ~ ")");
275         }
276       }
277         
278         static if(allSatisfy!(hasLength, RangesTypes))
279         {
280             @property
281             auto length()
282             {
283                 mixin("alias LT = CommonType!(" ~ expandMacro("typeof(_input[%1$s].length),", null) ~ ");");
284 
285                 LT m = LT.max;
286 
287                 mixin(expandMacro("m = min(m, _input[%1$s].length);\n", null));
288                 return m;
289             }
290 
291             alias length opDollar;
292         }
293         
294         static if(allSatisfy!(hasSlicing, RangesTypes))
295         {
296             auto opSlice(size_t idx1, size_t idx2)
297             {
298                 return mixin("TMap!(asB, T)(" ~ expandMacro("_input[%1$s][idx1 .. idx2],", "_input[%1$s],") ~ ")");
299             }
300         }
301         
302         static if(allSatisfy!(isForwardRange, RangesTypes))
303         {
304             @property
305             auto save()
306             {
307                 return mixin("TMap!(asB, T)(" ~ expandMacro("_input[%1$s].save,", "_input[%1$s],") ~ ")");
308             }
309         }
310 
311 
312       static if(allSatisfy!(isBidirectionalRange, RangesTypes) && allSatisfy!(hasLength, RangesTypes))
313       {
314         TMap!(true, T) asBidirectional() @property
315         {
316             auto dst = TMap!(true, T)(this._input);
317 
318             immutable size = dst.length;
319 
320             mixin(expandMacro(q{
321                 static if(hasSlicing!(T[%1$s]))
322                   dst._input[%1$s] = dst._input[%1$s][0 .. size];
323                 else
324                   dst._input[%1$s].popBackN(dst._input[%1$s].length - size);
325               }, null));
326 
327             return dst;
328         }
329       }
330     }
331 }
332 
333 
334 /// ditto
335 unittest
336 {
337     debug scope(failure) writefln("Unittest Failure :%s(%s) ", __FILE__, __LINE__);
338     debug scope(success) {writefln("Unittest Success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
339 
340     auto r1 = [1, 3, 2, 4, 0, 0];
341     auto s = "abc".dup;
342     auto tm1 = tmap!("a", "b", (a, b) => b.repeat(a).array().idup)(r1, s);
343     assert(equal(tm1, [tuple(1, 'a', "a"d),
344                        tuple(3, 'b', "bbb"d),
345                        tuple(2, 'c', "cc"d)]));
346 
347     assert(tmap!"a"(r1, s, "").empty);
348 
349     auto r3 = [1, 2, 3];
350     //first element of [0] expresses r3 is used as range, but second element expresses "abcd" is not used as range.
351     auto ss = tmap!("b[a]", [0])(r3, "abcd");
352     assert(equal(ss, ['b', 'c', 'd'][]));
353     assert(ss.length == 3);
354     assert(equal(ss, ss.save));
355 
356     static assert(isForwardRange!(typeof(ss)));
357     static assert(!isBidirectionalRange!(typeof(ss)));
358     static assert(!isRandomAccessRange!(typeof(ss)));
359 
360     auto ss_b = ss.asBidirectional;
361     static assert(isBidirectionalRange!(typeof(ss_b)));
362     static assert(isRandomAccessRange!(typeof(ss_b)));
363     assert(equal(ss_b.retro, ['d', 'c', 'b']));
364 
365     ///multi function and range-choose test
366     auto tm5 = tmap!("b[a]", "b[a-1]", [0])(r3, "abcd");
367     assert(equal(tm5, [tuple('b', 'a'), tuple('c', 'b'), tuple('d', 'c')][]));
368 }
369 
370 
371 
372 /**
373 各要素にある関数を適用し、それらを結合します。
374 つまり、r.map!fun.concatと等価です
375 */
376 auto flatMap(alias fun, R)(R r)
377 if(isInputRange!R)
378 {
379     return r.map!fun.concat;
380 }
381 
382 ///
383 unittest
384 {
385     debug scope(failure) writefln("Unittest Failure :%s(%s) ", __FILE__, __LINE__);
386     debug scope(success) {writefln("Unittest Success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
387 
388     auto r1 = [1, 2, 3, 4];
389     assert(equal(r1.flatMap!"repeat(a, a)", [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]));
390 
391     auto r2 = ["a b c", "de f", "ghi", "jkl mn"];
392     assert(equal(r2.flatMap!split, ["a", "b", "c", "de", "f", "ghi", "jkl", "mn"]));
393 
394     auto r3 = [1, 2];
395     assert(equal!equal(r3.flatMap!"repeat(a, a).repeat(a)",
396         [[1], [2, 2], [2, 2]]));
397 }
398 
399 
400 /**
401 Phobosの$(D std.algorithm.reduce)の拡張です。
402 つまり、$(D r.reduceEx!f(init))という呼び出しが有効になります。
403 */
404 template reduceEx(f...)
405 if(f.length >= 1)
406 {
407     auto ref reduceEx(R, E)(auto ref R r, auto ref E e)
408     if(is(typeof(std.algorithm.reduce!f(forward!e, forward!r))))
409     {
410         return std.algorithm.reduce!f(forward!e, forward!r);
411     }
412 
413 
414     auto ref reduceEx(R)(auto ref R r)
415     if(is(typeof(std.algorithm.reduce!f(forward!r))))
416     {
417         return std.algorithm.reduce!f(forward!r);
418     }
419 }
420 
421 ///
422 unittest{
423     debug scope(failure) writefln("Unittest Failure :%s(%s) ", __FILE__, __LINE__);
424     debug scope(success) {writefln("Unittest Success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
425 
426     assert(reduceEx!"a+1"([1, 2, 3], 1) == reduce!"a+1"(1, [1, 2, 3]));
427     assert(reduceEx!"a+1"([1, 2, 3]) == reduce!"a+1"([1, 2, 3]));
428 }
429 
430 
431 /**
432 reduceは、レンジのすべての要素をたどりますが、scanはその途中結果を要素としてもつレンジです。
433 */
434 auto scan(alias fun, R)(R r)
435 if(isInputRange!R && !isInfinite!R)
436 {
437     alias E = Unqual!(ElementType!R);
438     E e;
439     return scan!fun(e, r);
440 }
441 
442 
443 /// ditto
444 auto scan(alias fun, R, E)(E iniValue, R r)
445 if(isInputRange!R && !isInfinite!R && is(typeof(binaryFun!fun(iniValue, r.front)) : E))
446 {
447     static struct Scan()
448     {
449         @property
450         auto ref front() inout
451         {
452             return _v;
453         }
454 
455 
456         void popFront()
457         {
458             if(!_rIsEmpty){
459                 _v = binaryFun!fun(_v, _r.front);
460                 _r.popFront();
461             }else
462               _isEmpty = true;
463         }
464 
465 
466         @property
467         bool empty()
468         {
469             if(!_rIsEmpty)
470                 _rIsEmpty = _r.empty;
471 
472             return _isEmpty;
473         }
474 
475 
476       static if(isForwardRange!R)
477       {
478         @property
479         auto save()
480         {
481             typeof(this) dst = this;
482 
483             dst._r = dst._r.save;
484 
485             return dst;
486         }
487       }
488 
489 
490       private:
491         E _v;
492         R _r;
493         bool _rIsEmpty = false;
494         bool _isEmpty = false;
495     }
496 
497 
498     return Scan!()(iniValue, r);
499 }
500 
501 ///
502 unittest
503 {
504     debug scope(failure) writefln("Unittest Failure :%s(%s) ", __FILE__, __LINE__);
505     debug scope(success) {writefln("Unittest Success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
506 
507     // test cases : http://zvon.org/other/haskell/Outputprelude/scanl_f.html
508     assert(equal(scan!"a / b"(64, [4, 2, 4]), [64, 16, 8, 2]));
509 
510     int[] none;
511     assert(equal(scan!"a / b"(3, none), [3]));
512 
513     import std.algorithm : max;
514     assert(equal(scan!max(5, [1, 2, 3, 4]), [5, 5, 5, 5, 5]));
515 
516     assert(equal(scan!max(5, [1, 2, 3, 4, 5, 6, 7]), [5, 5, 5, 5, 5, 5, 6, 7]));
517 
518     assert(equal(scan!"2*a + b"(4, [1, 2, 3]), [4, 9, 20, 43]));
519 }