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 }