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