1 /**
2 This module contains various combinatorics algorithms.
3 
4 Authors: Sebastian Wilzbach, Ilya Yaroshenko
5 
6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
7 */
8 module mir.combinatorics;
9 
10 import mir.primitives: hasLength;
11 import mir.qualifier;
12 import std.traits;
13 
14 ///
15 version(mir_test) unittest
16 {
17     import mir.ndslice.fuse;
18 
19     assert(['a', 'b'].permutations.fuse == [['a', 'b'], ['b', 'a']]);
20     assert(['a', 'b'].cartesianPower(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']]);
21     assert(['a', 'b'].combinations(2).fuse == [['a', 'b']]);
22     assert(['a', 'b'].combinationsRepeat(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'b']]);
23 
24     assert(permutations!ushort(2).fuse == [[0, 1], [1, 0]]);
25     assert(cartesianPower!ushort(2, 2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
26     assert(combinations!ushort(2, 2).fuse == [[0, 1]]);
27     assert(combinationsRepeat!ushort(2, 2).fuse == [[0, 0], [0, 1], [1, 1]]);
28 
29     assert([3, 1].permutations!ubyte.fuse == [[3, 1], [1, 3]]);
30     assert([3, 1].cartesianPower!ubyte(2).fuse == [[3, 3], [3, 1], [1, 3], [1, 1]]);
31     assert([3, 1].combinations!ubyte(2).fuse == [[3, 1]]);
32     assert([3, 1].combinationsRepeat!ubyte(2).fuse == [[3, 3], [3, 1], [1, 1]]);
33 }
34 
35 /**
36 Checks whether we can do basic arithmetic operations, comparisons, modulo and
37 assign values to the type.
38 */
39 private template isArithmetic(R)
40 {
41     enum bool isArithmetic = is(typeof(
42     (inout int = 0)
43     {
44         R r = 1;
45         R test = (r * r / r + r - r) % r;
46         if (r < r && r > r) {}
47     }));
48 }
49 
50 /**
51 Checks whether we can do basic arithmetic operations, comparison and modulo
52 between two types. R needs to support item assignment of S (it includes S).
53 Both R and S need to be arithmetic types themselves.
54 */
55 private template isArithmetic(R, S)
56 {
57     enum bool isArithmetic = is(typeof(
58     (inout int = 0)
59     {
60         if (isArithmetic!R && isArithmetic!S) {}
61         S s = 1;
62         R r = 1;
63         R test = r * s + r * s;
64         R test2 = r / s + r / s;
65         R test3 = r - s + r - s;
66         R test4 = r % s + r % s;
67         if (r < s && s > r) {}
68         if (s < r && r > s) {}
69     }));
70 }
71 
72 /**
73 Computes the $(WEB en.wikipedia.org/wiki/Binomial_coefficient, binomial coefficient)
74 of n and k.
75 It is also known as "n choose k" or more formally as `_n!/_k!(_n-_k)`.
76 If a fixed-length integer type is used and an overflow happens, `0` is returned.
77 
78 Uses the generalized binomial coefficient for negative integers and floating
79 point number
80 
81 Params:
82     n = arbitrary arithmetic type
83     k = arbitrary arithmetic type
84 
85 Returns:
86     Binomial coefficient
87 */
88 R binomial(R = ulong, T)(T n, T k)
89     if (isArithmetic!(R, T) &&
90         ((is(typeof(T.min < 0)) && is(typeof(T.init & 1))) || !is(typeof(T.min < 0))) )
91 {
92     R result = 1;
93 
94     enum hasMinProperty = is(typeof(T.min < 0));
95     // only add negative support if possible
96     static if ((hasMinProperty && T.min < 0) || !hasMinProperty)
97     {
98         if (n < 0)
99         {
100             if (k >= 0)
101             {
102                 return (k & 1 ? -1 : 1) * binomial!(R, T)(-n + k-1, k);
103             }
104             else if (k <= n)
105             {
106                 return ((n-k) & 1 ? -1 : 1) * binomial!(R, T)(-k-1, n-k);
107             }
108         }
109         if (k < 0)
110         {
111             result = 0;
112             return result;
113         }
114     }
115 
116     if (k > n)
117     {
118         result = 0;
119         return result;
120     }
121     if (k > n - k)
122     {
123         k = n - k;
124     }
125     // make a copy of n (could be a custom type)
126     for (T i = 1, m = n; i <= k; i++, m--)
127     {
128         // check whether an overflow can happen
129         // hasMember!(Result, "max") doesn't work with dmd2.068 and ldc 0.17
130         static if (is(typeof(0 > R.max)))
131         {
132             if (result / i > R.max / m) return 0;
133             result = result / i * m + result % i * m / i;
134         }
135         else
136         {
137             result = result * m / i;
138         }
139     }
140     return result;
141 }
142 
143 ///
144 pure version(mir_test) unittest
145 {
146     assert(binomial(5, 2) == 10);
147     assert(binomial(6, 4) == 15);
148     assert(binomial(3, 1) == 3);
149 
150     import std.bigint: BigInt;
151     assert(binomial!BigInt(1000, 10) == BigInt("263409560461970212832400"));
152 }
153 
154 pure nothrow @safe @nogc version(mir_test) unittest
155 {
156     assert(binomial(5, 1) == 5);
157     assert(binomial(5, 0) == 1);
158     assert(binomial(1, 2) == 0);
159     assert(binomial(1, 0) == 1);
160     assert(binomial(1, 1) == 1);
161     assert(binomial(2, 1) == 2);
162     assert(binomial(2, 1) == 2);
163 
164     // negative
165     assert(binomial!long(-5, 3) == -35);
166     assert(binomial!long(5, -3) == 0);
167 }
168 
169 version(mir_test) unittest
170 {
171     import std.bigint;
172 
173     // test larger numbers
174     assert(binomial(100, 10) == 17_310_309_456_440);
175     assert(binomial(999, 5) == 82_09_039_793_949);
176     assert(binomial(300, 10) == 1_398_320_233_241_701_770LU);
177     assert(binomial(300LU, 10LU) == 1_398_320_233_241_701_770LU);
178 
179     // test overflow
180     assert(binomial(500, 10) == 0);
181 
182     // all parameters as custom types
183     BigInt n = 1010, k = 9;
184     assert(binomial!BigInt(n, k) == BigInt("2908077120956865974260"));
185 
186     // negative
187     assert(binomial!BigInt(-5, 3) == -35);
188     assert(binomial!BigInt(5, -3) == 0);
189     assert(binomial!BigInt(-5, -7) == 15);
190 }
191 
192 /**
193 Creates a projection of a generalized `Collection` range for the numeric case
194 case starting from `0` onto a custom `range` of any type.
195 
196 Params:
197     collection = range to be projected from
198     range = random access range to be projected to
199 
200 Returns:
201     Range with a projection to range for every element of collection
202 
203 See_Also:
204     $(LREF permutations), $(LREF cartesianPower), $(LREF combinations),
205     $(LREF combinationsRepeat)
206 */
207 IndexedRoR!(Collection, Range) indexedRoR(Collection, Range)(Collection collection, Range range)
208     if (__traits(compiles, Range.init[size_t.init]))
209 {
210     return IndexedRoR!(Collection, Range)(collection, range);
211 }
212 
213 /// ditto
214 struct IndexedRoR(Collection, Range)
215     if (__traits(compiles, Range.init[size_t.init]))
216 {
217     private Collection c;
218     private Range r;
219 
220     ///
221     alias DeepElement = ForeachType!Range;
222 
223     ///
224     this()(Collection collection, Range range)
225     {
226         this.c = collection;
227         this.r = range;
228     }
229 
230     ///
231     auto lightScope()()
232     {
233         return IndexedRoR!(LightScopeOf!Collection, LightScopeOf!Range)(.lightScope(c), .lightScope(r));
234     }
235 
236     ///
237     auto lightScope()() const
238     {
239         return IndexedRoR!(LightConstOf!(LightScopeOf!Collection), LightConstOf!(LightScopeOf!Range))(.lightScope(c), .lightScope(r));
240     }
241 
242     ///
243     auto lightConst()() const
244     {
245         return IndexedRoR!(LightConstOf!Collection, LightConstOf!Range)(.lightConst(c), .lightConst(r));
246     }
247 
248     /// Input range primitives
249     auto front()() @property
250     {
251         import mir.ndslice.slice: isSlice, sliced;
252         import mir.ndslice.topology: indexed;
253         import std.traits: ForeachType;
254         static if (isSlice!(ForeachType!Collection))
255             return r.indexed(c.front);
256         else
257             return r.indexed(c.front.sliced);
258     }
259 
260     /// ditto
261     void popFront() scope
262     {
263         c.popFront;
264     }
265 
266     /// ditto
267     bool empty()() @property scope const
268     {
269         return c.empty;
270     }
271 
272     static if (hasLength!Collection)
273     {
274         /// ditto
275         @property size_t length()() scope const
276         {
277             return c.length;
278         }
279 
280         /// 
281         @property size_t[2] shape()() scope const
282         {
283             return c.shape;
284         }
285     }
286 
287     static if (__traits(hasMember, Collection, "save"))
288     {
289         /// Forward range primitive. Calls `collection.save`.
290         typeof(this) save()() @property
291         {
292             return IndexedRoR!(Collection, Range)(c.save, r);
293         }
294     }
295 }
296 
297 ///
298 @safe pure nothrow version(mir_test) unittest
299 {
300     import mir.ndslice.fuse;
301 
302     auto perms = 2.permutations;
303     assert(perms.save.fuse == [[0, 1], [1, 0]]);
304 
305     auto projection = perms.indexedRoR([1, 2]);
306     assert(projection.fuse == [[1, 2], [2, 1]]);
307 }
308 
309 ///
310 version(mir_test) unittest
311 {
312     import mir.ndslice.fuse;
313     // import mir.ndslice.topology: only;
314 
315     auto projectionD = 2.permutations.indexedRoR("ab"d);
316     assert(projectionD.fuse == [['a', 'b'], ['b', 'a']]);
317 
318     // auto projectionC = 2.permutations.indexedRoR(only('a', 'b'));
319     // assert(projectionC.fuse == [['a', 'b'], ['b', 'a']]);
320 }
321 
322 @safe pure nothrow version(mir_test) unittest
323 {
324     import mir.ndslice.fuse;
325     import std.range: dropOne;
326 
327     auto perms = 2.permutations;
328     auto projection = perms.indexedRoR([1, 2]);
329     assert(projection.length == 2);
330 
331     // can save
332     assert(projection.save.dropOne.front == [2, 1]);
333     assert(projection.front == [1, 2]);
334 }
335 
336 @safe nothrow @nogc version(mir_test) unittest
337 {
338     import mir.algorithm.iteration: all;
339     import mir.ndslice.slice: sliced;
340     import mir.ndslice.fuse;
341     static perms = 2.permutations;
342     static immutable projectionArray = [1, 2];
343     auto projection = perms.indexedRoR(projectionArray);
344 
345     static immutable result = [1, 2,
346                                2, 1];
347     assert(result.sliced(2, 2).all!"a == b"(projection));
348 }
349 
350 /**
351 Lazily computes all _permutations of `r` using $(WEB
352 en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
353 
354 While generating a new item is in `O(k)` (amortized `O(1)`),
355 the number of permutations is `|n|!`.
356 
357 Params:
358     n = number of elements (`|r|`)
359     r = random access field. A field may not have iteration primitivies.
360     alloc = custom Allocator
361 
362 Returns:
363     Forward range, which yields the permutations
364 
365 See_Also:
366     $(LREF Permutations)
367 */
368 Permutations!T permutations(T = uint)(size_t n) @safe pure nothrow
369     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
370 {
371     assert(n, "must have at least one item");
372     return Permutations!T(new T[n-1], new T[n]);
373 }
374 
375 /// ditto
376 IndexedRoR!(Permutations!T, Range) permutations(T = uint, Range)(Range r) @safe pure nothrow
377     if (__traits(compiles, Range.init[size_t.init]))
378 {
379     return permutations!T(r.length).indexedRoR(r);
380 }
381 
382 /// ditto
383 Permutations!T makePermutations(T = uint, Allocator)(auto ref Allocator alloc, size_t n)
384     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
385 {
386     assert(n, "must have at least one item");
387     import std.experimental.allocator: makeArray;
388     auto state = alloc.makeArray!T(n - 1);
389     auto indices = alloc.makeArray!T(n);
390     return Permutations!T(state, indices);
391 }
392 
393 /**
394 Lazy Forward range of permutations using $(WEB
395 en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
396 
397 It always generates the permutations from 0 to `n - 1`,
398 use $(LREF indexedRoR) to map it to your range.
399 
400 Generating a new item is in `O(k)` (amortized `O(1)`),
401 the total number of elements is `n^k`.
402 
403 See_Also:
404     $(LREF permutations), $(LREF makePermutations)
405 */
406 struct Permutations(T)
407     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
408 {
409     import mir.ndslice.slice: sliced, Slice;
410 
411     private T[] indices, state;
412     private bool _empty;
413     private size_t _max_states = 1, _pos;
414 
415     ///
416     alias DeepElement = const T;
417 
418     /**
419     state should have the length of `n - 1`,
420     whereas the length of indices should be `n`
421     */
422     this()(T[] state, T[] indices) @safe pure nothrow @nogc
423     {
424         assert(state.length + 1 == indices.length);
425         // iota
426         foreach (i, ref index; indices)
427             index = cast(T)i;
428         state[] = 0;
429 
430         this.indices = indices;
431         this.state = state;
432 
433         _empty = indices.length == 0;
434 
435         // factorial
436         foreach (i; 1..indices.length + 1)
437             _max_states *= i;
438     }
439 
440     /// Input range primitives
441     @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
442     {
443         import mir.ndslice.slice: sliced;
444         return indices.sliced;
445     }
446 
447     /// ditto
448     void popFront()() scope @safe pure nothrow @nogc
449     {
450         import std.algorithm.mutation : swapAt;
451 
452         assert(!empty);
453         _pos++;
454 
455         for (T h = 0;;h++)
456         {
457             if (h + 2 > indices.length)
458             {
459                 _empty = true;
460                 break;
461             }
462 
463             indices.swapAt((h & 1) ? 0 : state[h], h + 1);
464 
465             if (state[h] == h + 1)
466             {
467                 state[h] = 0;
468                 continue;
469             }
470             state[h]++;
471             break;
472         }
473     }
474 
475     /// ditto
476     @property bool empty()() @safe pure nothrow @nogc scope const
477     {
478         return _empty;
479     }
480 
481     /// ditto
482     @property size_t length()() @safe pure nothrow @nogc scope const
483     {
484         return _max_states - _pos;
485     }
486 
487     /// 
488     @property size_t[2] shape()() scope const
489     {
490         return [length, indices.length];
491     }
492 
493     /// Forward range primitive. Allocates using GC.
494     @property Permutations save()() @safe pure nothrow
495     {
496         typeof(this) c = this;
497         c.indices = indices.dup;
498         c.state = state.dup;
499         return c;
500     }
501 }
502 
503 ///
504 pure @safe nothrow version(mir_test) unittest
505 {
506     import mir.ndslice.fuse;
507     import mir.ndslice.topology : iota;
508 
509     auto expectedRes = [[0, 1, 2],
510          [1, 0, 2],
511          [2, 0, 1],
512          [0, 2, 1],
513          [1, 2, 0],
514          [2, 1, 0]];
515 
516     auto r = iota(3);
517     auto rp = permutations(r.length).indexedRoR(r);
518     assert(rp.fuse == expectedRes);
519 
520     // direct style
521     auto rp2 = iota(3).permutations;
522     assert(rp2.fuse == expectedRes);
523 }
524 
525 ///
526 @nogc version(mir_test) unittest
527 {
528     import mir.algorithm.iteration: equal;
529     import mir.ndslice.slice: sliced;
530     import mir.ndslice.topology : iota;
531 
532     import std.experimental.allocator.mallocator;
533 
534     static immutable expected2 = [0, 1, 1, 0];
535     auto r = iota(2);
536     auto rp = makePermutations(Mallocator.instance, r.length);
537     assert(expected2.sliced(2, 2).equal(rp.indexedRoR(r)));
538     dispose(Mallocator.instance, rp);
539 }
540 
541 pure @safe nothrow version(mir_test) unittest
542 {
543     // is copyable?
544     import mir.ndslice.fuse;
545     import mir.ndslice.topology: iota;
546     import std.range: dropOne;
547     auto a = iota(2).permutations;
548     assert(a.front == [0, 1]);
549     assert(a.save.dropOne.front == [1, 0]);
550     assert(a.front == [0, 1]);
551 
552     // length
553     assert(1.permutations.length == 1);
554     assert(2.permutations.length == 2);
555     assert(3.permutations.length == 6);
556     assert(4.permutations.length == 24);
557     assert(10.permutations.length == 3_628_800);
558 }
559 
560 version (assert)
561 version(mir_test) unittest
562 {
563     // check invalid
564     import std.exception: assertThrown;
565     import core.exception: AssertError;
566     import std.experimental.allocator.mallocator: Mallocator;
567 
568     assertThrown!AssertError(0.permutations);
569     assertThrown!AssertError(Mallocator.instance.makePermutations(0));
570 }
571 
572 /**
573 Disposes a Permutations object. It destroys and then deallocates the
574 Permutations object pointed to by a pointer.
575 It is assumed the respective entities had been allocated with the same allocator.
576 
577 Params:
578     alloc = Custom allocator
579     perm = Permutations object
580 
581 See_Also:
582     $(LREF makePermutations)
583 */
584 void dispose(T, Allocator)(auto ref Allocator alloc, auto ref Permutations!T perm)
585 {
586     import std.experimental.allocator: dispose;
587     dispose(alloc, perm.state);
588     dispose(alloc, perm.indices);
589 }
590 
591 /**
592 Lazily computes the Cartesian power of `r` with itself
593 for a number of repetitions `D repeat`.
594 If the input is sorted, the product is in lexicographic order.
595 
596 While generating a new item is in `O(k)` (amortized `O(1)`),
597 the total number of elements is `n^k`.
598 
599 Params:
600     n = number of elements (`|r|`)
601     r = random access field. A field may not have iteration primitivies.
602     repeat = number of repetitions
603     alloc = custom Allocator
604 
605 Returns:
606     Forward range, which yields the product items
607 
608 See_Also:
609     $(LREF CartesianPower)
610 */
611 CartesianPower!T cartesianPower(T = uint)(size_t n, size_t repeat = 1) @safe pure nothrow
612     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
613 {
614     assert(repeat >= 1, "Invalid number of repetitions");
615     return CartesianPower!T(n, new T[repeat]);
616 }
617 
618 /// ditto
619 IndexedRoR!(CartesianPower!T, Range) cartesianPower(T = uint, Range)(Range r, size_t repeat = 1)
620 if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
621 {
622     assert(repeat >= 1, "Invalid number of repetitions");
623     return cartesianPower!T(r.length, repeat).indexedRoR(r);
624 }
625 
626 /// ditto
627 CartesianPower!T makeCartesianPower(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
628     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
629 {
630     assert(repeat >= 1, "Invalid number of repetitions");
631     import std.experimental.allocator: makeArray;
632     return CartesianPower!T(n, alloc.makeArray!T(repeat));
633 }
634 
635 /**
636 Lazy Forward range of Cartesian Power.
637 It always generates Cartesian Power from 0 to `n - 1`,
638 use $(LREF indexedRoR) to map it to your range.
639 
640 Generating a new item is in `O(k)` (amortized `O(1)`),
641 the total number of elements is `n^k`.
642 
643 See_Also:
644     $(LREF cartesianPower), $(LREF makeCartesianPower)
645 */
646 struct CartesianPower(T)
647     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
648 {
649     import mir.ndslice.slice: Slice;
650 
651     private T[] _state;
652     private size_t n;
653     private size_t _max_states, _pos;
654 
655     ///
656     alias DeepElement = const T;
657 
658     /// state should have the length of `repeat`
659     this()(size_t n, T[] state) @safe pure nothrow @nogc
660     {
661         assert(state.length >= 1, "Invalid number of repetitions");
662 
663         import std.math: pow;
664         this.n = n;
665         assert(n <= T.max);
666         this._state = state;
667 
668         _max_states = pow(n, state.length);
669     }
670 
671     /// Input range primitives
672     @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
673     {
674         import mir.ndslice.slice: sliced;
675         return _state.sliced;
676     }
677 
678     /// ditto
679     void popFront()() scope @safe pure nothrow @nogc
680     {
681         assert(!empty);
682         _pos++;
683 
684         /*
685         * Bitwise increment - starting from back
686         * It works like adding 1 in primary school arithmetic.
687         * If a block has reached the number of elements, we reset it to
688         * 0, and continue to increment, e.g. for n = 2:
689         *
690         * [0, 0, 0] -> [0, 0, 1]
691         * [0, 1, 1] -> [1, 0, 0]
692         */
693         foreach_reverse (i, ref el; _state)
694         {
695             ++el;
696             if (el < n)
697                 break;
698 
699             el = 0;
700         }
701     }
702 
703     /// ditto
704     @property size_t length()() @safe pure nothrow @nogc scope const
705     {
706         return _max_states - _pos;
707     }
708 
709     /// ditto
710     @property bool empty()() @safe pure nothrow @nogc scope const
711     {
712         return _pos == _max_states;
713     }
714 
715     /// 
716     @property size_t[2] shape()() scope const
717     {
718         return [length, _state.length];
719     }
720 
721     /// Forward range primitive. Allocates using GC.
722     @property CartesianPower save()() @safe pure nothrow
723     {
724         typeof(this) c = this;
725         c._state = _state.dup;
726         return c;
727     }
728 }
729 
730 ///
731 pure nothrow @safe version(mir_test) unittest
732 {
733     import mir.ndslice.fuse;
734     import mir.ndslice.topology: iota;
735     assert(iota(2).cartesianPower.fuse == [[0], [1]]);
736     assert(iota(2).cartesianPower(2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
737 
738     auto three_nums_two_bins = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]];
739     assert(iota(3).cartesianPower(2).fuse == three_nums_two_bins);
740 
741     assert("AB"d.cartesianPower(2).fuse == ["AA"d, "AB"d, "BA"d, "BB"d]);
742 }
743 
744 ///
745 @nogc version(mir_test) unittest
746 {
747     import mir.ndslice.topology: iota;
748     import mir.algorithm.iteration: equal;
749     import mir.ndslice.slice: sliced;
750 
751     import std.experimental.allocator.mallocator: Mallocator;
752     auto alloc = Mallocator.instance;
753 
754     static immutable expected2r2 = [
755         0, 0,
756         0, 1,
757         1, 0,
758         1, 1];
759     auto r = iota(2);
760     auto rc = alloc.makeCartesianPower(r.length, 2);
761     assert(expected2r2.sliced(4, 2).equal(rc.indexedRoR(r)));
762     alloc.dispose(rc);
763 }
764 
765 pure nothrow @safe version(mir_test) unittest
766 {
767     import mir.ndslice.fuse;
768     import mir.array.allocation: array;
769     import mir.ndslice.topology: iota;
770     import std.range: dropOne;
771 
772     assert(iota(0).cartesianPower.length == 0);
773     assert("AB"d.cartesianPower(3).fuse == ["AAA"d, "AAB"d, "ABA"d, "ABB"d, "BAA"d, "BAB"d, "BBA"d, "BBB"d]);
774     auto expected = ["AA"d, "AB"d, "AC"d, "AD"d,
775                      "BA"d, "BB"d, "BC"d, "BD"d,
776                      "CA"d, "CB"d, "CC"d, "CD"d,
777                      "DA"d, "DB"d, "DC"d, "DD"d];
778     assert("ABCD"d.cartesianPower(2).fuse == expected);
779     // verify with array too
780     assert("ABCD"d.cartesianPower(2).fuse == expected);
781 
782     assert(iota(2).cartesianPower.front == [0]);
783 
784     // is copyable?
785     auto a = iota(2).cartesianPower;
786     assert(a.front == [0]);
787     assert(a.save.dropOne.front == [1]);
788     assert(a.front == [0]);
789 
790     // test length shrinking
791     auto d = iota(2).cartesianPower;
792     assert(d.length == 2);
793     d.popFront;
794     assert(d.length == 1);
795 }
796 
797 version(assert)
798 version(mir_test) unittest
799 {
800     // check invalid
801     import std.exception: assertThrown;
802     import core.exception: AssertError;
803     import std.experimental.allocator.mallocator : Mallocator;
804 
805     assertThrown!AssertError(0.cartesianPower(0));
806     assertThrown!AssertError(Mallocator.instance.makeCartesianPower(0, 0));
807 }
808 
809 // length
810 pure nothrow @safe version(mir_test) unittest
811 {
812     assert(1.cartesianPower(1).length == 1);
813     assert(1.cartesianPower(2).length == 1);
814     assert(2.cartesianPower(1).length == 2);
815     assert(2.cartesianPower(2).length == 4);
816     assert(2.cartesianPower(3).length == 8);
817     assert(3.cartesianPower(1).length == 3);
818     assert(3.cartesianPower(2).length == 9);
819     assert(3.cartesianPower(3).length == 27);
820     assert(3.cartesianPower(4).length == 81);
821     assert(4.cartesianPower(10).length == 1_048_576);
822     assert(14.cartesianPower(7).length == 105_413_504);
823 }
824 
825 /**
826 Disposes a CartesianPower object. It destroys and then deallocates the
827 CartesianPower object pointed to by a pointer.
828 It is assumed the respective entities had been allocated with the same allocator.
829 
830 Params:
831     alloc = Custom allocator
832     cartesianPower = CartesianPower object
833 
834 See_Also:
835     $(LREF makeCartesianPower)
836 */
837 void dispose(T = uint, Allocator)(auto ref Allocator alloc, auto ref CartesianPower!T cartesianPower)
838 {
839     import std.experimental.allocator: dispose;
840     dispose(alloc, cartesianPower._state);
841 }
842 
843 /**
844 Lazily computes all k-combinations of `r`.
845 Imagine this as the $(LREF cartesianPower) filtered for only strictly ordered items.
846 
847 While generating a new combination is in `O(k)`,
848 the number of combinations is `binomial(n, k)`.
849 
850 Params:
851     n = number of elements (`|r|`)
852     r = random access field. A field may not have iteration primitivies.
853     k = number of combinations
854     alloc = custom Allocator
855 
856 Returns:
857     Forward range, which yields the k-combinations items
858 
859 See_Also:
860     $(LREF Combinations)
861 */
862 Combinations!T combinations(T = uint)(size_t n, size_t k = 1) @safe pure nothrow
863     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
864 {
865     assert(k >= 1, "Invalid number of combinations");
866     return Combinations!T(n, new T[k]);
867 }
868 
869 /// ditto
870 IndexedRoR!(Combinations!T, Range) combinations(T = uint, Range)(Range r, size_t k = 1)
871 if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
872 {
873     assert(k >= 1, "Invalid number of combinations");
874     return combinations!T(r.length, k).indexedRoR(r);
875 }
876 
877 /// ditto
878 Combinations!T makeCombinations(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
879     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
880 {
881     assert(repeat >= 1, "Invalid number of repetitions");
882     import std.experimental.allocator: makeArray;
883     return Combinations!T(cast(T) n, alloc.makeArray!T(cast(T) repeat));
884 }
885 
886 /**
887 Lazy Forward range of Combinations.
888 It always generates combinations from 0 to `n - 1`,
889 use $(LREF indexedRoR) to map it to your range.
890 
891 Generating a new combination is in `O(k)`,
892 the number of combinations is `binomial(n, k)`.
893 
894 See_Also:
895     $(LREF combinations), $(LREF makeCombinations)
896 */
897 struct Combinations(T)
898     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
899 {
900     import mir.ndslice.slice: Slice;
901 
902     private T[] state;
903     private size_t n;
904     private size_t max_states, pos;
905 
906     ///
907     alias DeepElement = const T;
908 
909     /// state should have the length of `repeat`
910     this()(size_t n, T[] state) @safe pure nothrow @nogc
911     {
912         import mir.ndslice.topology: iota;
913 
914         assert(state.length <= T.max);
915         this.n = n;
916         assert(n <= T.max);
917         this.max_states = cast(size_t) binomial(n, state.length);
918         this.state = state;
919 
920         // set initial state and calculate max possibilities
921         if (n > 0)
922         {
923             // skip first duplicate
924             if (n > 1 && state.length > 1)
925             {
926                 auto iotaResult = iota(state.length);
927                 foreach (i, ref el; state)
928                 {
929                     el = cast(T) iotaResult[i];
930                 }
931             }
932         }
933     }
934 
935     /// Input range primitives
936     @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
937     {
938         import mir.ndslice.slice: sliced;
939         return state.sliced;
940     }
941 
942     /// ditto
943     void popFront()() scope @safe pure nothrow @nogc
944     {
945         assert(!empty);
946         pos++;
947         // we might have bumped into the end state now
948         if (empty) return;
949 
950         // Behaves like: do _getNextState();  while (!_state.isStrictlySorted);
951         size_t i = state.length - 1;
952         /* Go from the back to next settable block
953         * - A must block must be lower than it's previous
954         * - A state i is not settable if it's maximum height is reached
955         *
956         * Think of it as a backwords search on state with
957         * iota(_repeat + d, _repeat + d) as search mask.
958         * (d = _nrElements -_repeat)
959         *
960         * As an example n = 3, r = 2, iota is [1, 2] and hence:
961         * [0, 1] -> i = 2
962         * [0, 2] -> i = 1
963         */
964         while (state[i] == n - state.length + i)
965         {
966             i--;
967         }
968         state[i] = cast(T)(state[i] + 1);
969 
970         /* Starting from our changed block, we need to take the change back
971         * to the end of the state array and update them by their new diff.
972         * [0, 1, 4] -> [0, 2, 3]
973         * [0, 3, 4] -> [1, 2, 3]
974         */
975         foreach (j; i + 1 .. state.length)
976         {
977             state[j] = cast(T)(state[i] + j - i);
978         }
979     }
980 
981     /// ditto
982     @property size_t length()() @safe pure nothrow @nogc scope const
983     {
984         return max_states - pos;
985     }
986 
987     /// ditto
988     @property bool empty()() @safe pure nothrow @nogc scope const
989     {
990         return pos == max_states;
991     }
992 
993     /// 
994     @property size_t[2] shape()() scope const
995     {
996         return [length, state.length];
997     }
998 
999     /// Forward range primitive. Allocates using GC.
1000     @property Combinations save()() @safe pure nothrow
1001     {
1002         typeof(this) c = this;
1003         c.state = state.dup;
1004         return c;
1005     }
1006 }
1007 
1008 ///
1009 pure nothrow @safe version(mir_test) unittest
1010 {
1011     import mir.ndslice.fuse;
1012     import mir.ndslice.topology: iota;
1013     assert(iota(3).combinations(2).fuse == [[0, 1], [0, 2], [1, 2]]);
1014     assert("AB"d.combinations(2).fuse == ["AB"d]);
1015     assert("ABC"d.combinations(2).fuse == ["AB"d, "AC"d, "BC"d]);
1016 }
1017 
1018 ///
1019 @nogc version(mir_test) unittest
1020 {
1021     import mir.algorithm.iteration: equal;
1022     import mir.ndslice.slice: sliced;
1023     import mir.ndslice.topology: iota;
1024 
1025     import std.experimental.allocator.mallocator;
1026     auto alloc = Mallocator.instance;
1027 
1028     static immutable expected3r2 = [
1029         0, 1,
1030         0, 2,
1031         1, 2];
1032     auto r = iota(3);
1033     auto rc = alloc.makeCombinations(r.length, 2);
1034     assert(expected3r2.sliced(3, 2).equal(rc.indexedRoR(r)));
1035     alloc.dispose(rc);
1036 }
1037 
1038 pure nothrow @safe version(mir_test) unittest
1039 {
1040     import mir.ndslice.fuse;
1041     import mir.array.allocation: array;
1042     import mir.ndslice.topology: iota;
1043     import std.range: dropOne;
1044 
1045     assert(iota(0).combinations.length == 0);
1046     assert(iota(2).combinations.fuse == [[0], [1]]);
1047 
1048     auto expected = ["AB"d, "AC"d, "AD"d, "BC"d, "BD"d, "CD"d];
1049     assert("ABCD"d.combinations(2).fuse == expected);
1050     // verify with array too
1051     assert("ABCD"d.combinations(2).fuse == expected);
1052     assert(iota(2).combinations.front == [0]);
1053 
1054     // is copyable?
1055     auto a = iota(2).combinations;
1056     assert(a.front == [0]);
1057     assert(a.save.dropOne.front == [1]);
1058     assert(a.front == [0]);
1059 
1060     // test length shrinking
1061     auto d = iota(2).combinations;
1062     assert(d.length == 2);
1063     d.popFront;
1064     assert(d.length == 1);
1065 
1066     // test larger combinations
1067     auto expected5 = [[0, 1, 2], [0, 1, 3], [0, 1, 4],
1068                       [0, 2, 3], [0, 2, 4], [0, 3, 4],
1069                       [1, 2, 3], [1, 2, 4], [1, 3, 4],
1070                       [2, 3, 4]];
1071     assert(iota(5).combinations(3).fuse == expected5);
1072     assert(iota(4).combinations(3).fuse == [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]);
1073     assert(iota(3).combinations(3).fuse == [[0, 1, 2]]);
1074     assert(iota(2).combinations(3).length == 0);
1075     assert(iota(1).combinations(3).length == 0);
1076 
1077     assert(iota(3).combinations(2).fuse == [[0, 1], [0, 2], [1, 2]]);
1078     assert(iota(2).combinations(2).fuse == [[0, 1]]);
1079     assert(iota(1).combinations(2).length == 0);
1080 
1081     assert(iota(1).combinations(1).fuse == [[0]]);
1082 }
1083 
1084 pure nothrow @safe version(mir_test) unittest
1085 {
1086     // test larger combinations
1087     import mir.ndslice.fuse;
1088     import mir.ndslice.topology: iota;
1089 
1090     auto expected6r4 = [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5],
1091                         [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 4, 5],
1092                         [0, 2, 3, 4], [0, 2, 3, 5], [0, 2, 4, 5],
1093                         [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5],
1094                         [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]];
1095     assert(iota(6).combinations(4).fuse == expected6r4);
1096 
1097     auto expected6r3 = [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 1, 5],
1098                         [0, 2, 3], [0, 2, 4], [0, 2, 5], [0, 3, 4],
1099                         [0, 3, 5], [0, 4, 5], [1, 2, 3], [1, 2, 4],
1100                         [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5],
1101                         [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]];
1102     assert(iota(6).combinations(3).fuse == expected6r3);
1103 
1104     auto expected6r2 = [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5],
1105                         [1, 2], [1, 3], [1, 4], [1, 5], [2, 3],
1106                         [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]];
1107     assert(iota(6).combinations(2).fuse == expected6r2);
1108 
1109     auto expected7r5 = [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6],
1110                         [0, 1, 2, 4, 5], [0, 1, 2, 4, 6], [0, 1, 2, 5, 6],
1111                         [0, 1, 3, 4, 5], [0, 1, 3, 4, 6], [0, 1, 3, 5, 6],
1112                         [0, 1, 4, 5, 6], [0, 2, 3, 4, 5], [0, 2, 3, 4, 6],
1113                         [0, 2, 3, 5, 6], [0, 2, 4, 5, 6], [0, 3, 4, 5, 6],
1114                         [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 5, 6],
1115                         [1, 2, 4, 5, 6], [1, 3, 4, 5, 6], [2, 3, 4, 5, 6]];
1116     assert(iota(7).combinations(5).fuse == expected7r5);
1117 }
1118 
1119 // length
1120 pure nothrow @safe version(mir_test) unittest
1121 {
1122     assert(1.combinations(1).length == 1);
1123     assert(1.combinations(2).length == 0);
1124     assert(2.combinations(1).length == 2);
1125     assert(2.combinations(2).length == 1);
1126     assert(2.combinations(3).length == 0);
1127     assert(3.combinations(1).length == 3);
1128     assert(3.combinations(2).length == 3);
1129     assert(3.combinations(3).length == 1);
1130     assert(3.combinations(4).length == 0);
1131     assert(4.combinations(10).length == 0);
1132     assert(14.combinations(11).length == 364);
1133     assert(20.combinations(7).length == 77_520);
1134     assert(30.combinations(10).length == 30_045_015);
1135     assert(30.combinations(15).length == 155_117_520);
1136 }
1137 
1138 version(assert)
1139 version(mir_test) unittest
1140 {
1141     // check invalid
1142     import std.exception: assertThrown;
1143     import core.exception: AssertError;
1144     import std.experimental.allocator.mallocator: Mallocator;
1145 
1146     assertThrown!AssertError(0.combinations(0));
1147     assertThrown!AssertError(Mallocator.instance.makeCombinations(0, 0));
1148 }
1149 
1150 /**
1151 Disposes a Combinations object. It destroys and then deallocates the
1152 Combinations object pointed to by a pointer.
1153 It is assumed the respective entities had been allocated with the same allocator.
1154 
1155 Params:
1156     alloc = Custom allocator
1157     perm = Combinations object
1158 
1159 See_Also:
1160     $(LREF makeCombinations)
1161 */
1162 void dispose(T, Allocator)(auto ref Allocator alloc, auto ref Combinations!T perm)
1163 {
1164     import std.experimental.allocator: dispose;
1165     dispose(alloc, perm.state);
1166 }
1167 
1168 /**
1169 Lazily computes all k-combinations of `r` with repetitions.
1170 A k-combination with repetitions, or k-multicombination,
1171 or multisubset of size k from a set S is given by a sequence of k
1172 not necessarily distinct elements of S, where order is not taken into account.
1173 Imagine this as the cartesianPower filtered for only ordered items.
1174 
1175 While generating a new combination with repeats is in `O(k)`,
1176 the number of combinations with repeats is `binomial(n + k - 1, k)`.
1177 
1178 Params:
1179     n = number of elements (`|r|`)
1180     r = random access field. A field may not have iteration primitivies.
1181     k = number of combinations
1182     alloc = custom Allocator
1183 
1184 Returns:
1185     Forward range, which yields the k-multicombinations items
1186 
1187 See_Also:
1188     $(LREF CombinationsRepeat)
1189 */
1190 CombinationsRepeat!T combinationsRepeat(T = uint)(size_t n, size_t k = 1) @safe pure nothrow
1191     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
1192 {
1193     assert(k >= 1, "Invalid number of combinations");
1194     return CombinationsRepeat!T(n, new T[k]);
1195 }
1196 
1197 /// ditto
1198 IndexedRoR!(CombinationsRepeat!T, Range) combinationsRepeat(T = uint, Range)(Range r, size_t k = 1)
1199     if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
1200 {
1201     assert(k >= 1, "Invalid number of combinations");
1202     return combinationsRepeat!T(r.length, k).indexedRoR(r);
1203 }
1204 
1205 /// ditto
1206 CombinationsRepeat!T makeCombinationsRepeat(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
1207     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
1208 {
1209     assert(repeat >= 1, "Invalid number of repetitions");
1210     import std.experimental.allocator: makeArray;
1211     return CombinationsRepeat!T(n, alloc.makeArray!T(repeat));
1212 }
1213 
1214 /**
1215 Lazy Forward range of combinations with repeats.
1216 It always generates combinations with repeats from 0 to `n - 1`,
1217 use $(LREF indexedRoR) to map it to your range.
1218 
1219 Generating a new combination with repeats is in `O(k)`,
1220 the number of combinations with repeats is `binomial(n, k)`.
1221 
1222 See_Also:
1223     $(LREF combinationsRepeat), $(LREF makeCombinationsRepeat)
1224 */
1225 struct CombinationsRepeat(T)
1226     if (isUnsigned!T && T.sizeof <= size_t.sizeof)
1227 {
1228     import mir.ndslice.slice: Slice;
1229 
1230     private T[] state;
1231     private size_t n;
1232     private size_t max_states, pos;
1233 
1234     ///
1235     alias DeepElement = const T;
1236 
1237     /// state should have the length of `repeat`
1238     this()(size_t n, T[] state) @safe pure nothrow @nogc
1239     {
1240         this.n = n;
1241         assert(n <= T.max);
1242         this.state = state;
1243         size_t repeatLen = state.length;
1244 
1245         // set initial state and calculate max possibilities
1246         if (n > 0)
1247         {
1248             max_states = cast(size_t) binomial(n + repeatLen - 1, repeatLen);
1249         }
1250     }
1251 
1252     /// Input range primitives
1253     @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
1254     {
1255         import mir.ndslice.slice: sliced;
1256         return state.sliced;
1257     }
1258 
1259     /// ditto
1260     void popFront()() scope @safe pure nothrow @nogc
1261     {
1262         assert(!empty);
1263         pos++;
1264 
1265         immutable repeat = state.length;
1266 
1267         // behaves like: do _getNextState();  while (!_state.isSorted);
1268         size_t i = repeat - 1;
1269         // go to next settable block
1270         // a block is settable if its not in the end state (=nrElements - 1)
1271         while (state[i] == n - 1 && i != 0)
1272         {
1273             i--;
1274         }
1275         state[i] = cast(T)(state[i] + 1);
1276 
1277         // if we aren't at the last block, we need to set all blocks
1278         // to equal the current one
1279         // e.g. [0, 2] -> (upper block: [1, 2]) -> [1, 1]
1280         if (i != repeat - 1)
1281         {
1282             for (size_t j = i + 1; j < repeat; j++)
1283                 state[j] = state[i];
1284         }
1285     }
1286 
1287     /// ditto
1288     @property size_t length()() @safe pure nothrow @nogc scope const
1289     {
1290         return max_states - pos;
1291     }
1292 
1293     /// ditto
1294     @property bool empty()() @safe pure nothrow @nogc scope const
1295     {
1296         return pos == max_states;
1297     }
1298 
1299     /// 
1300     @property size_t[2] shape()() scope const
1301     {
1302         return [length, state.length];
1303     }
1304 
1305     /// Forward range primitive. Allocates using GC.
1306     @property CombinationsRepeat save()() @safe pure nothrow
1307     {
1308         typeof(this) c = this;
1309         c.state = state.dup;
1310         return c;
1311     }
1312 }
1313 
1314 ///
1315 pure nothrow @safe version(mir_test) unittest
1316 {
1317     import mir.ndslice.fuse;
1318     import mir.ndslice.topology: iota;
1319 
1320     assert(iota(2).combinationsRepeat.fuse == [[0], [1]]);
1321     assert(iota(2).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [1, 1]]);
1322     assert(iota(3).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]);
1323     assert("AB"d.combinationsRepeat(2).fuse == ["AA"d, "AB"d,  "BB"d]);
1324 }
1325 
1326 ///
1327 @nogc version(mir_test) unittest
1328 {
1329     import mir.algorithm.iteration: equal;
1330     import mir.ndslice.slice: sliced;
1331     import mir.ndslice.topology: iota;
1332 
1333     import std.experimental.allocator.mallocator;
1334     auto alloc = Mallocator.instance;
1335 
1336     static immutable expected3r1 = [
1337         0, 
1338         1, 
1339         2];
1340     auto r = iota(3);
1341     auto rc = alloc.makeCombinationsRepeat(r.length, 1);
1342     assert(expected3r1.sliced(3, 1).equal(rc.indexedRoR(r)));
1343     alloc.dispose(rc);
1344 }
1345 
1346 version(mir_test) unittest
1347 {
1348     import mir.ndslice.fuse;
1349     import mir.array.allocation: array;
1350     import mir.ndslice.topology: iota;
1351     import std.range: dropOne;
1352 
1353     assert(iota(0).combinationsRepeat.length == 0);
1354     assert("AB"d.combinationsRepeat(3).fuse == ["AAA"d, "AAB"d, "ABB"d,"BBB"d]);
1355 
1356     auto expected = ["AA"d, "AB"d, "AC"d, "AD"d, "BB"d, "BC"d, "BD"d, "CC"d, "CD"d, "DD"d];
1357     assert("ABCD"d.combinationsRepeat(2).fuse == expected);
1358     // verify with array too
1359     assert("ABCD"d.combinationsRepeat(2).fuse == expected);
1360 
1361     assert(iota(2).combinationsRepeat.front == [0]);
1362 
1363     // is copyable?
1364     auto a = iota(2).combinationsRepeat;
1365     assert(a.front == [0]);
1366     assert(a.save.dropOne.front == [1]);
1367     assert(a.front == [0]);
1368 
1369     // test length shrinking
1370     auto d = iota(2).combinationsRepeat;
1371     assert(d.length == 2);
1372     d.popFront;
1373     assert(d.length == 1);
1374 }
1375 
1376 // length
1377 pure nothrow @safe version(mir_test) unittest
1378 {
1379     assert(1.combinationsRepeat(1).length == 1);
1380     assert(1.combinationsRepeat(2).length == 1);
1381     assert(2.combinationsRepeat(1).length == 2);
1382     assert(2.combinationsRepeat(2).length == 3);
1383     assert(2.combinationsRepeat(3).length == 4);
1384     assert(3.combinationsRepeat(1).length == 3);
1385     assert(3.combinationsRepeat(2).length == 6);
1386     assert(3.combinationsRepeat(3).length == 10);
1387     assert(3.combinationsRepeat(4).length == 15);
1388     assert(4.combinationsRepeat(10).length == 286);
1389     assert(11.combinationsRepeat(14).length == 1_961_256);
1390     assert(20.combinationsRepeat(7).length == 657_800);
1391     assert(20.combinationsRepeat(10).length == 20_030_010);
1392     assert(30.combinationsRepeat(10).length == 635_745_396);
1393 }
1394 
1395 pure nothrow @safe version(mir_test) unittest
1396 {
1397     // test larger combinations
1398     import mir.ndslice.fuse;
1399     import mir.ndslice.topology: iota;
1400 
1401     auto expected3r1 = [[0], [1], [2]];
1402     assert(iota(3).combinationsRepeat(1).fuse == expected3r1);
1403 
1404     auto expected3r2 = [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]];
1405     assert(iota(3).combinationsRepeat(2).fuse == expected3r2);
1406 
1407     auto expected3r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1],
1408                         [0, 1, 2], [0, 2, 2], [1, 1, 1], [1, 1, 2],
1409                         [1, 2, 2], [2, 2, 2]];
1410     assert(iota(3).combinationsRepeat(3).fuse == expected3r3);
1411 
1412     auto expected3r4 = [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2],
1413                         [0, 0, 1, 1], [0, 0, 1, 2], [0, 0, 2, 2],
1414                         [0, 1, 1, 1], [0, 1, 1, 2], [0, 1, 2, 2],
1415                         [0, 2, 2, 2], [1, 1, 1, 1], [1, 1, 1, 2],
1416                         [1, 1, 2, 2], [1, 2, 2, 2], [2, 2, 2, 2]];
1417     assert(iota(3).combinationsRepeat(4).fuse == expected3r4);
1418 
1419     auto expected4r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2],
1420                         [0, 0, 3], [0, 1, 1], [0, 1, 2],
1421                         [0, 1, 3], [0, 2, 2], [0, 2, 3],
1422                         [0, 3, 3], [1, 1, 1], [1, 1, 2],
1423                         [1, 1, 3], [1, 2, 2], [1, 2, 3],
1424                         [1, 3, 3], [2, 2, 2], [2, 2, 3],
1425                         [2, 3, 3], [3, 3, 3]];
1426     assert(iota(4).combinationsRepeat(3).fuse == expected4r3);
1427 
1428     auto expected4r2 = [[0, 0], [0, 1], [0, 2], [0, 3],
1429                          [1, 1], [1, 2], [1, 3], [2, 2],
1430                          [2, 3], [3, 3]];
1431     assert(iota(4).combinationsRepeat(2).fuse == expected4r2);
1432 
1433     auto expected5r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4],
1434                         [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 2],
1435                         [0, 2, 3], [0, 2, 4], [0, 3, 3], [0, 3, 4], [0, 4, 4],
1436                         [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 2, 2],
1437                         [1, 2, 3], [1, 2, 4], [1, 3, 3], [1, 3, 4], [1, 4, 4],
1438                         [2, 2, 2], [2, 2, 3], [2, 2, 4], [2, 3, 3], [2, 3, 4],
1439                         [2, 4, 4], [3, 3, 3], [3, 3, 4], [3, 4, 4], [4, 4, 4]];
1440     assert(iota(5).combinationsRepeat(3).fuse == expected5r3);
1441 
1442     auto expected5r2 = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
1443                         [1, 1], [1, 2], [1, 3], [1, 4], [2, 2],
1444                         [2, 3], [2, 4], [3, 3], [3, 4], [4, 4]];
1445     assert(iota(5).combinationsRepeat(2).fuse == expected5r2);
1446 }
1447 
1448 version(assert)
1449 version(mir_test) unittest
1450 {
1451     // check invalid
1452     import std.exception: assertThrown;
1453     import core.exception: AssertError;
1454     import std.experimental.allocator.mallocator: Mallocator;
1455 
1456     assertThrown!AssertError(0.combinationsRepeat(0));
1457     assertThrown!AssertError(Mallocator.instance.makeCombinationsRepeat(0, 0));
1458 }
1459 
1460 /**
1461 Disposes a CombinationsRepeat object. It destroys and then deallocates the
1462 CombinationsRepeat object pointed to by a pointer.
1463 It is assumed the respective entities had been allocated with the same allocator.
1464 
1465 Params:
1466     alloc = Custom allocator
1467     perm = CombinationsRepeat object
1468 
1469 See_Also:
1470     $(LREF makeCombinationsRepeat)
1471 */
1472 void dispose(T, Allocator)(auto ref Allocator alloc, auto ref CombinationsRepeat!T perm)
1473 {
1474     import std.experimental.allocator: dispose;
1475     dispose(alloc, perm.state);
1476 }