1 module des.math.combin.cons;
2 
3 import std.algorithm;
4 import std.typecons;
5 import std.range;
6 import std.meta;
7 
8 auto combinations(T...)( T tpls )
9 {
10     auto result(R...)( R rrr )
11         if( allSatisfy!(isInputRange,R) )
12     {
13         static struct Result
14         {
15             R r, orig; // храним текущее состояние и исходное
16 
17             this( R r ) { this.r = r; orig = r; }
18 
19             bool empty() @property { return r[0].empty; }
20 
21             void popFront()
22             {
23                 foreach_reverse( ref x; r )
24                 {
25                     x.popFront();
26                     if( !x.empty ) break;
27                 }
28 
29                 foreach( i, ref x; r[1..$] )
30                 {
31                     if( x.empty )
32                         x = orig[i+1];
33                 }
34             }
35 
36             auto front() @property { return getFronts( r ); }
37         }
38 
39         return Result(rrr);
40     }
41 
42     auto wrapTuples(X...)( X t ) pure
43     {
44         static if( X.length == 1 )
45         {
46             static if( isTuple!(X[0]) )
47                 return wrapTuples( t[0].expand );
48             else
49                 return tuple( t[0] );
50         }
51         else static if( X.length > 1 )
52             return tuple( wrapTuples(t[0]).expand, wrapTuples(t[1..$]).expand );
53     }
54 
55     return result( wrapTuples(tpls).expand );
56 }
57 
58 unittest
59 {
60     auto pp = combinations( iota(2), iota(3) );
61     auto exp = [ tuple(0,0), tuple(0,1), tuple(0,2),
62                  tuple(1,0), tuple(1,1), tuple(1,2) ];
63     assert( equal( pp, exp ) );
64 }
65 
66 unittest
67 {
68     auto pp1 = combinations( hCube!2(iota(3)), ["alpha","beta","gamma"], hCube!2(iota(6)) );
69     auto pp2 = combinations( iota(3), iota(3), ["alpha","beta","gamma"], iota(6), iota(6) );
70     assert( equal( pp1, pp2 ) );
71 }
72 
73 auto getFronts(R...)( R r )
74     if( allSatisfy!(isInputRange,R) )
75 {
76     static if( R.length == 1 ) return tuple( r[0].front );
77     else static if( R.length > 1 )
78         return tuple( getFronts(r[0]).expand, getFronts(r[1..$]).expand );
79     else static assert(0, "no ranges - no fronts" );
80 }
81 
82 auto hCube(size_t N,R)( R r )
83 {
84     static if( N == 0 ) return tuple();
85     static if( N == 1 ) return tuple(r);
86     else return tuple( r, hCube!(N-1)(r).expand );
87 }
88 
89 unittest
90 {
91     auto hc = hCube!3(iota(5));
92     static assert( hc.length == 3 );
93     assert( equal( hc[0], iota(5) ) );
94     assert( equal( hc[1], iota(5) ) );
95     assert( equal( hc[2], iota(5) ) );
96 }
97 
98 auto wrapTuple(T)( T val )
99 {
100     static if( isTuple!T ) return val;
101     else return tuple(val);
102 }
103 
104 auto partialPermutation(R)( R r, size_t k )
105     if( isInputRange!R )
106 {
107     static struct Result
108     {
109         R[] r, orig; // храним текущее состояние и исходное
110 
111         this( R[] r ) { this.r = r; orig = r.dup; }
112 
113         bool empty() @property { return r[0].empty; }
114 
115         void popFront()
116         {
117             foreach_reverse( ref x; r )
118             {
119                 x.popFront();
120                 if( !x.empty ) break;
121             }
122 
123             foreach( i, ref x; r[1..$] )
124             {
125                 if( x.empty )
126                     x = orig[i+1];
127             }
128         }
129 
130         auto front() @property { return r.map!(a=>a.front); }
131     }
132 
133     auto rr = new R[](k);
134     rr[] = r;
135 
136     return Result( rr );
137 }
138 
139 unittest
140 {
141     auto pp = partialPermutation( iota(2), 3 );
142     auto exp = [[0,0,0], [0,0,1], [0,1,0], [0,1,1],
143                 [1,0,0], [1,0,1], [1,1,0], [1,1,1]];
144     assert( equal!equal( pp, exp ) );
145 }
146 
147 auto uniqPartialPermutation(R)( R r, size_t k )
148 {
149     bool noDups(T)( T v ) pure
150     {
151         foreach( i; 0 .. v.length )
152             if( v[i+1..$].canFind( v[i] ) ) return false;
153         return true;
154     }
155     return partialPermutation(r,k).filter!(a=>noDups(a));
156 }
157 
158 unittest
159 {
160     auto upp = uniqPartialPermutation( iota(3), 2 );
161     auto exp = [[0,1],[0,2],[1,0],[1,2],[2,0],[2,1]];
162     assert( equal!equal( upp, exp ) );
163 }
164 
165 unittest
166 {
167     auto upp = uniqPartialPermutation( iota(4), 3 );
168 
169     auto exp = [ [0, 1, 2], [0, 1, 3], [0, 2, 1], [0, 2, 3],
170                  [0, 3, 1], [0, 3, 2], [1, 0, 2], [1, 0, 3],
171                  [1, 2, 0], [1, 2, 3], [1, 3, 0], [1, 3, 2],
172                  [2, 0, 1], [2, 0, 3], [2, 1, 0], [2, 1, 3],
173                  [2, 3, 0], [2, 3, 1], [3, 0, 1], [3, 0, 2],
174                  [3, 1, 0], [3, 1, 2], [3, 2, 0], [3, 2, 1] ];
175 
176     assert( equal!equal( upp, exp ) );
177 }