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 }