1 /**
2 *   Copyright: © 2014-2015 Anton Gushcha
3 *   License: Subject to the terms of the Boost 1.0 license as specified in LICENSE file
4 *   Authors: Anton Gushcha <ncrashed@gmail.com>
5 *
6 *   Template sorting using merge sort algorithm
7 */
8 module stribog.meta.sort;
9 
10 import std.math;
11 import stribog.meta.base;
12 
13 /**
14 *   Perfoms merge sort algorithm on expression list $(B T) using
15 *   function/template $(B F) that takes two elements and returns
16 *   boolean.
17 */
18 template staticSort(alias F, T...)
19 {
20     static if(T.length <= 1) {
21         alias staticSort = T;
22     } else {
23         private template FWrapped(alias a, alias b) 
24         {
25             static if(__traits(compiles, F(T[0], T[1]))) {
26                 static assert(is(typeof(F(T[0], T[1])) == bool), "Predicate return type must be a boolean!");
27                 enum FWrapped = F(a, b);
28             } else {
29                 static assert(is(typeof(F!(T[0], T[1])) == bool), "Predicate return type must be a boolean!");
30                 enum FWrapped = F!(a, b);
31             }
32         }
33         
34         private template merge(alias as, alias bs)
35         {
36             static if(as.expand.length == 0) {
37                 alias merge = bs;
38             } else static if (bs.expand.length == 0) {
39                 alias merge = as;
40             } else {                
41                 static if(FWrapped!(as.expand[0], bs.expand[0])) {
42                     alias merge = StrictExpressionList!(bs.expand[0], merge!(as, StrictExpressionList!(bs.expand[1 .. $])).expand); 
43                 } else {
44                     alias merge = StrictExpressionList!(as.expand[0], merge!(StrictExpressionList!(as.expand[1 .. $]), bs).expand); 
45                 } 
46             }
47         }
48         
49         private template mergePairs(xs...)
50         {
51             static if(xs.length < 2) {
52                 alias mergePairs = xs;
53             } else {
54                 alias mergePairs = ExpressionList!(merge!(xs[0], xs[1]), mergePairs!(xs[2..$]));
55             }
56         }
57         
58         private template mergeAll(xs...)
59         {
60             static if(xs.length <= 1) {
61                 alias mergeAll = xs;
62             } else {
63                 alias mergeAll = mergeAll!(mergePairs!xs);
64             }
65         }
66         
67         private template ascending(alias a, alias as, bs...)
68         {
69             static if (bs.length > 1 && !FWrapped!(a, bs[0])) {
70                 alias ascending = ascending!(bs[0], StrictExpressionList!(as.expand, a), bs[1..$]);
71             } else {
72                 alias ascending = ExpressionList!(StrictExpressionList!(as.expand, a), sequences!bs);
73             }
74         }
75         
76         private template descending(alias a, alias as, bs...)
77         {
78             static if (bs.length > 1 && FWrapped!(a, bs[0])) {
79                 alias descending = descending!(bs[0], StrictExpressionList!(a, as.expand), bs[1..$]);
80             } else {
81                 alias descending = ExpressionList!(StrictExpressionList!(a, as.expand), sequences!bs);
82             }
83         }
84         
85         private template sequences(U...)
86         {
87             static if (U.length < 2) {
88                 alias sequences = StrictExpressionList!U;
89             } else static if(FWrapped!(U[0], U[1])) {
90                 alias sequences = descending!(U[1], StrictExpressionList!(U[0]), U[2 .. $]);
91             } else {
92                 alias sequences = ascending!(U[1], StrictExpressionList!(U[0]), U[2 .. $]);
93             }
94         }
95         
96         private alias temp = mergeAll!(sequences!T)[0];
97         alias staticSort = temp.expand;
98     }
99 }
100 /// Example
101 unittest
102 {
103     bool ascending(int a, int b)
104     {
105         return a > b;
106     }
107     
108     bool descending(int a, int b)
109     {
110         return a < b;
111     }
112     
113     template Ascending(int a, int b)
114     {
115         enum Ascending = a > b;
116     }
117     
118     template Descending(int a, int b)
119     {
120         enum Descending = a < b;
121     }
122     
123     static assert([staticSort!(ascending, 3, 1, 5, 2)] == [1, 2, 3, 5]);
124     static assert([staticSort!(Ascending, 3, 1, 5, 2)] == [1, 2, 3, 5]);
125     static assert([staticSort!(descending, 3, 1, 5, 2)] == [5, 3, 2, 1]);
126     static assert([staticSort!(Descending, 3, 1, 5, 2)] == [5, 3, 2, 1]);
127     
128     import std.string;
129     
130     bool wordsCmp(string a, string b)
131     {
132         return toUpper(a) > toUpper(b);
133     }
134     
135     alias words = ExpressionList!("aBc", "a", "abc", "b", "ABC", "c");
136     alias sortedWords = staticSort!(wordsCmp, words);
137     static assert([sortedWords] == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
138 }